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