001
014
015 package com.liferay.portal.model.impl;
016
017 import com.liferay.portal.LayoutFriendlyURLException;
018 import com.liferay.portal.NoSuchGroupException;
019 import com.liferay.portal.kernel.exception.PortalException;
020 import com.liferay.portal.kernel.exception.SystemException;
021 import com.liferay.portal.kernel.log.Log;
022 import com.liferay.portal.kernel.log.LogFactoryUtil;
023 import com.liferay.portal.kernel.util.CharPool;
024 import com.liferay.portal.kernel.util.CookieKeys;
025 import com.liferay.portal.kernel.util.HttpUtil;
026 import com.liferay.portal.kernel.util.ListUtil;
027 import com.liferay.portal.kernel.util.LocaleUtil;
028 import com.liferay.portal.kernel.util.StringPool;
029 import com.liferay.portal.kernel.util.StringUtil;
030 import com.liferay.portal.kernel.util.UnicodeProperties;
031 import com.liferay.portal.kernel.util.Validator;
032 import com.liferay.portal.model.ColorScheme;
033 import com.liferay.portal.model.Group;
034 import com.liferay.portal.model.Layout;
035 import com.liferay.portal.model.LayoutConstants;
036 import com.liferay.portal.model.LayoutSet;
037 import com.liferay.portal.model.LayoutType;
038 import com.liferay.portal.model.LayoutTypePortlet;
039 import com.liferay.portal.model.LayoutTypePortletConstants;
040 import com.liferay.portal.model.Theme;
041 import com.liferay.portal.security.permission.ActionKeys;
042 import com.liferay.portal.security.permission.PermissionChecker;
043 import com.liferay.portal.service.GroupLocalServiceUtil;
044 import com.liferay.portal.service.LayoutLocalServiceUtil;
045 import com.liferay.portal.service.LayoutSetLocalServiceUtil;
046 import com.liferay.portal.service.ThemeLocalServiceUtil;
047 import com.liferay.portal.service.permission.LayoutPermissionUtil;
048 import com.liferay.portal.theme.ThemeDisplay;
049 import com.liferay.portal.util.LayoutClone;
050 import com.liferay.portal.util.LayoutCloneFactory;
051 import com.liferay.portal.util.PortalUtil;
052 import com.liferay.portal.util.PropsValues;
053 import com.liferay.portal.util.WebKeys;
054 import com.liferay.portlet.PortletURLImpl;
055
056 import java.io.IOException;
057
058 import java.util.ArrayList;
059 import java.util.Iterator;
060 import java.util.List;
061 import java.util.Locale;
062
063 import javax.portlet.PortletException;
064 import javax.portlet.PortletMode;
065 import javax.portlet.PortletRequest;
066 import javax.portlet.WindowState;
067
068 import javax.servlet.http.HttpServletRequest;
069
070
073 public class LayoutImpl extends LayoutBaseImpl {
074
075 public static boolean hasFriendlyURLKeyword(String friendlyURL) {
076 String keyword = _getFriendlyURLKeyword(friendlyURL);
077
078 if (Validator.isNotNull(keyword)) {
079 return true;
080 }
081
082 return false;
083 }
084
085 public static int validateFriendlyURL(String friendlyURL) {
086 return validateFriendlyURL(friendlyURL, true);
087 }
088
089 public static int validateFriendlyURL(
090 String friendlyURL, boolean checkMaxLength) {
091
092 if (friendlyURL.length() < 2) {
093 return LayoutFriendlyURLException.TOO_SHORT;
094 }
095
096 if (checkMaxLength &&
097 (friendlyURL.length() > LayoutConstants.FRIENDLY_URL_MAX_LENGTH)) {
098
099 return LayoutFriendlyURLException.TOO_LONG;
100 }
101
102 if (!friendlyURL.startsWith(StringPool.SLASH)) {
103 return LayoutFriendlyURLException.DOES_NOT_START_WITH_SLASH;
104 }
105
106 if (friendlyURL.endsWith(StringPool.SLASH)) {
107 return LayoutFriendlyURLException.ENDS_WITH_SLASH;
108 }
109
110 if (friendlyURL.contains(StringPool.DOUBLE_SLASH)) {
111 return LayoutFriendlyURLException.ADJACENT_SLASHES;
112 }
113
114 for (char c : friendlyURL.toCharArray()) {
115 if (!Validator.isChar(c) && !Validator.isDigit(c) &&
116 (c != CharPool.DASH) && (c != CharPool.PERCENT) &&
117 (c != CharPool.PERIOD) && (c != CharPool.PLUS) &&
118 (c != CharPool.SLASH) && (c != CharPool.STAR) &&
119 (c != CharPool.UNDERLINE)) {
120
121 return LayoutFriendlyURLException.INVALID_CHARACTERS;
122 }
123 }
124
125 return -1;
126 }
127
128 public static void validateFriendlyURLKeyword(String friendlyURL)
129 throws LayoutFriendlyURLException {
130
131 String keyword = _getFriendlyURLKeyword(friendlyURL);
132
133 if (Validator.isNotNull(keyword)) {
134 LayoutFriendlyURLException lfurle = new LayoutFriendlyURLException(
135 LayoutFriendlyURLException.KEYWORD_CONFLICT);
136
137 lfurle.setKeywordConflict(keyword);
138
139 throw lfurle;
140 }
141 }
142
143 public LayoutImpl() {
144 }
145
146 public List<Layout> getAllChildren() throws SystemException {
147 List<Layout> layouts = new ArrayList<Layout>();
148
149 for (Layout layout : getChildren()) {
150 layouts.add(layout);
151 layouts.addAll(layout.getAllChildren());
152 }
153
154 return layouts;
155 }
156
157 public long getAncestorLayoutId() throws PortalException, SystemException {
158 long layoutId = 0;
159
160 Layout layout = this;
161
162 while (true) {
163 if (!layout.isRootLayout()) {
164 layout = LayoutLocalServiceUtil.getLayout(
165 layout.getGroupId(), layout.isPrivateLayout(),
166 layout.getParentLayoutId());
167 }
168 else {
169 layoutId = layout.getLayoutId();
170
171 break;
172 }
173 }
174
175 return layoutId;
176 }
177
178 public long getAncestorPlid() throws PortalException, SystemException {
179 long plid = 0;
180
181 Layout layout = this;
182
183 while (true) {
184 if (!layout.isRootLayout()) {
185 layout = LayoutLocalServiceUtil.getLayout(
186 layout.getGroupId(), layout.isPrivateLayout(),
187 layout.getParentLayoutId());
188 }
189 else {
190 plid = layout.getPlid();
191
192 break;
193 }
194 }
195
196 return plid;
197 }
198
199 public List<Layout> getAncestors() throws PortalException, SystemException {
200 List<Layout> layouts = new ArrayList<Layout>();
201
202 Layout layout = this;
203
204 while (!layout.isRootLayout()) {
205 layout = LayoutLocalServiceUtil.getLayout(
206 layout.getGroupId(), layout.isPrivateLayout(),
207 layout.getParentLayoutId());
208
209 layouts.add(layout);
210 }
211
212 return layouts;
213 }
214
215 public List<Layout> getChildren() throws SystemException {
216 return LayoutLocalServiceUtil.getLayouts(
217 getGroupId(), isPrivateLayout(), getLayoutId());
218 }
219
220 public List<Layout> getChildren(PermissionChecker permissionChecker)
221 throws PortalException, SystemException {
222
223 List<Layout> layouts = ListUtil.copy(getChildren());
224
225 Iterator<Layout> itr = layouts.iterator();
226
227 while (itr.hasNext()) {
228 Layout layout = itr.next();
229
230 if (layout.isHidden() ||
231 !LayoutPermissionUtil.contains(
232 permissionChecker, layout, ActionKeys.VIEW)) {
233
234 itr.remove();
235 }
236 }
237
238 return layouts;
239 }
240
241 public ColorScheme getColorScheme()
242 throws PortalException, SystemException {
243
244 if (isInheritLookAndFeel()) {
245 return getLayoutSet().getColorScheme();
246 }
247 else {
248 return ThemeLocalServiceUtil.getColorScheme(
249 getCompanyId(), getTheme().getThemeId(), getColorSchemeId(),
250 false);
251 }
252 }
253
254 public String getCssText() throws PortalException, SystemException {
255 if (isInheritLookAndFeel()) {
256 return getLayoutSet().getCss();
257 }
258 else {
259 return getCss();
260 }
261 }
262
263 public Group getGroup() throws PortalException, SystemException {
264 return GroupLocalServiceUtil.getGroup(getGroupId());
265 }
266
267 public String getHTMLTitle(Locale locale) {
268 String localeLanguageId = LocaleUtil.toLanguageId(locale);
269
270 return getHTMLTitle(localeLanguageId);
271 }
272
273 public String getHTMLTitle(String localeLanguageId) {
274 String htmlTitle = getTitle(localeLanguageId);
275
276 if (Validator.isNull(htmlTitle)) {
277 htmlTitle = getName(localeLanguageId);
278 }
279
280 return htmlTitle;
281 }
282
283 public LayoutSet getLayoutSet() throws PortalException, SystemException {
284 if (_layoutSet == null) {
285 _layoutSet = LayoutSetLocalServiceUtil.getLayoutSet(
286 getGroupId(), isPrivateLayout());
287 }
288
289 return _layoutSet;
290 }
291
292 public LayoutType getLayoutType() {
293 if (_layoutType == null) {
294 _layoutType = new LayoutTypePortletImpl(this);
295 }
296
297 return _layoutType;
298 }
299
300 public long getParentPlid() throws PortalException, SystemException {
301 if (getParentLayoutId() == LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
302 return 0;
303 }
304
305 Layout layout = LayoutLocalServiceUtil.getLayout(
306 getGroupId(), isPrivateLayout(), getParentLayoutId());
307
308 return layout.getPlid();
309 }
310
311 public String getRegularURL(HttpServletRequest request)
312 throws PortalException, SystemException {
313
314 return _getURL(request, false, false);
315 }
316
317 public String getResetLayoutURL(HttpServletRequest request)
318 throws PortalException, SystemException {
319
320 return _getURL(request, true, true);
321 }
322
323 public String getResetMaxStateURL(HttpServletRequest request)
324 throws PortalException, SystemException {
325
326 return _getURL(request, true, false);
327 }
328
329 public Group getScopeGroup() throws PortalException, SystemException {
330 Group group = null;
331
332 try {
333 group = GroupLocalServiceUtil.getLayoutGroup(
334 getCompanyId(), getPlid());
335 }
336 catch (NoSuchGroupException nsge) {
337 }
338
339 return group;
340 }
341
342 public String getTarget() {
343 return PortalUtil.getLayoutTarget(this);
344 }
345
346 public Theme getTheme() throws PortalException, SystemException {
347 if (isInheritLookAndFeel()) {
348 return getLayoutSet().getTheme();
349 }
350 else {
351 return ThemeLocalServiceUtil.getTheme(
352 getCompanyId(), getThemeId(), false);
353 }
354 }
355
356 public String getThemeSetting(String key, String device) {
357 UnicodeProperties typeSettingsProperties = getTypeSettingsProperties();
358
359 String value = typeSettingsProperties.getProperty(
360 ThemeSettingImpl.namespaceProperty(device, key));
361
362 if (value != null) {
363 return value;
364 }
365
366 if (!isInheritLookAndFeel()) {
367 try {
368 Theme theme = _getTheme(device);
369
370 return theme.getSetting(key);
371 }
372 catch (Exception e) {
373 }
374 }
375
376 try {
377 LayoutSet layoutSet = getLayoutSet();
378
379 value = layoutSet.getThemeSetting(key, device);
380 }
381 catch (Exception e) {
382 }
383
384 return value;
385 }
386
387 @Override
388 public String getTypeSettings() {
389 if (_typeSettingsProperties == null) {
390 return super.getTypeSettings();
391 }
392 else {
393 return _typeSettingsProperties.toString();
394 }
395 }
396
397 public UnicodeProperties getTypeSettingsProperties() {
398 if (_typeSettingsProperties == null) {
399 _typeSettingsProperties = new UnicodeProperties(true);
400
401 _typeSettingsProperties.fastLoad(super.getTypeSettings());
402 }
403
404 return _typeSettingsProperties;
405 }
406
407 public String getTypeSettingsProperty(String key) {
408 UnicodeProperties typeSettingsProperties = getTypeSettingsProperties();
409
410 return typeSettingsProperties.getProperty(key);
411 }
412
413 public String getTypeSettingsProperty(String key, String defaultValue) {
414 UnicodeProperties typeSettingsProperties = getTypeSettingsProperties();
415
416 return typeSettingsProperties.getProperty(key, defaultValue);
417 }
418
419 public ColorScheme getWapColorScheme()
420 throws PortalException, SystemException {
421
422 if (isInheritLookAndFeel()) {
423 return getLayoutSet().getWapColorScheme();
424 }
425 else {
426 return ThemeLocalServiceUtil.getColorScheme(
427 getCompanyId(), getWapTheme().getThemeId(),
428 getWapColorSchemeId(), true);
429 }
430 }
431
432 public Theme getWapTheme() throws PortalException, SystemException {
433 if (isInheritWapLookAndFeel()) {
434 return getLayoutSet().getWapTheme();
435 }
436 else {
437 return ThemeLocalServiceUtil.getTheme(
438 getCompanyId(), getWapThemeId(), true);
439 }
440 }
441
442 public boolean hasAncestor(long layoutId)
443 throws PortalException, SystemException {
444
445 long parentLayoutId = getParentLayoutId();
446
447 while (parentLayoutId != LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
448 if (parentLayoutId == layoutId) {
449 return true;
450 }
451 else {
452 Layout parentLayout = LayoutLocalServiceUtil.getLayout(
453 getGroupId(), isPrivateLayout(), parentLayoutId);
454
455 parentLayoutId = parentLayout.getParentLayoutId();
456 }
457 }
458
459 return false;
460 }
461
462 public boolean hasChildren() throws SystemException {
463 return LayoutLocalServiceUtil.hasLayouts(
464 getGroupId(), isPrivateLayout(), getLayoutId());
465 }
466
467 public boolean hasScopeGroup() throws PortalException, SystemException {
468 Group group = getScopeGroup();
469
470 if (group != null) {
471 return true;
472 }
473 else {
474 return false;
475 }
476 }
477
478 public boolean isChildSelected(boolean selectable, Layout layout)
479 throws PortalException, SystemException {
480
481 if (selectable) {
482 long plid = getPlid();
483
484 List<Layout> ancestors = layout.getAncestors();
485
486 for (Layout curLayout : ancestors) {
487 if (plid == curLayout.getPlid()) {
488 return true;
489 }
490 }
491 }
492
493 return false;
494 }
495
496 public boolean isContentDisplayPage() {
497 UnicodeProperties typeSettingsProperties = getTypeSettingsProperties();
498
499 String defaultAssetPublisherPortletId =
500 typeSettingsProperties.getProperty(
501 LayoutTypePortletConstants.DEFAULT_ASSET_PUBLISHER_PORTLET_ID);
502
503 if (Validator.isNotNull(defaultAssetPublisherPortletId)) {
504 return true;
505 }
506
507 return false;
508 }
509
510 public boolean isFirstChild() {
511 if (getPriority() == 0) {
512 return true;
513 }
514
515 return false;
516 }
517
518 public boolean isFirstParent() {
519 if (isFirstChild() && isRootLayout()) {
520 return true;
521 }
522
523 return false;
524 }
525
526 public boolean isInheritLookAndFeel() {
527 if (Validator.isNull(getThemeId()) ||
528 Validator.isNull(getColorSchemeId())) {
529
530 return true;
531 }
532
533 return false;
534 }
535
536 public boolean isInheritWapLookAndFeel() {
537 if (Validator.isNull(getWapThemeId()) ||
538 Validator.isNull(getWapColorSchemeId())) {
539
540 return true;
541 }
542
543 return false;
544 }
545
546 public boolean isLayoutPrototypeLinkActive() {
547 if (isLayoutPrototypeLinkEnabled() &&
548 Validator.isNotNull(getLayoutPrototypeUuid())) {
549
550 return true;
551 }
552
553 return false;
554 }
555
556 public boolean isPublicLayout() {
557 return !isPrivateLayout();
558 }
559
560 public boolean isRootLayout() {
561 if (getParentLayoutId() == LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
562 return true;
563 }
564
565 return false;
566 }
567
568 public boolean isSelected(
569 boolean selectable, Layout layout, long ancestorPlid) {
570
571 if (selectable) {
572 long plid = getPlid();
573
574 if ((plid == layout.getPlid()) || (plid == ancestorPlid)) {
575 return true;
576 }
577 }
578
579 return false;
580 }
581
582 public boolean isSupportsEmbeddedPortlets() {
583 if (isTypeArticle() || isTypeEmbedded() || isTypePanel() ||
584 isTypePortlet()) {
585
586 return true;
587 }
588
589 return false;
590 }
591
592 public boolean isTypeArticle() {
593 if (getType().equals(LayoutConstants.TYPE_ARTICLE)) {
594 return true;
595 }
596
597 return false;
598 }
599
600 public boolean isTypeControlPanel() {
601 if (getType().equals(LayoutConstants.TYPE_CONTROL_PANEL)) {
602 return true;
603 }
604
605 return false;
606 }
607
608 public boolean isTypeEmbedded() {
609 if (getType().equals(LayoutConstants.TYPE_EMBEDDED)) {
610 return true;
611 }
612
613 return false;
614 }
615
616 public boolean isTypeLinkToLayout() {
617 if (getType().equals(LayoutConstants.TYPE_LINK_TO_LAYOUT)) {
618 return true;
619 }
620
621 return false;
622 }
623
624 public boolean isTypePanel() {
625 if (getType().equals(LayoutConstants.TYPE_PANEL)) {
626 return true;
627 }
628
629 return false;
630 }
631
632 public boolean isTypePortlet() {
633 if (getType().equals(LayoutConstants.TYPE_PORTLET)) {
634 return true;
635 }
636
637 return false;
638 }
639
640 public boolean isTypeURL() {
641 if (getType().equals(LayoutConstants.TYPE_URL)) {
642 return true;
643 }
644
645 return false;
646 }
647
648 @Override
649 public void setGroupId(long groupId) {
650 super.setGroupId(groupId);
651
652 _layoutSet = null;
653 }
654
655 public void setLayoutSet(LayoutSet layoutSet) {
656 _layoutSet = layoutSet;
657 }
658
659 @Override
660 public void setPrivateLayout(boolean privateLayout) {
661 super.setPrivateLayout(privateLayout);
662
663 _layoutSet = null;
664 }
665
666 @Override
667 public void setTypeSettings(String typeSettings) {
668 _typeSettingsProperties = null;
669
670 super.setTypeSettings(typeSettings);
671 }
672
673 public void setTypeSettingsProperties(
674 UnicodeProperties typeSettingsProperties) {
675
676 _typeSettingsProperties = typeSettingsProperties;
677
678 super.setTypeSettings(_typeSettingsProperties.toString());
679 }
680
681 private static String _getFriendlyURLKeyword(String friendlyURL) {
682 friendlyURL = friendlyURL.toLowerCase();
683
684 for (String keyword : _friendlyURLKeywords) {
685 if (friendlyURL.startsWith(keyword)) {
686 return keyword;
687 }
688
689 if (keyword.equals(friendlyURL + StringPool.SLASH)) {
690 return friendlyURL;
691 }
692 }
693
694 return null;
695 }
696
697 private static void _initFriendlyURLKeywords() {
698 _friendlyURLKeywords =
699 new String[PropsValues.LAYOUT_FRIENDLY_URL_KEYWORDS.length];
700
701 for (int i = 0; i < PropsValues.LAYOUT_FRIENDLY_URL_KEYWORDS.length;
702 i++) {
703
704 String keyword = PropsValues.LAYOUT_FRIENDLY_URL_KEYWORDS[i];
705
706 keyword = StringPool.SLASH + keyword;
707
708 if (!keyword.contains(StringPool.PERIOD)) {
709 if (keyword.endsWith(StringPool.STAR)) {
710 keyword = keyword.substring(0, keyword.length() - 1);
711 }
712 else {
713 keyword = keyword + StringPool.SLASH;
714 }
715 }
716
717 _friendlyURLKeywords[i] = keyword.toLowerCase();
718 }
719 }
720
721 private LayoutTypePortlet _getLayoutTypePortletClone(
722 HttpServletRequest request)
723 throws IOException {
724
725 LayoutTypePortlet layoutTypePortlet = null;
726
727 LayoutClone layoutClone = LayoutCloneFactory.getInstance();
728
729 if (layoutClone != null) {
730 String typeSettings = layoutClone.get(request, getPlid());
731
732 if (typeSettings != null) {
733 UnicodeProperties typeSettingsProperties =
734 new UnicodeProperties(true);
735
736 typeSettingsProperties.load(typeSettings);
737
738 String stateMax = typeSettingsProperties.getProperty(
739 LayoutTypePortletConstants.STATE_MAX);
740 String stateMin = typeSettingsProperties.getProperty(
741 LayoutTypePortletConstants.STATE_MIN);
742
743 Layout layout = (Layout)this.clone();
744
745 layoutTypePortlet = (LayoutTypePortlet)layout.getLayoutType();
746
747 layoutTypePortlet.setStateMax(stateMax);
748 layoutTypePortlet.setStateMin(stateMin);
749 }
750 }
751
752 if (layoutTypePortlet == null) {
753 layoutTypePortlet = (LayoutTypePortlet)getLayoutType();
754 }
755
756 return layoutTypePortlet;
757 }
758
759 private Theme _getTheme(String device)
760 throws PortalException, SystemException {
761
762 if (device.equals("regular")) {
763 return getTheme();
764 }
765 else {
766 return getWapTheme();
767 }
768 }
769
770 private String _getURL(
771 HttpServletRequest request, boolean resetMaxState,
772 boolean resetRenderParameters)
773 throws PortalException, SystemException {
774
775 ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
776 WebKeys.THEME_DISPLAY);
777
778 if (resetMaxState) {
779 Layout layout = themeDisplay.getLayout();
780
781 LayoutTypePortlet layoutTypePortlet = null;
782
783 if (layout.equals(this)) {
784 layoutTypePortlet = themeDisplay.getLayoutTypePortlet();
785 }
786 else {
787 try {
788 layoutTypePortlet = _getLayoutTypePortletClone(request);
789 }
790 catch (IOException ioe) {
791 _log.error("Unable to clone layout settings", ioe);
792
793 layoutTypePortlet = (LayoutTypePortlet)getLayoutType();
794 }
795 }
796
797 if (layoutTypePortlet.hasStateMax()) {
798 String portletId = StringUtil.split(
799 layoutTypePortlet.getStateMax())[0];
800
801 PortletURLImpl portletURLImpl = new PortletURLImpl(
802 request, portletId, getPlid(), PortletRequest.ACTION_PHASE);
803
804 try {
805 portletURLImpl.setWindowState(WindowState.NORMAL);
806 portletURLImpl.setPortletMode(PortletMode.VIEW);
807 }
808 catch (PortletException pe) {
809 throw new SystemException(pe);
810 }
811
812 portletURLImpl.setAnchor(false);
813
814 if (PropsValues.LAYOUT_DEFAULT_P_L_RESET &&
815 !resetRenderParameters) {
816
817 portletURLImpl.setParameter("p_l_reset", "0");
818 }
819 else if (!PropsValues.LAYOUT_DEFAULT_P_L_RESET &&
820 resetRenderParameters) {
821
822 portletURLImpl.setParameter("p_l_reset", "1");
823 }
824
825 return portletURLImpl.toString();
826 }
827 }
828
829 String portalURL = PortalUtil.getPortalURL(request);
830
831 String url = PortalUtil.getLayoutURL(this, themeDisplay);
832
833 if (!CookieKeys.hasSessionId(request) &&
834 (url.startsWith(portalURL) || url.startsWith(StringPool.SLASH))) {
835
836 url = PortalUtil.getURLWithSessionId(
837 url, request.getSession().getId());
838 }
839
840 if (!resetMaxState) {
841 return url;
842 }
843
844 if (PropsValues.LAYOUT_DEFAULT_P_L_RESET && !resetRenderParameters) {
845 url = HttpUtil.addParameter(url, "p_l_reset", 0);
846 }
847 else if (!PropsValues.LAYOUT_DEFAULT_P_L_RESET &&
848 resetRenderParameters) {
849
850 url = HttpUtil.addParameter(url, "p_l_reset", 1);
851 }
852
853 return url;
854 }
855
856 private static Log _log = LogFactoryUtil.getLog(LayoutImpl.class);
857
858 private static String[] _friendlyURLKeywords;
859
860 private LayoutSet _layoutSet;
861 private LayoutType _layoutType;
862 private UnicodeProperties _typeSettingsProperties;
863
864 static {
865 _initFriendlyURLKeywords();
866 }
867
868 }