/** * 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-2003 (C) Intalio Inc. All Rights Reserved. * * $Id$ */ package org.exolab.castor.xml.schema; import java.util.Enumeration; import java.util.Iterator; import java.util.Vector; import org.exolab.castor.xml.ValidationException; import org.exolab.castor.xml.validators.ValidationUtils; /** * An XML Schema ElementDecl * @author <a href="mailto:kvisco@intalio.com">Keith Visco</a> * @version $Revision$ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr 2006) $ **/ public class ElementDecl extends Particle implements Referable { /** SerialVersionUID */ private static final long serialVersionUID = -7804351635137964219L; //-------------------/ //- Class Variables -/ //-------------------/ /** * Error message for a null argument **/ private static String NULL_ARGUMENT = "A null argument was passed to the constructor of " + ElementDecl.class.getName(); //--------------------/ //- Member Variables -/ //--------------------/ /** * The block attribute for this element definition. **/ private BlockList _block = null; /** * collection of Identity Constraints **/ private Vector _constraints = null; /** * The default value for this element definition. Only * useful for simpleContent. **/ private String _default = null; /** * The name of a reference to a top-level element declaration **/ private String _elementRefName = null; /** * The top-level element declaration this element reference points to **/ private ElementDecl _referencedElement = null; /** * The final value for this element definition. **/ private FinalList _final = null; /** * The fixed value for this element definition. Only * used for simpleContent. **/ private String _fixed = null; /** * The form type for this element definition. * Specifies whether names should be qualified or unqualified. * Uses the default Form from the parent Schema if unspecified. **/ private Form _form = null; /** * The unique ID for this element definition (optional). **/ private String _id = null; /** * Flag indicating whether or not this Element declaration is * abstract **/ private boolean _isAbstract = false; /** * The element name **/ private String _name = null; /** * Flag indicating whether or not the element value may be null. **/ private boolean _nillable = false; /** * The parent for this ElementDecl **/ private Structure _parent = null; /** * The parent schema that this element declaration belongs to **/ private Schema _schema = null; /** * The substitutionGroup for this element definition. **/ private String _substitutionGroup = null; /** * The XMLType for this element declaration **/ private XMLType _xmlType = null; /** * Creates a new default element definition * @param schema the XML Schema to which this element declaration * belongs * <BR />This element definition will not be valid until a name has * been set **/ public ElementDecl(Schema schema) { this(schema, null); } //-- ElementDecl /** * Creates a new default element definition * @param schema the XML Schema to which this Element Declaration * belongs * @param name the name of the Element being declared **/ public ElementDecl(Schema schema, String name) { super(); setName(name); if (schema == null) { String err = NULL_ARGUMENT + "; 'schema' must not be null."; throw new IllegalArgumentException(err); } setSchema(schema); _constraints = new Vector(3); } //-- ElementDecl /** * Adds the given IdentityConstraint to this element definition. * * @param constraint the IdentityConstraint to add. **/ public void addIdentityConstraint(IdentityConstraint constraint) { if (constraint == null) return; _constraints.addElement(constraint); } //-- addIdentityConstraint /** * Returns the value of the 'block' attribute for this element * * @return the value of the block attribute. **/ public BlockList getBlock() { return _block; } //-- getBlock /** * Returns the default value of this element definition. * * @return the default value of this element definition, * or null if no default was specified. **/ public String getDefaultValue() { if (isReference()) { ElementDecl elem = getReference(); if (elem != null) return elem.getDefaultValue(); } return _default; } //-- getDefaultValue /** * Returns the value of the 'final' attribute for this element * definition. * * @return the FinalList for this element definition. **/ public FinalList getFinal() { return _final; } //-- getFinal /** * Returns the fixed value of this element definition. * * @return the fixed value of this element definition, * or null if no default was specified. */ public String getFixedValue() { if (isReference()) { ElementDecl elem = getReference(); if (elem != null) return elem.getFixedValue(); } return _fixed; } //-- getFixedValue /** * Returns the Form for this element definition. The Form object species * whether or not names are qualified or unqualified in the scope of * this element definition. If null, the Form should be obtained from the * parent Schema. * * @return the Form for this element definition, or null if not set. **/ public Form getForm() { return _form; } //-- getForm /** * Returns the 'id' for this element definition. * * @return the 'id' for this element definition. **/ public String getId() { return _id; } //-- getId /** * Returns an Enumeration of IdentityConstraint objects contained within * this element definition. * * @return an Enumeration of IdentityConstraint objects contained within * this element definition. **/ public Enumeration getIdentityConstraints() { return _constraints.elements(); } //-- getIdentityConstraints /** * Returns the name of this Element declaration. The name of the * referenced element is returned if the 'ref' attribute was used. * The name returned will be an NCName (no namespace prefix will * be included with the name). * * @return the name of this element declaration */ public String getName() { return getName(false); } //-- getName /** * Returns the name of this Element declaration. The name * returned, if not null, will be an NCName. * * @param ignoreRef if false the name of the referenced * element (if specified) is returned, otherwise the * localname (may be null). * * @return the name of this element declaration **/ public String getName(boolean ignoreRef) { if (isReference() && ignoreRef == false) { String localName = _elementRefName; //-- check for namespace prefix int idx = localName.indexOf(':'); if (idx > 0) { localName = localName.substring(idx+1); } return localName; } return _name; } //-- getName /** * Returns the parent of this ElementDecl, this value may be null if * no parent has been set. * * @return the parent Structure of this ElementDecl **/ public Structure getParent() { return _parent; } //-- getParent /** * Returns the XMLType (ComplexType or SimpleType) of this ElementDecl. * @return the XMLType of this ElementDecl **/ public XMLType getType() { XMLType result = null; if (isReference()) { ElementDecl element = getReference(); if (element != null) { return element.getType(); } return null; } if (_xmlType == null) return null; //1--Anonymous types if (_xmlType.getName() == null) return _xmlType.getType(); //--we look in the parent schema if we have redefinitions of types. result = _xmlType.getType(); //-- the current XML schema might have a MasterSchema where all the //-- type definitions have a higher priority [this is useful when //-- resolving redefined types for instance]. if (result != null) { Schema tempSchema = result.getSchema().getMasterSchema(); if (tempSchema != null) { XMLType tempType = tempSchema.getType(result.getName()); if (tempType != null) { result = tempType; } } } return result; } //-- getXMLType /** * Returns the ElementDecl that this element definition references. * This will return null if this element definition does not reference * a different element definition. * @return the ElementDecl that this element definition references **/ public ElementDecl getReference() { if (_referencedElement != null) { return _referencedElement; } ElementDecl result = null; if (_elementRefName != null) { result = _schema.getElementDecl(_elementRefName); if (result == null) { String err = "Unable to find element referenced :\" "; err += getName(); err += "\""; throw new IllegalStateException(err); } _referencedElement = result; } return result; } //-- getReference /** * Returns the actual reference name of this AttributeDecl, or null * if this AttributeDecl is not a reference. The name returned, if not * null, will be a QName, possibly containing the namespace prefix. * * @return the reference name */ public String getReferenceName() { return _elementRefName; } //-- getReference /** * Returns the Id used to Refer to this Object * * @return the Id used to Refer to this Object * @see Referable **/ public String getReferenceId() { if (_name != null) return "element:"+_name; return null; } //-- getReferenceId /** * Returns the XML Schema to which this element declaration belongs. * @return the XML Schema to which this element declaration belongs. **/ public Schema getSchema() { return _schema; } //-- getSchema /** * Returns the substitutionGroup for this element declaration, or * null if it's absent; if this {@link ElementDecl} instance is a reference * to a global element definition, return its substitution group * * @return the substitutionGroup membership for this element * declaration, or null if absent. **/ public String getSubstitutionGroup() { if (isReference()) { return getReference().getSubstitutionGroup(); } return _substitutionGroup; } //-- getSubstitutionGroup /** * Returns an enumeration of the elements that can be substitute to * this element declaration. * @return an enumeration of the elements that can be substitute to * this element declaration. */ public Enumeration getSubstitutionGroupMembers() { Vector result = new Vector(); Iterator<ElementDecl> enumeration = _schema.getElementDecls().iterator(); while (enumeration.hasNext()) { ElementDecl temp = (ElementDecl)enumeration.next(); String subName = temp.getSubstitutionGroup(); if (subName != null) { // no namespace(s) or default namespace in use if (subName.equals(_name)) { result.add(temp); } // namespace(s) incl. prefix in use // TODO: find a better way of dealing with a namespace prefix else if (subName.endsWith(_name) && subName.indexOf(":") > 0) { result.add(temp); } } } return result.elements(); } /** * Returns true if this element definition is abstract * @return true if this element definition is abstract **/ public boolean isAbstract() { if (isReference()) { return _referencedElement.isAbstract(); } return _isAbstract; } //-- isAbstract /** * Returns whether or not instances of this element definition * may appear with no content. * * @return true if instances of this element definition * may appear with no content, otherwise false. **/ public boolean isNillable() { if (isReference()) { return _referencedElement.isNillable(); } return _nillable; } //-- isNullable /** * Returns true if this element definition simply references another * element Definition * @return true if this element definition is a reference **/ public boolean isReference() { return (_elementRefName != null); } //-- isReference /** * Sets whether or not this element definition is abstract * @param isAbstract a boolean when true indicates that this * element definition should be abstract **/ public void setAbstract(boolean isAbstract) { _isAbstract = isAbstract; } //-- isAbstract /** * Returns true if this element has children (i.e if it * holds attributes or elements). * @return true if this element has children (i.e if it * holds attributes or elements). */ public boolean hasChildren() { XMLType type = getType(); if (type instanceof SimpleType) return false; if (type instanceof ComplexType) { //complexContent ->sure to have children if (((ComplexType)type).isComplexContent()) return true; //else check for contentModel group else if ( ((ComplexType)type).getParticleCount() != 0 ) return true; //else check for attributes else { java.util.Enumeration temp = ((ComplexType)type).getAttributeDecls(); return temp.hasMoreElements(); } } return false; } //-- hasChildren /** * Removes the given IdentityConstraint from this element definition. * * @param constraint the IdentityConstraint to remove. * @return true if the IdentityConstraint was contained within this * element defintion. **/ public boolean removeIdentityConstraint(IdentityConstraint constraint) { if (constraint == null) return false; return _constraints.removeElement(constraint); } //-- removeIdentityConstraint /** * Sets the value of the 'block' attribute for this element * * @param block the value of the block attribute for this * element definition. **/ public void setBlock(BlockList block) { _block = block; } //-- setBlock /** * Sets the value of the 'block' attribute for this element * * @param block the value of the block attribute for this * element definition. **/ public void setBlock(String block) { if (block == null) _block = null; else _block = new BlockList(block); } //-- setBlock /** * Sets the default value for this element definition. * * @param value the default value for this element definition. **/ public void setDefaultValue(String value) { this._default = value; } //-- setDefaultValue /** * Sets the value of the 'final' attribute for this element * definition. * * @param finalList the value of the final attribute for this * element definition. **/ public void setFinal(FinalList finalList) { _final = finalList; } //-- setFinal /** * Sets the value of the 'final' attribute for this element * definition. * * @param finalValue the value of the final attribute for this * element definition. **/ public void setFinal(String finalValue) { if (finalValue == null) _final = null; else _final = new FinalList(finalValue); } //-- setFinal /** * Sets the fixed value for this element definition. * * @param value the fixed value for this element definition. **/ public void setFixedValue(String value) { this._fixed = value; } //-- setDefaultValue /** * Sets the Form for this element definition. The Form object species * whether or not names are qualified or unqualified in the scope of * this element definition. If null, the Form is to be obtained from the * parent Schema. * * @param form the Form type for this element definition. **/ public void setForm(Form form) { _form = form; } //-- setForm /** * Sets the Id for this element definition. * * @param id the Id for this element definition. **/ public void setId(String id) { _id = id; } //-- setId /** * Sets the name of the element that this Element definition defines. * * @param name the name of the defined element **/ public void setName(String name) { if ((name == null) || (ValidationUtils.isNCName(name))) { _name = name; } else { String err = "error: '" + name + "' is not a valid NCName."; throw new IllegalArgumentException(err); } } //-- setName /** * Sets whether or not instances of this element definition may * contain empty content * * @param nillable the flag when true indicates that instances * of this element definition may appear with empty content **/ public void setNillable(boolean nillable) { _nillable = nillable; } //-- setNillable /** * Sets the parent for this ElementDecl. * * @param parent the parent Structure for this ElementDecl **/ protected void setParent(Structure parent) { if (parent != null) { switch (parent.getStructureType()) { case Structure.GROUP: case Structure.MODELGROUP: case Structure.SCHEMA: break; default: String error = "Invalid parent for element."; throw new IllegalArgumentException(error); } } _parent = parent; } //-- setParent /** * Sets the reference for this element definition * @param reference the Element definition that this definition references **/ public void setReference(ElementDecl reference) { if (reference == null) { _elementRefName = null; _referencedElement = null; } else { if (reference.getSchema() == this.getSchema()) { _elementRefName = reference.getName(); _referencedElement = reference; } else { String qName = reference.getName(); String nsURI = reference.getSchema().getTargetNamespace(); if (nsURI != null) { String prefix = getSchema().getNamespacePrefix(nsURI); if ((prefix != null) && (prefix.length() > 0)) qName = prefix + ":" + qName; } _elementRefName = qName; _referencedElement = reference; } } } //-- setReference /** * Sets the name which this element declaration refers to * @param referenceName the name of the element definition that this * definition references **/ public void setReferenceName(String referenceName) { if ((referenceName == null) || (ValidationUtils.isQName(referenceName))) { _elementRefName = referenceName; } else { String err = "error: '" + referenceName + "' is not a valid QName."; throw new IllegalArgumentException(err); } } //-- setReference /** * Sets the substitutionGroup for this element definition. * * @param substitutionGroup the substitutionGroup for this * element definition. **/ public void setSubstitutionGroup(String substitutionGroup) { _substitutionGroup = substitutionGroup; } //-- setSubstitutionGroup /** * Sets the XMLType for this Element declaration. * @param type the XMLType for this element declaration. * <BR /> * <B>Note:</B> This method is mutually exclusive with * #setTypeReference, if a reference has previously been * set it will be ignored. **/ public void setType(XMLType type) { //-- reset parent of current type if (_xmlType != null) { _xmlType.setParent(null); } if (type != null) { type.setParent(this); } _xmlType = type; } //-- setType /** * Sets the type of this element to be a reference. */ public void setTypeReference(String name) { TypeReference reference= new TypeReference(); reference.setName(name); reference.setSchema(_schema); setType(reference); } //-------------------------------/ //- Implementation of Structure -/ //-------------------------------/ /** * Returns the type of this Schema Structure * @return the type of this Schema Structure **/ public short getStructureType() { return Structure.ELEMENT; } //-- getStructureType /** * Checks the validity of this element definition. * * @throws ValidationException when this element definition * is invalid. **/ public void validate() throws ValidationException { //-- If this element merely references another element definition //-- just check that we can resolve the reference if (_elementRefName != null) { if (_schema.getElementDecl(_elementRefName) == null) { String err = "<element ref=\"" + _elementRefName + "\"> "+ "is not resolvable."; throw new ValidationException(err); } return; } if (_name == null) { String err = "<element> is missing required 'name' or " + "'ref' attribute."; throw new ValidationException(err); } //--check that the particle information is not present on top level //--element //--do you really allow parent to be null??? if (getParent() != null) { if (getParent().getStructureType() == Structure.SCHEMA) { if (isMinOccursSet()) { String err = "'minOccurs' declaration is prohibited on top level element '/" + getName() + "'."; throw new ValidationException(err); } if (isMaxOccursSet()) { String err = "'maxOccurs' declaration is prohibited on top level element '/" + getName() + "'."; throw new ValidationException(err); } } } //-- If type is anonymous, make sure the type is valid. //-- To prevent excess validation, we ONLY validate //-- if the type is anonymous, because otherwise //-- the Schema itself will validate the type. XMLType type = getType(); if (type != null) { if (type.isComplexType()) { ComplexType complexType = (ComplexType)type; if (!complexType.isTopLevel()) { complexType.validate(); } } else if (type.isSimpleType()) { SimpleType simpleType = (SimpleType)type; if (simpleType.getParent() != simpleType.getSchema()) { simpleType.validate(); } //-- print warning message if ID, IDREF, IDREFS, NMTOKEN, NTOKENS are //-- used as element type int typeCode = simpleType.getTypeCode(); switch (typeCode) { case SimpleTypesFactory.ID_TYPE: case SimpleTypesFactory.IDREF_TYPE: case SimpleTypesFactory.IDREFS_TYPE: case SimpleTypesFactory.NMTOKENS_TYPE: case SimpleTypesFactory.NMTOKEN_TYPE: String err = "Warning : For XML Compatibility " + simpleType.getName()+" should be used only as attributes\n."; //--Future versions will log the message System.out.println(err); break; default: break; } } //-- anyType else { //-- nothing to validate anyType is always valid. } } } //-- validate /** * Sets the XMl schema to where this element has been defined * @param schema The defining XML schema */ private void setSchema(final Schema schema) { _schema = schema; } /** * Indicates whether a type is set for this element definiion. * @return True if a type is set. */ public boolean hasXMLType() { return (_xmlType != null); } } //-- Element