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.i18n.CmsMessages; 039import org.opencms.i18n.CmsMultiMessages; 040import org.opencms.main.CmsLog; 041import org.opencms.main.OpenCms; 042import org.opencms.util.CmsMacroResolver; 043import org.opencms.util.CmsStringUtil; 044import org.opencms.util.I_CmsMacroResolver; 045import org.opencms.widgets.A_CmsWidget; 046import org.opencms.widgets.CmsWidgetConfigurationException; 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 content resource. */ 258 private CmsFile m_file; 259 260 /** Indicates the visited content has fields that are configured to be invisible to the current user. */ 261 private boolean m_hasInvisible; 262 263 /** The content locale. */ 264 private Locale m_locale; 265 266 /** The locale synchronized attribute names. */ 267 private List<String> m_localeSynchronizations; 268 269 /** The dynamically loaded attribute names. */ 270 private List<String> m_dynamicallyLoaded; 271 272 /** The messages. */ 273 private CmsMultiMessages m_messages; 274 275 /** The registered types. */ 276 private Map<String, CmsType> m_registeredTypes; 277 278 /** The tab informations. */ 279 private List<CmsTabInfo> m_tabInfos; 280 281 /** The widget configurations. */ 282 private Map<String, CmsExternalWidgetConfiguration> m_widgetConfigurations; 283 284 /** The widgets encountered by this visitor. */ 285 private List<I_CmsWidget> m_widgets = new ArrayList<I_CmsWidget>(); 286 287 /** The root content definition. */ 288 private CmsXmlContentDefinition m_rootContentDefinition; 289 290 /** 291 * Constructor.<p> 292 * 293 * @param cms the CMS context 294 * @param file the content file 295 * @param locale the content locale 296 */ 297 public CmsContentTypeVisitor(CmsObject cms, CmsFile file, Locale locale) { 298 299 m_file = file; 300 m_cms = cms; 301 m_locale = locale; 302 } 303 304 /** 305 * Gets the CMS context.<p> 306 * 307 * @return the CMS context 308 */ 309 public CmsObject getCmsObject() { 310 311 return m_cms; 312 } 313 314 /** 315 * Gets the list of widgets which have been processed by this visitor.<p> 316 * 317 * @return the list of widget 318 */ 319 public List<I_CmsWidget> getCollectedWidgets() { 320 321 return Collections.unmodifiableList(m_widgets); 322 } 323 324 /** 325 * Gets the map of complex widget configurations.<p> 326 * 327 * @return a map from attribute names to complex widget configurations 328 */ 329 public Map<String, CmsComplexWidgetData> getComplexWidgetData() { 330 331 return m_complexWidgets; 332 } 333 334 /** 335 * Returns the tabInfos.<p> 336 * 337 * @return the tabInfos 338 */ 339 public List<CmsTabInfo> getTabInfos() { 340 341 return m_tabInfos; 342 } 343 344 /** 345 * Returns if the visited content has invisible fields.<p> 346 * 347 * @return <code>true</code> if the visited content has invisible fields 348 */ 349 public boolean hasInvisibleFields() { 350 351 return m_hasInvisible; 352 } 353 354 /** 355 * Returns <code>true</code> if the value of the attribute is dynamically loaded. 356 * @param attributeName the attribute to check 357 * @return <code>true</code> if the value of the attribute is dynamically loaded. 358 */ 359 public boolean isDynamicallyLoaded(String attributeName) { 360 361 return m_dynamicallyLoaded.contains(attributeName); 362 } 363 364 /** 365 * Checks if the content type widgets are compatible with the new content editor.<p> 366 * 367 * @param xmlContentDefinition the content definition 368 * 369 * @return <code>true</code> if the content type widgets are compatible with the new content editor 370 * 371 * @throws CmsXmlException if something goes wrong reading the type widget 372 */ 373 public boolean isEditorCompatible(CmsXmlContentDefinition xmlContentDefinition) throws CmsXmlException { 374 375 boolean result = true; 376 for (I_CmsXmlSchemaType subType : xmlContentDefinition.getTypeSequence()) { 377 if (subType.isSimpleType()) { 378 result = isEditorCompatible((A_CmsXmlContentValue)subType); 379 } else { 380 CmsXmlContentDefinition subTypeDefinition = ((CmsXmlNestedContentDefinition)subType).getNestedContentDefinition(); 381 result = isEditorCompatible(subTypeDefinition); 382 } 383 if (!result) { 384 break; 385 } 386 } 387 return result; 388 } 389 390 /** 391 * Visits all types within the XML content definition.<p> 392 * 393 * @param xmlContentDefinition the content definition 394 * @param messageLocale the locale 395 */ 396 /** 397 * Visits all types within the XML content definition.<p> 398 * 399 * @param xmlContentDefinition the content definition 400 * @param messageLocale the locale 401 */ 402 public void visitTypes(CmsXmlContentDefinition xmlContentDefinition, Locale messageLocale) { 403 404 m_rootContentDefinition = xmlContentDefinition; 405 m_contentHandler = xmlContentDefinition.getContentHandler(); 406 CmsMessages messages = null; 407 m_messages = new CmsMultiMessages(messageLocale); 408 m_messages.setFallbackHandler(xmlContentDefinition.getContentHandler().getMessageKeyHandler()); 409 410 try { 411 messages = OpenCms.getWorkplaceManager().getMessages(messageLocale); 412 if (messages != null) { 413 m_messages.addMessages(messages); 414 } 415 messages = m_contentHandler.getMessages(messageLocale); 416 if (messages != null) { 417 m_messages.addMessages(messages); 418 } 419 } catch (Exception e) { 420 // may happen during start up 421 LOG.debug(e.getMessage(), e); 422 } 423 // generate a new multi messages object and add the messages from the workplace 424 425 m_attributeConfigurations = new HashMap<String, CmsAttributeConfiguration>(); 426 m_widgetConfigurations = new HashMap<String, CmsExternalWidgetConfiguration>(); 427 m_registeredTypes = new HashMap<String, CmsType>(); 428 m_localeSynchronizations = new ArrayList<String>(); 429 m_dynamicallyLoaded = new ArrayList<String>(); 430 m_tabInfos = collectTabInfos(xmlContentDefinition); 431 readTypes(xmlContentDefinition, ""); 432 } 433 434 /** 435 * Returns the attribute configurations.<p> 436 * 437 * @return the attribute configurations 438 */ 439 protected Map<String, CmsAttributeConfiguration> getAttributeConfigurations() { 440 441 return m_attributeConfigurations; 442 } 443 444 /** 445 * Returns the locale synchronized attribute names.<p> 446 * 447 * @return the locale synchronized attribute names 448 */ 449 protected List<String> getLocaleSynchronizations() { 450 451 return m_localeSynchronizations; 452 } 453 454 /** 455 * Returns the types of the visited content definition.<p> 456 * 457 * @return the types 458 */ 459 protected Map<String, CmsType> getTypes() { 460 461 return m_registeredTypes; 462 } 463 464 /** 465 * Returns the external widget configurations.<p> 466 * 467 * @return the external widget configurations 468 */ 469 protected Collection<CmsExternalWidgetConfiguration> getWidgetConfigurations() { 470 471 return m_widgetConfigurations.values(); 472 } 473 474 /** 475 * Returns the tab informations for the given content definition.<p> 476 * 477 * @param definition the content definition 478 * 479 * @return the tab informations 480 */ 481 private List<CmsTabInfo> collectTabInfos(CmsXmlContentDefinition definition) { 482 483 List<CmsTabInfo> result = new ArrayList<CmsTabInfo>(); 484 CmsMacroResolver resolver = new CmsMacroResolver(); 485 resolver.setMessages(m_messages); 486 if (definition.getContentHandler().getTabs() != null) { 487 for (CmsXmlContentTab xmlTab : definition.getContentHandler().getTabs()) { 488 String tabName; 489 // in case the tab name attribute contains a localization macro 490 if (xmlTab.getTabName().contains(MESSAGE_MACRO_START) 491 || xmlTab.getTabName().contains(MESSAGE_MACRO_START_OLD)) { 492 tabName = resolver.resolveMacros(xmlTab.getTabName()); 493 } else { 494 tabName = m_messages.keyDefault( 495 A_CmsWidget.LABEL_PREFIX + definition.getInnerName() + "." + xmlTab.getTabName(), 496 xmlTab.getTabName()); 497 } 498 499 result.add( 500 new CmsTabInfo( 501 tabName, 502 xmlTab.getIdName(), 503 xmlTab.getStartName(), 504 xmlTab.isCollapsed(), 505 resolver.resolveMacros(xmlTab.getDescription()))); 506 } 507 } 508 return result; 509 } 510 511 /** 512 * Returns the help information for this value.<p> 513 * 514 * @param value the value 515 * 516 * @return the help information 517 */ 518 private String getHelp(I_CmsXmlSchemaType value) { 519 520 StringBuffer result = new StringBuffer(64); 521 I_CmsXmlContentHandler handler = value.getContentDefinition().getContentHandler(); 522 if (handler instanceof CmsDefaultXmlContentHandler) { 523 CmsDefaultXmlContentHandler defaultHandler = (CmsDefaultXmlContentHandler)handler; 524 String help = defaultHandler.getFieldHelp().get(value.getName()); 525 if (help != null) { 526 CmsMacroResolver resolver = new CmsMacroResolver(); 527 resolver.setKeepEmptyMacros(true); 528 resolver.setMessages(m_messages); 529 return resolver.resolveMacros(help); 530 } 531 } 532 result.append(A_CmsWidget.LABEL_PREFIX); 533 result.append(getTypeKey(value)); 534 result.append(A_CmsWidget.HELP_POSTFIX); 535 return m_messages.keyDefault(result.toString(), null); 536 } 537 538 /** 539 * Returns the label for this value.<p> 540 * 541 * @param value the value 542 * 543 * @return the label 544 */ 545 private String getLabel(I_CmsXmlSchemaType value) { 546 547 I_CmsXmlContentHandler handler = value.getContentDefinition().getContentHandler(); 548 if (handler instanceof CmsDefaultXmlContentHandler) { 549 CmsDefaultXmlContentHandler defaultHandler = (CmsDefaultXmlContentHandler)handler; 550 String label = defaultHandler.getFieldLabels().get(value.getName()); 551 if (label != null) { 552 CmsMacroResolver resolver = new CmsMacroResolver(); 553 resolver.setKeepEmptyMacros(true); 554 resolver.setMessages(m_messages); 555 return resolver.resolveMacros(label); 556 } 557 } 558 StringBuffer result = new StringBuffer(64); 559 result.append(A_CmsWidget.LABEL_PREFIX); 560 result.append(getTypeKey(value)); 561 return m_messages.keyDefault(result.toString(), value.getName()); 562 } 563 564 /** 565 * Returns the schema type message key.<p> 566 * 567 * @param value the schema type 568 * 569 * @return the schema type message key 570 */ 571 private String getTypeKey(I_CmsXmlSchemaType value) { 572 573 StringBuffer result = new StringBuffer(64); 574 result.append(value.getContentDefinition().getInnerName()); 575 result.append('.'); 576 result.append(value.getName()); 577 return result.toString(); 578 } 579 580 /** 581 * Checks if the content value widget is compatible with the new content editor.<p> 582 * 583 * @param schemaType the content value type 584 * 585 * @return <code>true</code> if the content value widget is compatible with the new content editor 586 * 587 * @throws CmsXmlException if something goes wrong reading the type widget 588 */ 589 private boolean isEditorCompatible(A_CmsXmlContentValue schemaType) throws CmsXmlException { 590 591 boolean result = false; 592 I_CmsXmlContentHandler contentHandler = schemaType.getContentDefinition().getContentHandler(); 593 // We don't care about the old editor for the 'inheritable' widget configuration, 594 // so we're using the old getWidget method here 595 I_CmsWidget widget = contentHandler.getWidget(schemaType); 596 result = (widget == null) || (widget instanceof I_CmsADEWidget); 597 return result; 598 } 599 600 /** 601 * Returns if an element with the given path will be displayed at root level of a content editor tab.<p> 602 * 603 * @param path the element path 604 * 605 * @return <code>true</code> if an element with the given path will be displayed at root level of a content editor tab 606 */ 607 private boolean isTabRootLevel(String path) { 608 609 path = path.substring(1); 610 if (!path.contains("/")) { 611 return true; 612 } 613 if (m_tabInfos != null) { 614 for (CmsTabInfo info : m_tabInfos) { 615 if (info.isCollapsed() 616 && path.startsWith(info.getStartName()) 617 && !path.substring(info.getStartName().length() + 1).contains("/")) { 618 return true; 619 } 620 } 621 } 622 return false; 623 } 624 625 /** 626 * Reads the attribute configuration for the given schema type. May return <code>null</code> if no special configuration was set.<p> 627 * 628 * @param schemaType the schema type 629 * @param path the attribute path 630 * 631 * @return the attribute configuration 632 */ 633 private DisplayTypeEvaluator readConfiguration(A_CmsXmlContentValue schemaType, String path) { 634 635 String widgetName = null; 636 String widgetConfig = null; 637 CmsObject cms = getCmsObject(); 638 String label = getLabel(schemaType); 639 // set the default display type 640 DisplayType configuredType = DisplayType.none; 641 DisplayType defaultType = DisplayType.none; 642 EvaluationRule rule = EvaluationRule.none; 643 try { 644 WidgetInfo widgetInfo = CmsWidgetUtil.collectWidgetInfo(m_rootContentDefinition, path); 645 I_CmsWidget widget = widgetInfo.getWidget(); 646 I_CmsComplexWidget complexWidget = widgetInfo.getComplexWidget(); 647 configuredType = widgetInfo.getDisplayType(); 648 if (configuredType.equals(DisplayType.none) && schemaType.isSimpleType()) { 649 // check the type is on the root level of the document, those will be displayed 'wide' 650 // the path will always have a leading '/' 651 // also in case the label has more than 15 characters, we display 'wide' 652 if (isTabRootLevel(path)) { 653 rule = EvaluationRule.rootLevel; 654 } else if (label.length() > 15) { 655 rule = EvaluationRule.labelLength; 656 } else if ((schemaType.getMinOccurs() == 0)) { 657 rule = EvaluationRule.optional; 658 } 659 } 660 if (widget != null) { 661 widgetName = widget.getClass().getName(); 662 if ((configuredType == DisplayType.column) 663 && !(schemaType.isSimpleType() 664 && (schemaType.getMaxOccurs() == 1) 665 && widget.isCompactViewEnabled())) { 666 // column view is not allowed for this widget 667 configuredType = DisplayType.singleline; 668 } 669 long timer = 0; 670 if (widget instanceof I_CmsADEWidget) { 671 if (CmsContentService.LOG.isDebugEnabled()) { 672 timer = System.currentTimeMillis(); 673 } 674 I_CmsADEWidget adeWidget = (I_CmsADEWidget)widget; 675 defaultType = adeWidget.getDefaultDisplayType(); 676 widgetName = adeWidget.getWidgetName(); 677 widgetConfig = adeWidget.getConfiguration(cms, schemaType, m_messages, m_file, m_locale); 678 if (!adeWidget.isInternal() && !m_widgetConfigurations.containsKey(widgetName)) { 679 CmsExternalWidgetConfiguration externalConfiguration = new CmsExternalWidgetConfiguration( 680 widgetName, 681 adeWidget.getInitCall(), 682 adeWidget.getJavaScriptResourceLinks(cms), 683 adeWidget.getCssResourceLinks(cms)); 684 m_widgetConfigurations.put(widgetName, externalConfiguration); 685 } 686 if (CmsContentService.LOG.isDebugEnabled()) { 687 CmsContentService.LOG.debug( 688 Messages.get().getBundle().key( 689 Messages.LOG_TAKE_READING_WIDGET_CONFIGURATION_TIME_2, 690 widgetName, 691 "" + (System.currentTimeMillis() - timer))); 692 } 693 694 } 695 m_widgets.add(widget); 696 } 697 if (complexWidget != null) { 698 CmsComplexWidgetData widgetData = complexWidget.getWidgetData(m_cms); 699 CmsExternalWidgetConfiguration externalConfig = widgetData.getExternalWidgetConfiguration(); 700 if (externalConfig != null) { 701 m_widgetConfigurations.put(complexWidget.getName(), externalConfig); 702 } 703 m_complexWidgets.put(CmsContentService.getAttributeName(schemaType), widgetData); 704 } 705 } catch (CmsWidgetConfigurationException e) { 706 LOG.error(e.getLocalizedMessage(), e); 707 } catch (Exception e) { 708 // may happen if no widget was set for the value 709 CmsContentService.LOG.debug(e.getMessage(), e); 710 } 711 712 // remove the leading slash from element path to check visibility 713 boolean visible = !m_contentHandler.hasVisibilityHandlers() 714 || m_contentHandler.isVisible(cms, schemaType, path.substring(1), m_file, m_locale); 715 if (!visible) { 716 // set the has invisible flag 717 m_hasInvisible = true; 718 } 719 boolean localeSynchronized = (m_contentHandler.hasSynchronizedElements() 720 && m_contentHandler.getSynchronizations().contains(path.substring(1))) 721 || schemaType.getTypeName().equals(CmsXmlDynamicCategoryValue.TYPE_NAME); 722 723 boolean dynamicallyLoaded = schemaType instanceof CmsXmlDynamicCategoryValue; 724 725 CmsAttributeConfiguration result = new CmsAttributeConfiguration( 726 label, 727 getHelp(schemaType), 728 widgetName, 729 widgetConfig, 730 readDefaultValue(schemaType, path), 731 configuredType.name(), 732 visible, 733 localeSynchronized, 734 dynamicallyLoaded); 735 return new DisplayTypeEvaluator(result, configuredType, defaultType, rule); 736 } 737 738 /** 739 * Reads the default value for the given type.<p> 740 * 741 * @param schemaType the schema type 742 * @param path the element path 743 * 744 * @return the default value 745 */ 746 private String readDefaultValue(I_CmsXmlSchemaType schemaType, String path) { 747 748 return m_contentHandler.getDefault(getCmsObject(), m_file, schemaType, path, m_locale); 749 } 750 751 /** 752 * Reads the types from the given content definition and adds the to the map of already registered 753 * types if necessary.<p> 754 * 755 * @param xmlContentDefinition the XML content definition 756 * @param path the element path 757 * 758 * @return the type 759 */ 760 private CmsType readTypes(CmsXmlContentDefinition xmlContentDefinition, String path) { 761 762 String typeName = (CmsStringUtil.isEmptyOrWhitespaceOnly(path) ? "" : (path + ":")) 763 + CmsContentService.getTypeUri(xmlContentDefinition); 764 if (m_registeredTypes.containsKey(typeName)) { 765 return m_registeredTypes.get(typeName); 766 } 767 CmsType type = new CmsType(typeName); 768 type.setChoiceMaxOccurrence(xmlContentDefinition.getChoiceMaxOccurs()); 769 m_registeredTypes.put(typeName, type); 770 CmsType choiceType = null; 771 if (type.isChoice()) { 772 choiceType = new CmsType(typeName + "/" + CmsType.CHOICE_ATTRIBUTE_NAME); 773 m_registeredTypes.put(choiceType.getId(), choiceType); 774 type.addAttribute(CmsType.CHOICE_ATTRIBUTE_NAME, choiceType, 1, xmlContentDefinition.getChoiceMaxOccurs()); 775 } 776 ArrayList<DisplayTypeEvaluator> evaluators = new ArrayList<DisplayTypeEvaluator>(); 777 for (I_CmsXmlSchemaType subType : xmlContentDefinition.getTypeSequence()) { 778 779 String subTypeName = null; 780 String childPath = path + "/" + subType.getName(); 781 String subAttributeName = CmsContentService.getAttributeName(subType.getName(), typeName); 782 DisplayTypeEvaluator ev = readConfiguration((A_CmsXmlContentValue)subType, childPath); 783 ev.setAttributeName(subAttributeName); 784 evaluators.add(ev); 785 CmsType subEntityType; 786 if (subType.isSimpleType()) { 787 subTypeName = CmsContentService.TYPE_NAME_PREFIX + subType.getTypeName(); 788 if (!m_registeredTypes.containsKey(subTypeName)) { 789 subEntityType = new CmsType(subTypeName); 790 m_registeredTypes.put(subTypeName, subEntityType); 791 } else { 792 subEntityType = m_registeredTypes.get(subTypeName); 793 } 794 } else { 795 CmsXmlContentDefinition subTypeDefinition = ((CmsXmlNestedContentDefinition)subType).getNestedContentDefinition(); 796 subTypeName = CmsContentService.getTypeUri(subTypeDefinition); 797 subEntityType = readTypes(subTypeDefinition, childPath); 798 } 799 if (choiceType != null) { 800 choiceType.addAttribute( 801 subAttributeName, 802 subEntityType, 803 subType.getMinOccurs(), 804 subType.getMaxOccurs()); 805 } else { 806 type.addAttribute(subAttributeName, subEntityType, subType.getMinOccurs(), subType.getMaxOccurs()); 807 } 808 } 809 DisplayType predecessor = null; 810 for (int i = 0; i < evaluators.size(); i++) { 811 DisplayTypeEvaluator ev = evaluators.get(i); 812 DisplayType successor = ((i + 1) < evaluators.size()) ? evaluators.get(i + 1).getProposedType() : null; 813 CmsAttributeConfiguration evaluated = ev.getEvaluatedConfiguration(predecessor, successor); 814 m_attributeConfigurations.put(ev.getAttributeName(), evaluated); 815 if (evaluated.isLocaleSynchronized()) { 816 m_localeSynchronizations.add(ev.getAttributeName()); 817 } 818 if (evaluated.isDynamicallyLoaded()) { 819 m_dynamicallyLoaded.add(ev.getAttributeName()); 820 } 821 predecessor = DisplayType.valueOf(evaluated.getDisplayType()); 822 } 823 return type; 824 } 825}