001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (C) Alkacon Software (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.ade.contenteditor; 029 030import org.opencms.acacia.shared.CmsAttributeConfiguration; 031import org.opencms.acacia.shared.CmsTabInfo; 032import org.opencms.acacia.shared.CmsType; 033import org.opencms.ade.contenteditor.CmsWidgetUtil.WidgetInfo; 034import org.opencms.ade.contenteditor.shared.CmsComplexWidgetData; 035import org.opencms.ade.contenteditor.shared.CmsExternalWidgetConfiguration; 036import org.opencms.file.CmsFile; 037import org.opencms.file.CmsObject; 038import org.opencms.file.CmsRequestContext; 039import org.opencms.i18n.CmsMessages; 040import org.opencms.i18n.CmsMultiMessages; 041import org.opencms.jsp.util.CmsKeyDummyMacroResolver; 042import org.opencms.main.CmsLog; 043import org.opencms.main.OpenCms; 044import org.opencms.util.CmsMacroResolver; 045import org.opencms.util.CmsStringUtil; 046import org.opencms.util.I_CmsMacroResolver; 047import org.opencms.widgets.A_CmsWidget; 048import org.opencms.widgets.I_CmsADEWidget; 049import org.opencms.widgets.I_CmsComplexWidget; 050import org.opencms.widgets.I_CmsWidget; 051import org.opencms.xml.CmsXmlContentDefinition; 052import org.opencms.xml.CmsXmlException; 053import org.opencms.xml.content.CmsDefaultXmlContentHandler; 054import org.opencms.xml.content.CmsXmlContentTab; 055import org.opencms.xml.content.I_CmsXmlContentHandler; 056import org.opencms.xml.content.I_CmsXmlContentHandler.DisplayType; 057import org.opencms.xml.types.A_CmsXmlContentValue; 058import org.opencms.xml.types.CmsXmlAccessRestrictionValue; 059import org.opencms.xml.types.CmsXmlDynamicCategoryValue; 060import org.opencms.xml.types.CmsXmlNestedContentDefinition; 061import org.opencms.xml.types.I_CmsXmlSchemaType; 062 063import java.util.ArrayList; 064import java.util.Collection; 065import java.util.Collections; 066import java.util.HashMap; 067import java.util.List; 068import java.util.Locale; 069import java.util.Map; 070 071import org.apache.commons.logging.Log; 072 073/** 074 * Visitor to read all types and attribute configurations within a content definition.<p> 075 */ 076public class CmsContentTypeVisitor { 077 078 /** 079 * Helper class to evaluate the widget display type.<p> 080 */ 081 protected static class DisplayTypeEvaluator { 082 083 /** The attribute name. */ 084 private String m_attributeName; 085 086 /** The attribute type configuration. */ 087 private CmsAttributeConfiguration m_config; 088 089 /** The configured display type. */ 090 private DisplayType m_configuredType; 091 092 /** The default display type. */ 093 private DisplayType m_default; 094 095 /** The applied rule. */ 096 private EvaluationRule m_rule; 097 098 /** 099 * Constructor.<p> 100 * 101 * @param config the attribute type configuration 102 * @param configuredType the configured display type 103 * @param defaultType the default display type 104 * @param rule the applied rule 105 */ 106 protected DisplayTypeEvaluator( 107 CmsAttributeConfiguration config, 108 DisplayType configuredType, 109 DisplayType defaultType, 110 EvaluationRule rule) { 111 112 m_config = config; 113 114 m_configuredType = configuredType; 115 m_default = defaultType; 116 m_rule = rule; 117 } 118 119 /** 120 * Returns the attribute name.<p> 121 * 122 * @return the attribute name 123 */ 124 protected String getAttributeName() { 125 126 return m_attributeName; 127 } 128 129 /** 130 * Returns the attribute configuration with the evaluated display type.<p> 131 * 132 * @param predecessor the proposed predecessor display type 133 * @param successor the proposed successor display type 134 * 135 * @return the attribute configuration 136 */ 137 protected CmsAttributeConfiguration getEvaluatedConfiguration(DisplayType predecessor, DisplayType successor) { 138 139 DisplayType resultingType = m_configuredType; 140 141 if (resultingType.equals(DisplayType.none)) { 142 if (m_rule.equals(EvaluationRule.rootLevel)) { 143 resultingType = DisplayType.wide; 144 } else { 145 resultingType = getProposedType(); 146 if ((predecessor != null) && predecessor.equals(DisplayType.none)) { 147 predecessor = null; 148 } 149 if ((successor != null) && successor.equals(DisplayType.none)) { 150 successor = null; 151 } 152 if ((predecessor != null) && predecessor.equals(DisplayType.column)) { 153 predecessor = DisplayType.singleline; 154 } 155 if ((successor != null) && successor.equals(DisplayType.column)) { 156 successor = DisplayType.singleline; 157 } 158 boolean strong = m_rule.equals(EvaluationRule.none) 159 || (m_rule.equals(EvaluationRule.optional) && m_default.equals(DisplayType.singleline)) 160 || (m_rule.equals(EvaluationRule.labelLength) && m_default.equals(DisplayType.wide)); 161 if (((predecessor == null) || (successor == null)) && strong) { 162 resultingType = m_default; 163 } else if ((predecessor != null) || (successor != null)) { 164 165 // check if the proposed type matches neither the type of the predecessor nor the type of the successor 166 if (!(((predecessor != null) && resultingType.equals(predecessor)) 167 || ((successor != null) && resultingType.equals(successor)))) { 168 DisplayType match = (predecessor != null) 169 && (predecessor.equals(DisplayType.wide) || predecessor.equals(DisplayType.singleline)) 170 ? predecessor 171 : ((successor != null) 172 && (successor.equals(DisplayType.wide) || successor.equals(DisplayType.singleline)) 173 ? successor 174 : null); 175 resultingType = match != null ? match : resultingType; 176 } 177 } 178 } 179 } 180 m_config.setDisplayType(resultingType.name()); 181 return m_config; 182 } 183 184 /** 185 * Returns the proposed display type.<p> 186 * 187 * @return the proposed display type 188 */ 189 protected DisplayType getProposedType() { 190 191 DisplayType resultingType = m_configuredType; 192 if (resultingType.equals(DisplayType.none)) { 193 switch (m_rule) { 194 case rootLevel: 195 case labelLength: 196 resultingType = DisplayType.wide; 197 break; 198 case optional: 199 resultingType = DisplayType.singleline; 200 break; 201 default: 202 resultingType = m_default; 203 204 } 205 } 206 return resultingType; 207 } 208 209 /** 210 * Sets the attribute name.<p> 211 * 212 * @param attributeName the attribute name 213 */ 214 protected void setAttributeName(String attributeName) { 215 216 m_attributeName = attributeName; 217 } 218 } 219 220 /** Widget display type evaluation rules. */ 221 protected enum EvaluationRule { 222 /** Label length rule. */ 223 labelLength, 224 /** No rule applied. */ 225 none, 226 /** Optional field rule. */ 227 optional, 228 /** Root level rule. */ 229 rootLevel 230 } 231 232 /** Logger instance for this class. */ 233 private static final Log LOG = CmsLog.getLog(CmsContentTypeVisitor.class); 234 235 /** The localization macro start sequence. */ 236 private static final String MESSAGE_MACRO_START = "" 237 + I_CmsMacroResolver.MACRO_DELIMITER 238 + I_CmsMacroResolver.MACRO_START 239 + CmsMacroResolver.KEY_LOCALIZED_PREFIX; 240 241 /** The old style localization macro start sequence. */ 242 private static final String MESSAGE_MACRO_START_OLD = "" 243 + I_CmsMacroResolver.MACRO_DELIMITER_OLD 244 + I_CmsMacroResolver.MACRO_START_OLD 245 + CmsMacroResolver.KEY_LOCALIZED_PREFIX; 246 247 /** The attribute configurations. */ 248 private Map<String, CmsAttributeConfiguration> m_attributeConfigurations; 249 250 /** The CMS context used for this visitor. */ 251 private CmsObject m_cms; 252 253 /** Map from attribute names to complex widget configurations. */ 254 private Map<String, CmsComplexWidgetData> m_complexWidgets = new HashMap<String, CmsComplexWidgetData>(); 255 256 /** The content handler. */ 257 private I_CmsXmlContentHandler m_contentHandler; 258 259 /** The dynamically loaded attribute names. */ 260 private List<String> m_dynamicallyLoaded; 261 262 /** The optional dynamic categoy fields. */ 263 private CmsDynamicCategoryFieldList m_dynamicCategoryFields = new CmsDynamicCategoryFieldList(); 264 265 /** The content resource. */ 266 private CmsFile m_file; 267 268 /** Indicates the visited content has fields that are configured to be invisible to the current user. */ 269 private boolean m_hasInvisible; 270 271 /** The content locale. */ 272 private Locale m_locale; 273 274 /** The locale synchronized attribute names. */ 275 private List<String> m_localeSynchronizations; 276 277 /** The messages. */ 278 private CmsMultiMessages m_messages; 279 280 /** The registered types. */ 281 private Map<String, CmsType> m_registeredTypes; 282 283 /** The root content definition. */ 284 private CmsXmlContentDefinition m_rootContentDefinition; 285 286 /** The tab informations. */ 287 private List<CmsTabInfo> m_tabInfos; 288 289 /** The widget configurations. */ 290 private Map<String, CmsExternalWidgetConfiguration> m_widgetConfigurations; 291 292 /** The widgets encountered by this visitor. */ 293 private List<I_CmsWidget> m_widgets = new ArrayList<I_CmsWidget>(); 294 295 /** 296 * Constructor.<p> 297 * 298 * @param cms the CMS context 299 * @param file the content file 300 * @param locale the content locale 301 */ 302 public CmsContentTypeVisitor(CmsObject cms, CmsFile file, Locale locale) { 303 304 m_file = file; 305 m_cms = cms; 306 m_locale = locale; 307 } 308 309 /** 310 * Returns the tab informations for the given content definition.<p> 311 * @param cms the CMS context 312 * @param definition the content definition 313 * @param messages the localization messages 314 * 315 * @return the tab informations 316 */ 317 public static List<CmsTabInfo> collectTabInfos( 318 CmsObject cms, 319 CmsXmlContentDefinition definition, 320 CmsMessages messages) { 321 322 List<CmsTabInfo> result = new ArrayList<CmsTabInfo>(); 323 CmsMacroResolver resolver = new CmsMacroResolver(); 324 resolver.setCmsObject(cms); 325 resolver.setMessages(messages); 326 CmsKeyDummyMacroResolver keyResolver = new CmsKeyDummyMacroResolver(resolver); 327 328 if (definition.getContentHandler().getTabs() != null) { 329 for (CmsXmlContentTab xmlTab : definition.getContentHandler().getTabs()) { 330 String tabKey = null; 331 String tabName; 332 333 // in case the tab name attribute contains a localization macro 334 if (xmlTab.getTabName().contains(MESSAGE_MACRO_START) 335 || xmlTab.getTabName().contains(MESSAGE_MACRO_START_OLD)) { 336 tabName = resolver.resolveMacros(xmlTab.getTabName()); 337 tabKey = CmsKeyDummyMacroResolver.getKey(keyResolver.resolveMacros(xmlTab.getTabName())); 338 } else { 339 tabName = messages.keyDefault( 340 A_CmsWidget.LABEL_PREFIX + definition.getInnerName() + "." + xmlTab.getTabName(), 341 xmlTab.getTabName()); 342 tabKey = A_CmsWidget.LABEL_PREFIX + definition.getInnerName() + "." + xmlTab.getTabName(); 343 } 344 345 String descriptionKey = null; 346 if (xmlTab.getDescription() != null) { 347 descriptionKey = CmsKeyDummyMacroResolver.getKey( 348 keyResolver.resolveMacros(xmlTab.getDescription())); 349 } 350 351 result.add( 352 new CmsTabInfo( 353 tabName, 354 tabKey, 355 xmlTab.getIdName(), 356 xmlTab.getStartName(), 357 xmlTab.isCollapsed(), 358 resolver.resolveMacros(xmlTab.getDescription()), 359 descriptionKey)); 360 } 361 } 362 return result; 363 } 364 365 /** 366 * Gets the CMS context.<p> 367 * 368 * @return the CMS context 369 */ 370 public CmsObject getCmsObject() { 371 372 return m_cms; 373 } 374 375 /** 376 * Gets the list of widgets which have been processed by this visitor.<p> 377 * 378 * @return the list of widget 379 */ 380 public List<I_CmsWidget> getCollectedWidgets() { 381 382 return Collections.unmodifiableList(m_widgets); 383 } 384 385 /** 386 * Gets the map of complex widget configurations.<p> 387 * 388 * @return a map from attribute names to complex widget configurations 389 */ 390 public Map<String, CmsComplexWidgetData> getComplexWidgetData() { 391 392 return m_complexWidgets; 393 } 394 395 /** 396 * Returns the label for this value.<p> 397 * 398 * @param value the value 399 * @param defaultValue the default value 400 * 401 * @return the label 402 */ 403 public String getLabel(I_CmsXmlSchemaType value, String defaultValue) { 404 405 I_CmsXmlContentHandler handler = value.getContentDefinition().getContentHandler(); 406 if (handler instanceof CmsDefaultXmlContentHandler) { 407 CmsDefaultXmlContentHandler defaultHandler = (CmsDefaultXmlContentHandler)handler; 408 String label = defaultHandler.getFieldLabels().get(value.getName()); 409 if (label != null) { 410 CmsMacroResolver resolver = new CmsMacroResolver(); 411 resolver.setCmsObject(m_cms); 412 resolver.setKeepEmptyMacros(true); 413 resolver.setMessages(m_messages); 414 return resolver.resolveMacros(label); 415 } 416 } 417 StringBuffer result = new StringBuffer(64); 418 result.append(A_CmsWidget.LABEL_PREFIX); 419 result.append(getTypeKey(value)); 420 return m_messages.keyDefault(result.toString(), defaultValue); 421 } 422 423 /** 424 * Gets the optional dynamic category fields collected so far. 425 * 426 * @return the optional dynamic category fields 427 */ 428 public CmsDynamicCategoryFieldList getOptionalDynamicCategoryFields() { 429 430 return m_dynamicCategoryFields; 431 } 432 433 /** 434 * Returns the tabInfos.<p> 435 * 436 * @return the tabInfos 437 */ 438 public List<CmsTabInfo> getTabInfos() { 439 440 return m_tabInfos; 441 } 442 443 /** 444 * Returns if the visited content has invisible fields.<p> 445 * 446 * @return <code>true</code> if the visited content has invisible fields 447 */ 448 public boolean hasInvisibleFields() { 449 450 return m_hasInvisible; 451 } 452 453 /** 454 * Returns <code>true</code> if the value of the attribute is dynamically loaded. 455 * @param attributeName the attribute to check 456 * @return <code>true</code> if the value of the attribute is dynamically loaded. 457 */ 458 public boolean isDynamicallyLoaded(String attributeName) { 459 460 return m_dynamicallyLoaded.contains(attributeName); 461 } 462 463 /** 464 * Checks if the content type widgets are compatible with the new content editor.<p> 465 * 466 * @param xmlContentDefinition the content definition 467 * 468 * @return <code>true</code> if the content type widgets are compatible with the new content editor 469 * 470 * @throws CmsXmlException if something goes wrong reading the type widget 471 */ 472 public boolean isEditorCompatible(CmsXmlContentDefinition xmlContentDefinition) throws CmsXmlException { 473 474 boolean result = true; 475 for (I_CmsXmlSchemaType subType : xmlContentDefinition.getTypeSequence()) { 476 if (subType.isSimpleType()) { 477 result = isEditorCompatible((A_CmsXmlContentValue)subType); 478 } else { 479 CmsXmlContentDefinition subTypeDefinition = ((CmsXmlNestedContentDefinition)subType).getNestedContentDefinition(); 480 result = isEditorCompatible(subTypeDefinition); 481 } 482 if (!result) { 483 break; 484 } 485 } 486 return result; 487 } 488 489 /** 490 * Visits all types within the XML content definition.<p> 491 * 492 * @param xmlContentDefinition the content definition 493 * @param messageLocale the locale 494 */ 495 /** 496 * Visits all types within the XML content definition.<p> 497 * 498 * @param xmlContentDefinition the content definition 499 * @param messageLocale the locale 500 */ 501 public void visitTypes(CmsXmlContentDefinition xmlContentDefinition, Locale messageLocale) { 502 503 m_rootContentDefinition = xmlContentDefinition; 504 m_contentHandler = xmlContentDefinition.getContentHandler(); 505 CmsMessages messages = null; 506 m_messages = new CmsMultiMessages(messageLocale); 507 m_messages.setFallbackHandler(xmlContentDefinition.getContentHandler().getMessageKeyHandler()); 508 509 try { 510 messages = OpenCms.getWorkplaceManager().getMessages(messageLocale); 511 if (messages != null) { 512 m_messages.addMessages(messages); 513 } 514 messages = m_contentHandler.getMessages(messageLocale); 515 if (messages != null) { 516 m_messages.addMessages(messages); 517 } 518 } catch (Exception e) { 519 // may happen during start up 520 LOG.debug(e.getMessage(), e); 521 } 522 // generate a new multi messages object and add the messages from the workplace 523 524 m_attributeConfigurations = new HashMap<String, CmsAttributeConfiguration>(); 525 m_widgetConfigurations = new HashMap<String, CmsExternalWidgetConfiguration>(); 526 m_registeredTypes = new HashMap<String, CmsType>(); 527 m_localeSynchronizations = new ArrayList<String>(); 528 m_dynamicallyLoaded = new ArrayList<String>(); 529 m_tabInfos = collectTabInfos(m_cms, xmlContentDefinition, m_messages); 530 readTypes(xmlContentDefinition, ""); 531 } 532 533 /** 534 * Returns the attribute configurations.<p> 535 * 536 * @return the attribute configurations 537 */ 538 protected Map<String, CmsAttributeConfiguration> getAttributeConfigurations() { 539 540 return m_attributeConfigurations; 541 } 542 543 /** 544 * Returns the locale synchronized attribute names.<p> 545 * 546 * @return the locale synchronized attribute names 547 */ 548 protected List<String> getLocaleSynchronizations() { 549 550 return m_localeSynchronizations; 551 } 552 553 /** 554 * Returns the types of the visited content definition.<p> 555 * 556 * @return the types 557 */ 558 protected Map<String, CmsType> getTypes() { 559 560 return m_registeredTypes; 561 } 562 563 /** 564 * Returns the external widget configurations.<p> 565 * 566 * @return the external widget configurations 567 */ 568 protected Collection<CmsExternalWidgetConfiguration> getWidgetConfigurations() { 569 570 return m_widgetConfigurations.values(); 571 } 572 573 /** 574 * Returns the help information for this value.<p> 575 * 576 * @param value the value 577 * 578 * @return the help information 579 */ 580 private String getHelp(I_CmsXmlSchemaType value) { 581 582 StringBuffer result = new StringBuffer(64); 583 I_CmsXmlContentHandler handler = value.getContentDefinition().getContentHandler(); 584 if (handler instanceof CmsDefaultXmlContentHandler) { 585 CmsDefaultXmlContentHandler defaultHandler = (CmsDefaultXmlContentHandler)handler; 586 String help = defaultHandler.getFieldHelp().get(value.getName()); 587 if (help != null) { 588 CmsMacroResolver resolver = new CmsMacroResolver(); 589 resolver.setCmsObject(m_cms); 590 resolver.setKeepEmptyMacros(true); 591 resolver.setMessages(m_messages); 592 return resolver.resolveMacros(help); 593 } 594 } 595 result.append(A_CmsWidget.LABEL_PREFIX); 596 result.append(getTypeKey(value)); 597 result.append(A_CmsWidget.HELP_POSTFIX); 598 return m_messages.keyDefault(result.toString(), null); 599 } 600 601 /** 602 * Returns the schema type message key.<p> 603 * 604 * @param value the schema type 605 * 606 * @return the schema type message key 607 */ 608 private String getTypeKey(I_CmsXmlSchemaType value) { 609 610 StringBuffer result = new StringBuffer(64); 611 result.append(value.getContentDefinition().getInnerName()); 612 result.append('.'); 613 result.append(value.getName()); 614 return result.toString(); 615 } 616 617 /** 618 * Checks if the content value widget is compatible with the new content editor.<p> 619 * 620 * @param schemaType the content value type 621 * 622 * @return <code>true</code> if the content value widget is compatible with the new content editor 623 * 624 * @throws CmsXmlException if something goes wrong reading the type widget 625 */ 626 private boolean isEditorCompatible(A_CmsXmlContentValue schemaType) throws CmsXmlException { 627 628 boolean result = false; 629 I_CmsXmlContentHandler contentHandler = schemaType.getContentDefinition().getContentHandler(); 630 // We don't care about the old editor for the 'inheritable' widget configuration, 631 // so we're using the old getWidget method here 632 I_CmsWidget widget = contentHandler.getWidget(schemaType); 633 result = (widget == null) || (widget instanceof I_CmsADEWidget); 634 return result; 635 } 636 637 /** 638 * Returns if an element with the given path will be displayed at root level of a content editor tab.<p> 639 * 640 * @param path the element path 641 * 642 * @return <code>true</code> if an element with the given path will be displayed at root level of a content editor tab 643 */ 644 private boolean isTabRootLevel(String path) { 645 646 path = path.substring(1); 647 if (!path.contains("/")) { 648 return true; 649 } 650 if (m_tabInfos != null) { 651 for (CmsTabInfo info : m_tabInfos) { 652 if (info.isCollapsed() 653 && path.startsWith(info.getStartName()) 654 && !path.substring(info.getStartName().length() + 1).contains("/")) { 655 return true; 656 } 657 } 658 } 659 return false; 660 } 661 662 /** 663 * Reads the attribute configuration for the given schema type. May return <code>null</code> if no special configuration was set.<p> 664 * 665 * @param schemaType the schema type 666 * @param path the attribute path 667 * 668 * @return the attribute configuration 669 */ 670 private DisplayTypeEvaluator readConfiguration(A_CmsXmlContentValue schemaType, String path) { 671 672 String widgetName = null; 673 String widgetConfig = null; 674 CmsObject cms = getCmsObject(); 675 String label = getLabel(schemaType, schemaType.getName()); 676 // set the default display type 677 DisplayType configuredType = DisplayType.none; 678 DisplayType defaultType = DisplayType.none; 679 EvaluationRule rule = EvaluationRule.none; 680 try { 681 if ((cms.getRequestContext().getAttribute(CmsRequestContext.ATTRIBUTE_ADE_CONTEXT_PATH) == null) 682 && (m_file != null)) { 683 cms.getRequestContext().setAttribute( 684 CmsRequestContext.ATTRIBUTE_ADE_CONTEXT_PATH, 685 m_file.getRootPath()); 686 } 687 WidgetInfo widgetInfo = CmsWidgetUtil.collectWidgetInfo(cms, m_rootContentDefinition, path, m_messages); 688 I_CmsWidget widget = widgetInfo.getWidget(); 689 I_CmsComplexWidget complexWidget = widgetInfo.getComplexWidget(); 690 configuredType = widgetInfo.getDisplayType(); 691 if (configuredType.equals(DisplayType.none) && schemaType.isSimpleType()) { 692 // check the type is on the root level of the document, those will be displayed 'wide' 693 // the path will always have a leading '/' 694 // also in case the label has more than 15 characters, we display 'wide' 695 if (isTabRootLevel(path)) { 696 rule = EvaluationRule.rootLevel; 697 } else if (label.length() > 15) { 698 rule = EvaluationRule.labelLength; 699 } else if ((schemaType.getMinOccurs() == 0)) { 700 rule = EvaluationRule.optional; 701 } 702 } 703 if (widget != null) { 704 widgetName = widget.getClass().getName(); 705 if ((configuredType == DisplayType.column) 706 && !(schemaType.isSimpleType() 707 && (schemaType.getMaxOccurs() == 1) 708 && widget.isCompactViewEnabled())) { 709 // column view is not allowed for this widget 710 configuredType = DisplayType.singleline; 711 } 712 long timer = 0; 713 if (widget instanceof I_CmsADEWidget) { 714 if (CmsContentService.LOG.isDebugEnabled()) { 715 timer = System.currentTimeMillis(); 716 } 717 I_CmsADEWidget adeWidget = (I_CmsADEWidget)widget; 718 defaultType = adeWidget.getDefaultDisplayType(); 719 widgetName = adeWidget.getWidgetName(); 720 721 widgetConfig = adeWidget.getConfiguration(cms, schemaType, m_messages, m_file, m_locale); 722 if (!adeWidget.isInternal() && !m_widgetConfigurations.containsKey(widgetName)) { 723 CmsExternalWidgetConfiguration externalConfiguration = new CmsExternalWidgetConfiguration( 724 widgetName, 725 adeWidget.getInitCall(), 726 adeWidget.getJavaScriptResourceLinks(cms), 727 adeWidget.getCssResourceLinks(cms)); 728 m_widgetConfigurations.put(widgetName, externalConfiguration); 729 } 730 if (CmsContentService.LOG.isDebugEnabled()) { 731 CmsContentService.LOG.debug( 732 Messages.get().getBundle().key( 733 Messages.LOG_TAKE_READING_WIDGET_CONFIGURATION_TIME_2, 734 widgetName, 735 "" + (System.currentTimeMillis() - timer))); 736 } 737 738 } 739 m_widgets.add(widget); 740 } 741 if (complexWidget != null) { 742 CmsComplexWidgetData widgetData = complexWidget.getWidgetData(m_cms); 743 CmsExternalWidgetConfiguration externalConfig = widgetData.getExternalWidgetConfiguration(); 744 if (externalConfig != null) { 745 m_widgetConfigurations.put(complexWidget.getName(), externalConfig); 746 } 747 m_complexWidgets.put(CmsContentService.getAttributeName(schemaType), widgetData); 748 } 749 } catch (Exception e) { 750 LOG.error(e.getLocalizedMessage(), e); 751 } 752 753 // remove the leading slash from element path to check visibility 754 boolean visible = !m_contentHandler.hasVisibilityHandlers() 755 || m_contentHandler.isVisible(cms, schemaType, path.substring(1), m_file, m_locale); 756 if (!visible) { 757 // set the has invisible flag 758 m_hasInvisible = true; 759 } 760 boolean localeSynchronized = (m_contentHandler.hasSynchronizedElements() 761 && m_contentHandler.getSynchronizations(true).getSynchronizationPaths().contains(path.substring(1))) 762 || schemaType.getTypeName().equals(CmsXmlDynamicCategoryValue.TYPE_NAME); 763 764 boolean dynamicallyLoaded = (schemaType instanceof CmsXmlDynamicCategoryValue) 765 || (schemaType instanceof CmsXmlAccessRestrictionValue); 766 767 CmsAttributeConfiguration result = new CmsAttributeConfiguration( 768 label, 769 getHelp(schemaType), 770 widgetName, 771 widgetConfig, 772 readDefaultValue(schemaType, path), 773 configuredType.name(), 774 visible, 775 localeSynchronized, 776 dynamicallyLoaded); 777 return new DisplayTypeEvaluator(result, configuredType, defaultType, rule); 778 } 779 780 /** 781 * Reads the default value for the given type.<p> 782 * 783 * @param schemaType the schema type 784 * @param path the element path 785 * 786 * @return the default value 787 */ 788 private String readDefaultValue(I_CmsXmlSchemaType schemaType, String path) { 789 790 return m_contentHandler.getDefault(getCmsObject(), m_file, schemaType, path, m_locale); 791 } 792 793 /** 794 * Reads the types from the given content definition and adds the to the map of already registered 795 * types if necessary.<p> 796 * 797 * @param xmlContentDefinition the XML content definition 798 * @param path the element path 799 * 800 * @return the type 801 */ 802 private CmsType readTypes(CmsXmlContentDefinition xmlContentDefinition, String path) { 803 804 String typeName = (CmsStringUtil.isEmptyOrWhitespaceOnly(path) ? "" : (path + ":")) 805 + CmsContentService.getTypeUri(xmlContentDefinition); 806 if (m_registeredTypes.containsKey(typeName)) { 807 return m_registeredTypes.get(typeName); 808 } 809 CmsType type = new CmsType(typeName); 810 type.setChoiceMaxOccurrence(xmlContentDefinition.getChoiceMaxOccurs()); 811 m_registeredTypes.put(typeName, type); 812 CmsType choiceType = null; 813 if (type.isChoice()) { 814 choiceType = new CmsType(typeName + "/" + CmsType.CHOICE_ATTRIBUTE_NAME); 815 m_registeredTypes.put(choiceType.getId(), choiceType); 816 type.addAttribute(CmsType.CHOICE_ATTRIBUTE_NAME, choiceType, 1, xmlContentDefinition.getChoiceMaxOccurs()); 817 } 818 ArrayList<DisplayTypeEvaluator> evaluators = new ArrayList<DisplayTypeEvaluator>(); 819 for (I_CmsXmlSchemaType subType : xmlContentDefinition.getTypeSequence()) { 820 String subTypeName = null; 821 String childPath = path + "/" + subType.getName(); 822 String subAttributeName = CmsContentService.getAttributeName(subType.getName(), typeName); 823 DisplayTypeEvaluator ev = readConfiguration((A_CmsXmlContentValue)subType, childPath); 824 ev.setAttributeName(subAttributeName); 825 evaluators.add(ev); 826 CmsType subEntityType; 827 if (subType.isSimpleType()) { 828 subTypeName = CmsContentService.TYPE_NAME_PREFIX + subType.getTypeName(); 829 if (!m_registeredTypes.containsKey(subTypeName)) { 830 subEntityType = new CmsType(subTypeName); 831 m_registeredTypes.put(subTypeName, subEntityType); 832 } else { 833 subEntityType = m_registeredTypes.get(subTypeName); 834 } 835 } else { 836 CmsXmlContentDefinition subTypeDefinition = ((CmsXmlNestedContentDefinition)subType).getNestedContentDefinition(); 837 subTypeName = CmsContentService.getTypeUri(subTypeDefinition); 838 subEntityType = readTypes(subTypeDefinition, childPath); 839 } 840 if (choiceType != null) { 841 choiceType.addAttribute( 842 subAttributeName, 843 subEntityType, 844 subType.getMinOccurs(), 845 subType.getMaxOccurs()); 846 } else { 847 int minOccurs = subType.getMinOccurs(); 848 if ((subType instanceof CmsXmlDynamicCategoryValue) && (subType.getMinOccurs() == 0)) { 849 String dynamicCategoryPath; 850 if ("".equals(path)) { 851 dynamicCategoryPath = subType.getName(); 852 } else { 853 dynamicCategoryPath = path + "/" + subType.getName(); 854 } 855 m_dynamicCategoryFields.add(dynamicCategoryPath); 856 minOccurs = 1; 857 } 858 type.addAttribute(subAttributeName, subEntityType, minOccurs, subType.getMaxOccurs()); 859 } 860 } 861 DisplayType predecessor = null; 862 for (int i = 0; i < evaluators.size(); i++) { 863 DisplayTypeEvaluator ev = evaluators.get(i); 864 DisplayType successor = ((i + 1) < evaluators.size()) ? evaluators.get(i + 1).getProposedType() : null; 865 CmsAttributeConfiguration evaluated = ev.getEvaluatedConfiguration(predecessor, successor); 866 m_attributeConfigurations.put(ev.getAttributeName(), evaluated); 867 if (evaluated.isLocaleSynchronized()) { 868 m_localeSynchronizations.add(ev.getAttributeName()); 869 } 870 if (evaluated.isDynamicallyLoaded()) { 871 m_dynamicallyLoaded.add(ev.getAttributeName()); 872 } 873 predecessor = DisplayType.valueOf(evaluated.getDisplayType()); 874 } 875 return type; 876 } 877}