/**
* 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, 2000 (C) Intalio, Inc. All Rights Reserved.
*
* $Id$
*/
package org.exolab.castor.xml.schema;
import java.util.Enumeration;
import org.exolab.castor.xml.ValidationException;
/**
* An XML Schema Group
* @author <a href="mailto:kvisco@intalio.com">Keith Visco</a>
* @version $Revision$ $Date: 2006-04-14 04:14:43 -0600 (Fri, 14 Apr 2006) $
**/
public class Group extends Particle implements ContentModelGroup, Referable {
/** SerialVersionUID */
private static final long serialVersionUID = 3133443973681261845L;
/**
* the implementation of ContentModelGroup.
**/
private ContentModelGroup _contentModel = null;
/**
* The name of this Group.
**/
private String _name = null;
/**
* The Compositor for the Group.
**/
private Order _order = Order.sequence;
/**
* ID of this Group (if present at all).
*/
private String _id = null;
/**
* True if was created for a group tag, false otherwise (all, choice,
* sequence).
*/
private boolean _isModelGroupDefinition = false;
/**
* The parent for this Group (either another Group or a ComplexType).
**/
private Structure _parent = null;
/**
* Creates a new {@link Group}, with no name.
**/
public Group() {
this(null);
}
/**
* Creates a new {@link Group} with the given name.
* @param name of the {@link Group}
**/
public Group(final String name) {
super();
this._name = name;
_contentModel = new ContentModelGroupImpl();
}
/**
* {@inheritDoc}
*
* @see org.exolab.castor.xml.schema.ContentModelGroup#addWildcard(org.exolab.castor.xml.schema.Wildcard)
*/
public void addWildcard(final Wildcard wildcard) throws SchemaException {
if (wildcard.isAttributeWildcard()) {
throw new SchemaException("only <any> should be add in a group.");
}
_contentModel.addWildcard(wildcard);
}
/**
* {@inheritDoc}
*
* @see org.exolab.castor.xml.schema.ContentModelGroup#removeWildcard(org.exolab.castor.xml.schema.Wildcard)
*/
public boolean removeWildcard(final Wildcard wildcard) {
if (wildcard == null) {
return false;
}
return _contentModel.removeWildcard(wildcard);
}
/**
* Returns the {@link ContentModelGroup} for this group.
* Only used for a <group/> element
* @return the ContentModelGroup for this group
*/
public ContentModelGroup getContentModelGroup() {
return _contentModel;
}
/**
* Returns the ID for this {@link Group}.
* @return the ID for this {@link Group}, or null if no ID is present
**/
public String getId() {
return _id;
}
/**
* Returns the name of this {@link Group}, or null if no name was defined.
* @return the name of this {@link Group}, or null if no name was defined
**/
public String getName() {
return _name;
}
/**
* Returns the compositor for this {@link Group}.
* @return the compositor for this {@link Group}
**/
public Order getOrder() {
//-- Return proper compositor...
//-- according to XML Schema spec 20000407 section 4.3.5
//-- Note: it's important not to simply call
//-- #getParticleCount or #getParticle because those
//-- methods also perform some trickery
if (_contentModel.getParticleCount() == 1) {
Particle particle = _contentModel.getParticle(0);
if (particle.getStructureType() == Structure.GROUP) {
if ((getMinOccurs() == 1) && (getMaxOccurs() == 1)) {
return ((Group) particle).getOrder();
}
}
}
return this._order;
}
/**
* Returns the parent of this Group, this value may be null if
* no parent has been set.
*
* @return the parent Structure of this Group.
**/
public Structure getParent() {
return _parent;
}
/**
* Sets if the group is a model group definition.
* @deprecated Since Castor 0.9.2, to handle properly the <group/>
* element the class ModelGroup has been created
* @see ModelGroup
*/
public void setIsModelGroupDefinition(final boolean isModelGroupDefinition) {
_isModelGroupDefinition = isModelGroupDefinition;
}
/**
* Tells if the group is a model group definition.
* @return true if the group is a model group definition (<group/> tag), false
* otherwise {@literal <all/>}, <choice/>, or <sequence/> tags.
* @deprecated Since Castor 0.9.2, to handle properly the <group/>
* element the class {@link ModelGroup} has been created
* @see ModelGroup
*/
public boolean isModelGroupDefinition() {
return _isModelGroupDefinition;
}
/**
* 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 "group:" + _name;
}
return null;
}
/**
* Sets the name of this {@link Group}.
* @param name the new name for this {@link Group}
**/
public void setName(final String name) {
this._name = name;
}
/**
* Sets the ID for this {@link Group}.
* @param id the ID for this {@link Group}
**/
public void setId(final String id) {
_id = id;
}
/**
* Sets the {@link Order} for this {@link Group}.
* @param order the type of {@link Order} that this {@link Group} is restricted to
**/
public void setOrder(final Order order) {
if (order == null) {
this._order = Order.all;
} else {
this._order = order;
}
}
//---------------------------------------/
//- Implementation of ContentModelGroup -/
//---------------------------------------/
/**
* {@inheritDoc}
*
* @see org.exolab.castor.xml.schema.ContentModelGroup#addElementDecl(org.exolab.castor.xml.schema.ElementDecl)
*/
public void addElementDecl(final ElementDecl elementDecl)
throws SchemaException {
_contentModel.addElementDecl(elementDecl);
// --set the parent
elementDecl.setParent(this);
}
/**
* {@inheritDoc}
*
* @see org.exolab.castor.xml.schema.ContentModelGroup#removeElementDecl(org.exolab.castor.xml.schema.ElementDecl)
*/
public boolean removeElementDecl(final ElementDecl element) {
return _contentModel.removeElementDecl(element);
}
/**
* {@inheritDoc}
*
* @see org.exolab.castor.xml.schema.ContentModelGroup#addGroup(org.exolab.castor.xml.schema.Group)
*/
public void addGroup(final Group group) throws SchemaException {
_contentModel.addGroup(group);
// -- set reference to parent
group.setParent(this);
}
/**
* {@inheritDoc}
*
* @see org.exolab.castor.xml.schema.ContentModelGroup#removeGroup(org.exolab.castor.xml.schema.Group)
*/
public boolean removeGroup(final Group group){
boolean result = _contentModel.removeGroup(group);
group.setParent(null);
return result;
}
/**
* {@inheritDoc}
*
* @see org.exolab.castor.xml.schema.ContentModelGroup#addGroup(org.exolab.castor.xml.schema.ModelGroup)
*/
public void addGroup(final ModelGroup group) throws SchemaException {
_contentModel.addGroup(group);
// -- set reference to parent
group.setParent(this);
}
/**
* {@inheritDoc}
*
* @see org.exolab.castor.xml.schema.ContentModelGroup#removeGroup(org.exolab.castor.xml.schema.ModelGroup)
*/
public boolean removeGroup(final ModelGroup group) {
boolean result = _contentModel.removeGroup(group);
group.setParent(null);
return result;
}
/**
* {@inheritDoc}
* @see org.exolab.castor.xml.schema.ContentModelGroup#enumerate()
*/
public Enumeration<Annotated> enumerate() {
// -- Some trickery to properly handle
// -- XML Schema spec 20000407 section 4.3.5
if (_contentModel.getParticleCount() == 1) {
Particle particle = _contentModel.getParticle(0);
if (particle.getStructureType() == Structure.GROUP) {
Group temp = (Group) particle;
if (((getMinOccurs() == 1) && (getMaxOccurs() == 1))
&& ((temp.getMinOccurs() == 1) && (temp.getMaxOccurs() == 1))) {
return temp.enumerate();
}
}
}
return _contentModel.enumerate();
} //-- enumerate
/**
* {@inheritDoc}
* @see org.exolab.castor.xml.schema.ContentModelGroup#getElementDecl(java.lang.String)
*/
public ElementDecl getElementDecl(final String name) {
return _contentModel.getElementDecl(name);
}
/**
* {@inheritDoc}
* @see org.exolab.castor.xml.schema.ContentModelGroup#getParticle(int)
*/
public Particle getParticle(final int index) {
// -- Some trickery to properly handle
// -- XML Schema spec 20000407 section 4.3.5
if (_contentModel.getParticleCount() == 1) {
Particle particle = _contentModel.getParticle(0);
if (particle.getStructureType() == Structure.GROUP) {
if ((getMinOccurs() == 1) && (getMaxOccurs() == 1)) {
return ((Group) particle).getParticle(index);
}
}
}
return _contentModel.getParticle(index);
}
/**
* {@inheritDoc}
*
* @see org.exolab.castor.xml.schema.ContentModelGroup#getParticleCount()
*/
public int getParticleCount() {
// -- Some trickery to properly handle
// -- XML Schema spec 20000407 section 4.3.5
if (_contentModel.getParticleCount() == 1) {
Particle particle = _contentModel.getParticle(0);
if (particle.getStructureType() == Structure.GROUP) {
if ((getMinOccurs() == 1) && (getMaxOccurs() == 1)) {
return ((Group) particle).getParticleCount();
}
}
}
return _contentModel.getParticleCount();
}
//-------------------------------/
//- Implementation of Structure -/
//-------------------------------/
/**
* {@inheritDoc}
*
* @see org.exolab.castor.xml.schema.Structure#getStructureType()
*/
public short getStructureType() {
return Structure.GROUP;
}
/**
* A helper method that returns true if this group
* contains an {@literal <any>} element.
* @return method that returns true if this group
* contains an {@literal <any>} element.
*/
public boolean hasAny() {
boolean result = false;
Enumeration<Structure> enumeration = _contentModel.enumerate();
while (enumeration.hasMoreElements() && !result) {
Structure struct = 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;
}
/**
* Checks the validity of this {@link Group} defintion.
*
* @throws ValidationException when this {@link Group} definition
* is invalid.
**/
public void validate() throws ValidationException {
if (_order == Order.all) {
if (getMaxOccurs() != 1) {
String err = "Wrong maxOccurs value for a <all>:" + getMaxOccurs();
err += "\n1 is the only possible value.";
throw new ValidationException(err);
}
if (getMinOccurs() > 1) {
String err = "Wrong minOccurs value for a <all>:" + getMinOccurs();
err += "\n0 or 1 are the only possible values.";
throw new ValidationException(err);
}
}
Enumeration<Structure> enumeration = _contentModel.enumerate();
while (enumeration.hasMoreElements()) {
enumeration.nextElement().validate();
}
}
/**
* Sets the parent for this {@link Group}.
*
* @param parent the parent {@link Structure} for this {@link Group}
**/
protected void setParent(final Structure parent) {
if (parent != null) {
switch (parent.getStructureType()) {
case Structure.COMPLEX_TYPE:
case Structure.GROUP:
case Structure.MODELGROUP:
case Structure.SCHEMA:
break;
default:
String error = "Invalid parent for group";
throw new IllegalArgumentException(error);
}
}
_parent = parent;
}
/**
* Indicates whether this {@link Particle} is 'emptiable'
* @return true if this Particle is 'emptiable'
*/
public boolean isEmptiable() {
if (getMinOccurs() == 0) {
return true;
}
boolean result = false;
switch (this.getOrder()) {
case choice:
{
result = false;
Enumeration<Annotated> enumerate = this.enumerate();
while (enumerate.hasMoreElements()) {
Particle p = (Particle) enumerate.nextElement();
if (p.isEmptiable()) {
result = true;
break;
}
}
}
break;
case all:
case sequence:
{
result = true;
Enumeration<Annotated> enumerate = this.enumerate();
while (enumerate.hasMoreElements()) {
Particle p = (Particle) enumerate.nextElement();
if (!p.isEmptiable()) {
result = false;
break;
}
}
}
break;
}
return result;
}
}