/** * Redistribution and use of this software and associated documentation * ("Software"), with or without modification, are permitted provided * that the following conditions are met: * * 1. Redistributions of source code must retain copyright * statements and notices. Redistributions must also contain a * copy of this document. * * 2. 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. * * 3. The name "Exolab" must not be used to endorse or promote * products derived from this Software without prior written * permission of Intalio, Inc. For written permission, * please contact info@exolab.org. * * 4. Products derived from this Software may not be called "Exolab" * nor may "Exolab" appear in their names without prior written * permission of Intalio, Inc. Exolab is a registered * trademark of Intalio, Inc. * * 5. Due credit should be given to the Exolab Project * (http://www.exolab.org/). * * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESSED 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 * INTALIO, INC. OR ITS 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. * * Copyright 1999-2004 (C) Intalio, Inc. All Rights Reserved. * * $Id$ */ package org.exolab.castor.xml.schema; import java.util.Enumeration; import org.exolab.castor.xml.ValidationException; /** * The XML Schema ComplexType class. * * @author <a href="mailto:kvisco@intalio.com">Keith Visco</a> * @version $Revision$ $Date: 2006-02-01 15:47:48 -0700 (Wed, 01 Feb * 2006) $ */ public class ComplexType extends XMLType implements ContentModelGroup, Referable { /** SerialVersionUID */ private static final long serialVersionUID = 5348120259072084658L; /** * The abstract flag for this ComplexType. */ private boolean _abstract = false; /** * A wildcard that represents an {@literal <anyAttribute>} element if any. * Only one {@literal <anyAttribute>} can appear inside the global scope of * a complexType */ private Wildcard _anyAttribute = null; /** * The attribute declarations for this ComplexType. */ private AttributeGroupDecl _attributes = null; /** * The name of the base type used in <restriction>> or <extension>. */ private String _baseType = null; /** * The value of the 'block' property for this ComplexType. This value may be * null. */ private BlockList _block = null; /** * a flag set to true if this complexType is a complexContent. */ private boolean _complexContent = true; /** * The content type ("mixed", "simpleType","elemOnly") for this ComplexType. */ private ContentType _content = ContentType.elemOnly; /** * The ContentModel for this ComplexType. */ private ContentModelGroup _contentModel = null; /** * The final property for this ComplexType. This value may be null.. */ private FinalList _final = null; /** * The parent structure for this {@link ComplexType} (either an {@link ElementDecl} or a * {@link Schema}). */ private Structure _parent = null; /** * a flag set to true if this complexType is a restriction. */ private boolean _restricted = false; /** * An attribute that indicates if this ComplexType is a redefinition. */ private boolean _redefinition = false; // ------------------/ // - Constructor(s) -/ // ------------------/ /** * Creates a new {@link ComplexType} instance with no name. * * @param schema * the owning {@link Schema} document */ public ComplexType(final Schema schema) { this(schema, null); } // -- Complextype /** * Creates a new {@link ComplexType} with the given name. * * @param schema * The owning {@link Schema} instance * @param name * Name of the {@link ComplexType} */ public ComplexType(final Schema schema, final String name) { super(); if (schema == null) { String err = NULL_ARGUMENT + "; 'schema' must not be null."; throw new IllegalArgumentException(err); } setSchema(schema); setName(name); _attributes = new AttributeGroupDecl(schema); _contentModel = new ContentModelGroupImpl(); } // -- Complextype /** * Adds the given {@link AttributeDecl} to this {@link ComplexType}. * * @param attrDecl * the {@link AttributeDecl} to add to this {@link ComplexType} */ public void addAttributeDecl(final AttributeDecl attrDecl) { _attributes.addAttribute(attrDecl); // --set the parent attrDecl.setParent(this); } // -- addAttributeDecl /** * Removes the given {@link AttributeDecl} from this {@link ComplexType}. * * @param attrDecl * the AttributeDecl to remove. */ public void removeAttributeDecl(final AttributeDecl attrDecl) { _attributes.removeAttribute(attrDecl); } /** * Adds the given AttributeGroupReference to this ComplexType. * * @param attrGroupRef * the AttributeGroupReference to add to this ComplexType */ public void addAttributeGroupReference(final AttributeGroupReference attrGroupRef) { _attributes.addReference(attrGroupRef); } // -- addAttributeGroupReference /** * Removes the given {@link AttributeGroupReference} from this {@link ComplexType}. * * @param attrGroupRef * the {@link AttributeGroupReference} to remove. */ public void removeAttributeGroupReference( final AttributeGroupReference attrGroupRef) { _attributes.removeReference(attrGroupRef); } /** * Creates an {@link AttributeDecl} with the given name. The attribute declaration * will still need to be added to this {@link ComplexType}, or another archetype in * the same schema, by making a call to #addAttributeDecl * * @param name * the name of the attribute * @return the new AttributeDecl */ public AttributeDecl createAttributeDecl(final String name) { return new AttributeDecl(getSchema(), name); } // -- createAttributeDecl /** * Returns the wilcard used in this complexType (can be null). * * @return the wilcard used in this complexType (can be null) */ public Wildcard getAnyAttribute() { return _anyAttribute; } /** * Returns the {@link AttributeDecl} associated with the given name. * @param name Name of the associated {@link AttributeDecl} * * @return the {@link AttributeDecl} associated with the given name, or null if no * {@link AttributeDecl} with the given name was found. */ public AttributeDecl getAttributeDecl(final String name) { AttributeDecl result = _attributes.getAttribute(name); return result; } // -- getAttributeDecl /** * Returns an Enumeration of *all* the AttributeDecl objects declared within * this ComplexType. The Enumeration will contain all AttributeDecl from * AttributeGroup references as well. To return only locally declared * attributes make a call to <code>getLocalAttributeDecls</code>. * * @return an Enumeration of all the AttributeDecl objects declared within * this Complextype */ public Enumeration getAttributeDecls() { return _attributes.getAttributes(); } // -- getAttributeDecls /** * Returns an Enumeration of *all* locally defined AttributeDecl declared * within this ComplexType. The Enumeration will not contain any * AttributeDecl from AttributeGroup references. * * @return an Enumeration of all locally declared AttributeDecl. */ public Enumeration getLocalAttributeDecls() { return _attributes.getLocalAttributes(); } // -- getLocalAttributeDecls /** * Returns an Enumeration of all the AttributeGroup that are referenced * within this ComplexType. * * @return an Enumeration of all the AttributeGroup that are referenced * within this ComplexType. */ public Enumeration getAttributeGroupReferences() { return _attributes.getLocalAttributeGroupReferences(); } /** * Returns the base type that this type inherits from. * * @return the base type (also called super type). */ public XMLType getBaseType() { if ((_baseType != null) && (super.getBaseType() == null)) { XMLType baseType = getSchema().getType(_baseType); setBaseType(baseType); } return super.getBaseType(); } // -- getBaseType /** * Returns the value of the 'block' attribute for this element. * * @return the value of the 'block' attribute for this element */ public BlockList getBlock() { return _block; } // -- getBlock /** * Returns the content type of this ComplexType. The Content Type holds the * information about the content of the complexType. For instance, if this * complexType is a simpleContent then the simpleType information will be * hold in the content type. * * @return the content type of this ComplexType */ public ContentType getContentType() { return _content; } // -- getContentType /** * Returns the list of values for the final property for this ComplexType, * or null if no final values have been set. * * @return the FinalList for this ComplexType */ public FinalList getFinal() { return _final; } // -- getFinal /** * Returns the parent of this ComplexType, this value may be null if no * parent has been set. * * @return the parent Structure of this ComplexType. */ public Structure getParent() { return _parent; } // -- getParent /** * Returns the Id used to Refer to this Object. * * @return the Id used to Refer to this Object * @see Referable */ public String getReferenceId() { return "complexType:" + getName(); } // -- getReferenceId /** * A helper method that returns true if this complexType contains an * {@literal <any>} element. * * @return method that returns true if this complexType contains an * {@literal <any>} element. */ public boolean hasAny() { boolean result = false; Enumeration enumeration = _contentModel.enumerate(); while (enumeration.hasMoreElements() && !result) { Structure struct = (Structure) enumeration.nextElement(); switch (struct.getStructureType()) { case Structure.ELEMENT: break; case Structure.GROUP: case Structure.MODELGROUP: result = ((Group) struct).hasAny(); break; case Structure.WILDCARD: result = true; break; default: break; } } return result; } /** * Returns true if this ComplexType has been marked as Abstract. * * @return true if this ComplexType is "abstract". */ public boolean isAbstract() { return _abstract; } // -- isAbstract /** * Returns true if this complexType is a redefinition. * * @return true if this complexType is a redefinition. */ public boolean isRedefined() { return _redefinition; } /** * Returns true if this is a top level {@link ComplexType}. * * @return true if this is a top level {@link ComplexType} */ public boolean isTopLevel() { if (getName() == null) { return false; } if (getSchema() == null) { return false; } return (getSchema().getComplexType(getName()) == this); } // -- isTopLevel /** * Returns true if this complexType is a 'complexContent'. * * @return true if this complexType is a 'complexContent' */ public boolean isComplexContent() { return _complexContent; } /** * Returns true if this complexType is a 'simpleContent'. * * @return true if this complexType is a 'simpleContent' */ public boolean isSimpleContent() { return (!_complexContent); } /** * Returns true if this complexType is a restriction. * * @return true if this complexType is a restriction */ public boolean isRestricted() { return _restricted; } /** * Sets whether or not this ComplexType should be abstract. * * @param isAbstract * a boolean that when true makes this ComplexType abstract. */ public void setAbstract(final boolean isAbstract) { _abstract = isAbstract; } // -- setAbstract /** * Sets the wildcard (anyAttribute) of the {@link ComplexType}. * * @exception SchemaException * thrown when a wildcard as already be set or when the * wildCard is not an {@literal <anyAttribute>}. */ public void setAnyAttribute(final Wildcard wildcard) throws SchemaException { if (wildcard != null) { if (_anyAttribute != null) { String err = "<anyAttribute> already set in this complexType: " + this.getName(); throw new SchemaException(err); } if (!wildcard.isAttributeWildcard()) { String err = "In complexType, " + this.getName() + "the wildcard must be an <anyAttribute>"; throw new SchemaException(err); } } _anyAttribute = wildcard; } /** * Removes the given Wildcard from this Group. * * @param wildcard * the Wildcard to remove. * @return true if the wildcard has been successfully removed, false * otherwise. */ public boolean removeWildcard(final Wildcard wildcard) { if (wildcard == null) { return false; } if (wildcard.equals(_anyAttribute)) { _anyAttribute = null; return true; } return false; } public void addWildcard(final Wildcard wildcard) throws SchemaException { setAnyAttribute(wildcard); } /** * Sets the base type that this type is derived from. * * @param base * the type that this type is derived from */ public void setBase(final String base) { _baseType = base; } // -- setBase /** * Sets the base type for this ComplexType. * * @param baseType * the base type which this ComplexType extends or restricts */ public void setBaseType(final XMLType baseType) { super.setBaseType(baseType); if (baseType != null) { if (baseType.isSimpleType()) { _complexContent = false; _content = new SimpleContent((SimpleType) baseType); } else if (baseType.isComplexType()) { ComplexType complexType = (ComplexType) baseType; if (complexType.isSimpleContent()) { _complexContent = false; _content = ((SimpleContent) complexType.getContentType()) .copy(); } else { _complexContent = true; } } else { // -- assuming anyType _complexContent = true; } } } // -- setBaseType /** * Sets the value of the 'block' attribute for this ComplexType. * * @param block * the value of the block attribute for this ComplexType * definition. */ public void setBlock(final BlockList block) { if (block != null) { if (block.hasSubstitution()) { String err = "'substitution' is an illegal value of the " + "'block' attribute for a complexType definition."; throw new IllegalArgumentException(err); } } _block = block; } // -- setBlock /** * Sets the value of the 'block' attribute for this ComplexType. * * @param block * the value of the block attribute for this ComplexType * definition. */ public void setBlock(final String block) { if (block == null) { _block = null; } else { setBlock(new BlockList(block)); } } // -- setBlock /** * Sets whether or not this complexType is a 'complexContent'. * * @param complexContent * true if this complexType is a 'complexContent' */ public void setComplexContent(final boolean complexContent) { this._complexContent = complexContent; } /** * Sets the content type of this complexType. The Content Type holds the * information about the content of the complexType. For instance, if this * complexType is a simpleContent then the simpleType information will be * hold in the content type. * * @param contentType * the ContentType for this complexType */ public void setContentType(final ContentType contentType) { _content = contentType; } // -- setContentType /** * Sets the value of the 'final' attribute for this ComplexType definition. * * @param finalList * the value of the final attribute for this ComplexType * definition. */ public void setFinal(final FinalList finalList) { _final = finalList; } // -- setFinal /** * Sets the value of the 'final' attribute for this ComplexType definition. * * @param finalValue * the value of the final attribute for this ComplexType * definition. */ public void setFinal(final String finalValue) { if (finalValue == null) { _final = null; } else { _final = new FinalList(finalValue); } } // -- setFinal /** * Sets this Group has redefined. */ public void setRedefined() { _redefinition = true; } /** * Sets whether or not this complexType is a 'simpleContent'. * * @param simpleContent * true if this complexType is a 'simpleContent' */ public void setSimpleContent(final boolean simpleContent) { _complexContent = (!simpleContent); } /** * Sets whether or not this complexType is a restriction. * * @param restricted * true if this complexType is a restriction */ public void setRestriction(final boolean restricted) { this._restricted = restricted; } public void useResolver(final Resolver resolver) { // do nothing for now } // ---------------------------------------/ // - Implementation of ContentModelGroup -/ // ---------------------------------------/ /** * Adds the given ElementDecl to this {@link ContentModelGroup}. * * @param elementDecl * the ElementDecl to add * @exception SchemaException * when an ElementDecl already exists with the same name * as the given ElementDecl */ public void addElementDecl(final ElementDecl elementDecl) throws SchemaException { _contentModel.addElementDecl(elementDecl); // --set the parent elementDecl.setParent(this); } // -- addElementDecl /** * Removes the given ElementDecl from this ContentModelGroup. * * @param element * the ElementDecl to remove. * @return true if the element has been successfully removed, false * otherwise. */ public boolean removeElementDecl(final ElementDecl element) { return _contentModel.removeElementDecl(element); } /** * Adds the given Group to this {@link ContentModelGroup}. * * @param group * the Group to add * @exception SchemaException * when a group with the same name as the specified group * already exists in the current scope */ public void addGroup(final Group group) throws SchemaException { _contentModel.addGroup(group); // -- set reference to parent group.setParent(this); } // -- addGroup /** * Removes the given Group from this ContentModelGroup. * * @param group * the Group to remove. * @return true if the group has been successfully removed, false otherwise. */ public boolean removeGroup(final Group group) { boolean result = _contentModel.removeGroup(group); group.setParent(null); return result; } /** * Adds the given ModelGroup Definition to this {@link ContentModelGroup}. * * @param group * the ModelGroup to add * @exception SchemaException * when a group with the same name as the specified group * already exists in the current scope */ public void addGroup(final ModelGroup group) throws SchemaException { _contentModel.addGroup(group); // -- set reference to parent group.setParent(this); } // -- addGroup /** * Removes the given ModelGroup Definition from this ContentModelGroup. * * @param group * the ModelGroup Definition to remove. * @return true if the group has been successfully removed, false otherwise. */ public boolean removeGroup(final ModelGroup group) { boolean result = _contentModel.removeGroup(group); group.setParent(null); return result; } /** * Returns an enumeration of all the Particles of this ContentModelGroup. * * @return an enumeration of the Particles contained within this * ContentModelGroup */ public Enumeration enumerate() { return _contentModel.enumerate(); } // -- enumerate /** * Returns the element declaration with the given name, or null if no * element declaration with that name exists in this ContentModelGroup. * * @param name * the name of the element. * @return the ElementDecl with the given name, or null if no ElementDecl * exists in this ContentModelGroup. */ public ElementDecl getElementDecl(final String name) { ElementDecl result = _contentModel.getElementDecl(name); return result; } // -- getElementDecl /** * Returns the maximum number of occurances that this ContentModelGroup may * appear. * * @return the maximum number of occurances that this ContentModelGroup may * appear. A non positive (n < 1) value indicates that the value is * unspecified (ie. unbounded). */ public int getMaxOccurs() { if (_contentModel.getParticleCount() > 0) { Particle particle = _contentModel.getParticle(0); if (particle instanceof ContentModelGroup) { return particle.getMaxOccurs(); } } return _contentModel.getMaxOccurs(); } // -- getMaxOccurs /** * Returns the minimum number of occurances that this ContentModelGroup must * appear. * * @return the minimum number of occurances that this ContentModelGroup must * appear A negative (n < 0) value indicates that the value is * unspecified. */ public int getMinOccurs() { if (_contentModel.getParticleCount() > 0) { Particle particle = _contentModel.getParticle(0); if (particle instanceof ContentModelGroup) { return particle.getMinOccurs(); } } return _contentModel.getMinOccurs(); } // -- getMinOccurs /** * Returns the Particle at the specified index * * @param index * the index of the particle to return * @return the CMParticle at the specified index */ public Particle getParticle(final int index) { Particle result = _contentModel.getParticle(index); return result; } // -- getParticle /** * Returns the number of particles contained within this ContentModelGroup. * * @return the number of particles */ public int getParticleCount() { return _contentModel.getParticleCount(); } // -- getParticleCount // -------------------------------/ // - Implementation of Structure -/ // -------------------------------/ /** * Returns the type of this Schema Structure. * * @return the type of this Schema Structure */ public short getStructureType() { return Structure.COMPLEX_TYPE; } // -- getStructureType /** * Checks the validity of this ComplexType defintion. * * @throws ValidationException * when this ComplexType definition is invalid. */ public void validate() throws ValidationException { // -- check name if (_parent != null && _parent.getStructureType() != Structure.SCHEMA) { if (getName() != null) { String err = "Only top-level complexTypes can be named."; err += getName() + "is not a valid complexType."; throw new ValidationException(err); } } // -- check attributes _attributes.validate(); // -- check content model Enumeration enumeration = _contentModel.enumerate(); while (enumeration.hasMoreElements()) { ((Structure) enumeration.nextElement()).validate(); } // -- make sure baseType is accessible XMLType type = getBaseType(); if ((type == null) && (_baseType != null)) { String error = "The base type '" + _baseType + "' was not found."; throw new ValidationException(error); } if (type != null) { if (type.getStructureType() == Structure.SIMPLE_TYPE) { if (_restricted) { String name = getName(); if (name == null) { name = "anonymous-complexType-for-element: "; if (_parent != null) { // -- parent should be an element if name is null, // but // -- we'll check the type to be on the safe side if (_parent.getStructureType() == Structure.ELEMENT) { name += ((ElementDecl) _parent).getName(); } else { name += _parent.toString(); } } } String err = "complexType: " + name; err += "; A complex type cannot be a restriction" + " of a simpleType:"; err += type.getName(); throw new ValidationException(err); } } else if (type.getStructureType() == Structure.COMPLEX_TYPE) { if (!_complexContent) { // we are now sure that the base is a ComplexType // but is the base of this complexType a simpleType? (see // 4.3.3->simpleContent->content type) if (((ComplexType) type).getContentType().getType() != ContentType.SIMPLE) { String name = getName(); if (name == null) { name = "anonymous-complexType-for-element: "; if (_parent != null) { // -- parent should be an element if name is // null, but // -- we'll check the type to be on the safe // side if (_parent.getStructureType() == Structure.ELEMENT) { name += ((ElementDecl) _parent).getName(); } else { name += _parent.toString(); } } } String err = "complexType: " + name; err += "; When a complexType is a restriction of simpleContent the base type" + " must be a complexType whose base is also simpleContent."; throw new ValidationException(err); } } } } } // -- validate // ---------------------/ // - Protected Methods -/ // ---------------------/ /** * Sets the parent for this ComplexType. * * @param parent * the parent Structure for this ComplexType */ protected void setParent(final Structure parent) { if (parent != null) { switch (parent.getStructureType()) { case Structure.SCHEMA: case Structure.ELEMENT: break; default: String error = "Invalid parent for ComplexType"; throw new IllegalArgumentException(error); } } _parent = parent; } // -- setParent /** * @return true if the content model for this ComplexType is emptiable. */ public boolean isEmptiable() { switch (getParticleCount()) { case 0: return true; case 1: Particle p = getParticle(0); if (p.isEmptiable()) { if ((_baseType != null) && !isRestricted()) { // derived by extension XMLType baseType = getBaseType(); if ((baseType != null) && baseType.isComplexType() && ((ComplexType) baseType).isEmptiable()) { return true; } } else { // derived by restriction (explicit or shorthand from // xs:anyType return true; } } break; } return false; } } // -- Complextype