/* * Copyright (c) 2007-2008, Dennis M. Sosnoski. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following * disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of * JiBX nor the names of its contributors may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.jibx.schema.codegen; import org.jibx.schema.SchemaUtils; import org.jibx.schema.codegen.custom.ComponentExtension; import org.jibx.schema.elements.AnnotatedBase; import org.jibx.schema.elements.ElementElement; import org.jibx.schema.elements.SchemaBase; /** * Base class for code generation items. Each instance corresponds to a particular schema component, and this base class * tracks that schema component (by way of the extension information), along with related details and linkage * information. The linkage uses embedded list links, which allows replacing one instance with another with minimal * overhead. * * @author Dennis M. Sosnoski */ public abstract class Item { /** Corresponding schema component extension. */ private final ComponentExtension m_componentExtension; /** Flag for an optional item. */ private final boolean m_optional; /** Flag for a collection item. */ private final boolean m_collection; /** Flag for a nillable item. */ private final boolean m_nillable; /** Containing group item. */ private GroupItem m_parent; /** Attribute data present flag. */ private boolean m_attributePresent; /** Element data present flag. */ private boolean m_elementPresent; /** Character data content data present flag. */ private boolean m_contentPresent; /** Next item in list (null if none). */ protected Item m_next; /** Preceding item in list (null if none). */ protected Item m_last; /** Actual name to be used for item (null if to be inherited). */ private String m_name; /** * Basic constructor. This uses the schema component to determine all information other than the parent item group, * including the optional/nillable/collection flags. As a special case, if the parent group is associated with the * same component this sets all three of these flags false to avoid redundant handling. * * @param comp schema component * @param parent containing group (null if a top-level group) */ protected Item(AnnotatedBase comp, GroupItem parent) { // save basic values m_componentExtension = (ComponentExtension)comp.getExtension(); m_parent = parent; // set other values based on extension and component information if (parent == null || parent.getSchemaComponent() != comp) { m_optional = m_componentExtension.isOptional(); m_collection = m_componentExtension.isRepeated(); m_nillable = comp.type() == SchemaBase.ELEMENT_TYPE && ((ElementElement)comp).isNillable(); } else { m_optional = m_collection = m_nillable = false; } m_name = m_componentExtension.getBaseName(); } /** * Copy constructor. This creates a copy with a new parent. * * @param original * @param ref reference (for name override; null if none) * @param parent */ protected Item(Item original, Item ref, GroupItem parent) { m_componentExtension = original.m_componentExtension; m_parent = parent; m_optional = original.m_optional; m_collection = original.m_collection; m_nillable = original.m_nillable; m_attributePresent = original.m_attributePresent; m_elementPresent = original.m_elementPresent; m_contentPresent = original.m_contentPresent; if (ref == null || original.isFixedName()) { m_name = original.m_name; } else { m_name = ref.m_name; } } /** * Replace the parent for this item. * * @param parent */ protected void reparent(GroupItem parent) { m_parent = parent; } /** * Get schema component corresponding to this item. * * @return schema component */ public AnnotatedBase getSchemaComponent() { return (AnnotatedBase)m_componentExtension.getComponent(); } /** * Get schema component annotation corresponding to this item. * * @return schema component */ public ComponentExtension getComponentExtension() { return m_componentExtension; } /** * Get containing group item. * * @return group (null if a top-level group) */ public GroupItem getParent() { return m_parent; } /** * Check if the name is fixed by configuration. * * @return true if fixed, false if not */ public boolean isFixedName() { return m_componentExtension.getBaseName() != null; } /** * Get effective item name, applying inheritance if necessary. * * @return name */ public String getEffectiveName() { Item item = this; while (item.m_name == null) { item = item.m_parent; if (item == null) { throw new IllegalStateException("Inherited name with nothing to inherit"); } } return item.m_name; } /** * Get name set directly for this item. * * @return name (null if to be inherited) */ public String getName() { return m_name; } /** * Set name directly for this item. It is an error to call this method if the name is fixed. * * @param name (null if to be inherited) */ public void setName(String name) { if (isFixedName()) { throw new IllegalStateException("Internal error - attempt to change configured name"); } else { m_name = name; } } /** * Get next item in list. * * @return next */ public Item getNext() { return m_next; } /** * Check if item is optional. * * @return optional */ public boolean isOptional() { return m_optional; } /** * Check if a collection item. * * @return true if collection */ public boolean isCollection() { return m_collection; } /** * Check if an attribute is part of this item. This is only true for items corresponding to attribute * definitions, and groupings including these items which do not define an element name. * * @return true if attribute */ public boolean isAttributePresent() { return m_attributePresent; } /** * Check if a child elements is part of this item. This is true for all items corresponding to element * definitions, and all groupings which include such an item. * * @return true if content */ public boolean isElementPresent() { return m_elementPresent; } /** * Check if character data content is part of this item. This is true for all items corresponding to * simpleContent definitions, and all groupings which include such an item. * * @return true if content */ public boolean isContentPresent() { return m_contentPresent; } /** * Set attribute present in group. This cascades the attribute present flag upward through containing groups until * one is found which defines an element name. */ protected void forceAttributePresent() { if (!m_attributePresent && m_componentExtension.getComponent().type() != SchemaBase.ELEMENT_TYPE) { if (m_parent != null) { ((Item)m_parent).forceAttributePresent(); } } m_attributePresent = true; } /** * Set element present in group. This cascades the element present flag upward through containing groups until * one is found which defines an element name. */ protected void forceElementPresent() { if (!m_elementPresent && m_componentExtension.getComponent().type() != SchemaBase.ELEMENT_TYPE) { if (m_parent != null) { ((Item)m_parent).forceElementPresent(); } } m_elementPresent = true; } /** * Set character data content present in group. This cascades the content present flag upward through all containing * groups until one is found which defines an element name. */ protected void forceContentPresent() { if (!m_contentPresent && m_componentExtension.getComponent().type() != SchemaBase.ELEMENT_TYPE) { if (m_parent != null) { ((Item)m_parent).forceContentPresent(); } } m_contentPresent = true; } /** * Copy the item under a different parent. This is intended for replacing a reference with a copy, and allows the * reference to override settings from the original. * * @param ref reference (for overrides to copy; null if none) * @param parent * @return copy */ protected abstract Item copy(Item ref, GroupItem parent); /** * Classify the content of this item as attribute, element, and/or character data content. This needs to be done as * a separate step after construction in order to handle references, which must assume the content of the * definition, and also to work after inlining. This base class implementation does the classification based solely * on the schema component type. Any subclasses which override this method should call the base class implementation * before doing their own classification handling. */ protected void classifyContent() { // find the parent group with a different schema component GroupItem parent = getParent(); AnnotatedBase comp = getSchemaComponent(); while (parent != null && parent.getSchemaComponent() == comp) { parent = parent.getParent(); } if (parent != null) { // flag content type for that parent group switch (comp.type()) { case SchemaBase.ANY_TYPE: case SchemaBase.ELEMENT_TYPE: case SchemaBase.GROUP_TYPE: parent.forceElementPresent(); break; case SchemaBase.SIMPLECONTENT_TYPE: parent.forceContentPresent(); break; case SchemaBase.ANYATTRIBUTE_TYPE: case SchemaBase.ATTRIBUTE_TYPE: case SchemaBase.ATTRIBUTEGROUP_TYPE: parent.forceAttributePresent(); break; } } } /** * Generate a description of the item. For items with nested items this will show the complete structure. * * @param depth current nesting depth * @return description */ public abstract String describe(int depth); /** * Generate the standard leading text for description of the item. * * @param depth current nesting depth * @return leading text for description */ public String leadString(int depth) { StringBuffer buff = new StringBuffer(SchemaUtils.getIndentation(depth)); if (isOptional()) { buff.append("optional "); } if (isCollection()) { buff.append("repeating "); } if (m_attributePresent) { if (m_contentPresent) { buff.append("attribute+content "); } else { buff.append("attribute "); } } else if (m_contentPresent) { buff.append("content "); } return buff.toString(); } }