blob: caf6cebbe4d84591766ffcc1fe820b7d27cc871f [file] [log] [blame]
/* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.xmlbeans.impl.schema;
import org.apache.xmlbeans.SchemaType;
import org.apache.xmlbeans.SchemaParticle;
import org.apache.xmlbeans.XmlError;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.SchemaLocalElement;
import org.apache.xmlbeans.SchemaIdentityConstraint;
import org.apache.xmlbeans.SchemaAttributeModel;
import org.apache.xmlbeans.SchemaLocalAttribute;
import org.apache.xmlbeans.SchemaGlobalElement;
import org.apache.xmlbeans.XmlID;
import org.apache.xmlbeans.XmlAnySimpleType;
import org.apache.xmlbeans.XmlErrorCodes;
import org.apache.xmlbeans.XmlNOTATION;
import org.apache.xmlbeans.XmlString;
import org.apache.xmlbeans.impl.common.XBeanDebug;
import org.apache.xmlbeans.impl.common.QNameHelper;
import javax.xml.namespace.QName;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.math.BigInteger;
public class StscChecker
{
public static void checkAll()
{
// walk the tree of types
StscState state = StscState.get();
List allSeenTypes = new ArrayList();
allSeenTypes.addAll(Arrays.asList(state.documentTypes()));
allSeenTypes.addAll(Arrays.asList(state.attributeTypes()));
allSeenTypes.addAll(Arrays.asList(state.redefinedGlobalTypes()));
allSeenTypes.addAll(Arrays.asList(state.globalTypes()));
for (int i = 0; i < allSeenTypes.size(); i++)
{
SchemaType gType = (SchemaType)allSeenTypes.get(i);
if (!state.noPvr() && // option to turn off particle restriction checking
!gType.isDocumentType()) // Don't check doc types for restriction.
{
checkRestriction((SchemaTypeImpl)gType);
}
checkFields((SchemaTypeImpl)gType);
allSeenTypes.addAll(Arrays.asList(gType.getAnonymousTypes()));
}
checkSubstitutionGroups(state.globalElements());
}
/**
* The following code checks rule #5 of http://www.w3.org/TR/xmlschema-1/#coss-ct
* as well as attribute + element default/fixed validity. <p/>
* Checks that xs:NOTATION is not used directly
*/
public static void checkFields(SchemaTypeImpl sType)
{
if (sType.isSimpleType())
return;
XmlObject location = sType.getParseObject();
SchemaAttributeModel sAttrModel = sType.getAttributeModel();
if (sAttrModel != null)
{
SchemaLocalAttribute[] sAttrs = sAttrModel.getAttributes();
QName idAttr = null;
for (int i = 0; i < sAttrs.length; i++)
{
XmlObject attrLocation = ((SchemaLocalAttributeImpl)sAttrs[i])._parseObject;
if (XmlID.type.isAssignableFrom(sAttrs[i].getType()))
{
if (idAttr == null)
{
idAttr = sAttrs[i].getName();
}
else
{
StscState.get().error(XmlErrorCodes.ATTR_GROUP_PROPERTIES$TWO_IDS,
new Object[]{ QNameHelper.pretty(idAttr), sAttrs[i].getName() },
attrLocation != null ? attrLocation : location);
}
if (sAttrs[i].getDefaultText() != null)
{
StscState.get().error(XmlErrorCodes.ATTR_PROPERTIES$ID_FIXED_OR_DEFAULT,
null, attrLocation != null ? attrLocation : location);
}
}
else if (XmlNOTATION.type.isAssignableFrom(sAttrs[i].getType()))
{
if (sAttrs[i].getType().getBuiltinTypeCode() == SchemaType.BTC_NOTATION)
{
StscState.get().recover(XmlErrorCodes.ATTR_NOTATION_TYPE_FORBIDDEN,
new Object[]{ QNameHelper.pretty(sAttrs[i].getName()) },
attrLocation != null ? attrLocation : location);
}
else
{
if (sAttrs[i].getType().getSimpleVariety() == SchemaType.UNION)
{
SchemaType[] members = sAttrs[i].getType().getUnionConstituentTypes();
for (int j = 0; j < members.length; j++)
if (members[j].getBuiltinTypeCode() == SchemaType.BTC_NOTATION)
StscState.get().recover(XmlErrorCodes.ATTR_NOTATION_TYPE_FORBIDDEN,
new Object[]{ QNameHelper.pretty(sAttrs[i].getName()) },
attrLocation != null ? attrLocation : location);
}
// Check that the Schema in which this is present doesn't have a targetNS
boolean hasNS;
if (sType.isAttributeType())
hasNS = sAttrs[i].getName().getNamespaceURI().length() > 0;
else
{
SchemaType t = sType;
while (t.getOuterType() != null)
t = t.getOuterType();
if (t.isDocumentType())
hasNS = t.getDocumentElementName().getNamespaceURI().length() > 0;
else hasNS = t.getName().getNamespaceURI().length() > 0;
}
if (hasNS)
StscState.get().warning(XmlErrorCodes.ATTR_COMPATIBILITY_TARGETNS,
new Object[] {QNameHelper.pretty(sAttrs[i].getName()) },
attrLocation != null ? attrLocation : location);
}
}
else
{
String valueConstraint = sAttrs[i].getDefaultText();
if (valueConstraint != null)
{
try
{
XmlAnySimpleType val = sAttrs[i].getDefaultValue();
if (!val.validate())
throw new Exception();
SchemaPropertyImpl sProp = (SchemaPropertyImpl)sType.getAttributeProperty(sAttrs[i].getName());
if (sProp != null && sProp.getDefaultText() != null)
{
sProp.setDefaultValue(new XmlValueRef(val));
}
}
catch (Exception e)
{
// move to 'fixed' or 'default' attribute on the attribute definition
String constraintName = (sAttrs[i].isFixed() ? "fixed" : "default");
XmlObject constraintLocation = location;
if (attrLocation != null)
{
constraintLocation = attrLocation.selectAttribute("", constraintName);
if (constraintLocation == null)
constraintLocation = attrLocation;
}
StscState.get().error(XmlErrorCodes.ATTR_PROPERTIES$CONSTRAINT_VALID,
new Object[] { QNameHelper.pretty(sAttrs[i].getName()),
constraintName,
valueConstraint,
QNameHelper.pretty(sAttrs[i].getType().getName()) },
constraintLocation);
}
}
}
}
}
checkElementDefaults(sType.getContentModel(), location, sType);
}
/**
* Checks the default values of elements.<p/>
* Also checks that the type of elements is not one of ID, IDREF, IDREFS, ENTITY, ENTITIES or
* NOTATION as per XMLSchema part 2.
* @param model
* @param location
* @param parentType
*/
private static void checkElementDefaults(SchemaParticle model, XmlObject location, SchemaType parentType)
{
if (model == null)
return;
switch (model.getParticleType())
{
case SchemaParticle.SEQUENCE:
case SchemaParticle.CHOICE:
case SchemaParticle.ALL:
SchemaParticle[] children = model.getParticleChildren();
for (int i = 0; i < children.length; i++)
{
checkElementDefaults(children[i], location, parentType);
}
break;
case SchemaParticle.ELEMENT:
String valueConstraint = model.getDefaultText();
if (valueConstraint != null)
{
if (model.getType().isSimpleType() || model.getType().getContentType() == SchemaType.SIMPLE_CONTENT)
{
try
{
XmlAnySimpleType val = model.getDefaultValue();
XmlOptions opt = new XmlOptions();
opt.put(XmlOptions.VALIDATE_TEXT_ONLY);
if (!val.validate(opt))
throw new Exception();
SchemaPropertyImpl sProp = (SchemaPropertyImpl)parentType.getElementProperty(model.getName());
if (sProp != null && sProp.getDefaultText() != null)
{
sProp.setDefaultValue(new XmlValueRef(val));
}
}
catch (Exception e)
{
// move to 'fixed' or 'default' attribute on the element definition
String constraintName = (model.isFixed() ? "fixed" : "default");
XmlObject constraintLocation = location.selectAttribute("", constraintName);
StscState.get().error(XmlErrorCodes.ELEM_PROPERTIES$CONSTRAINT_VALID,
new Object[] { QNameHelper.pretty(model.getName()),
constraintName,
valueConstraint,
QNameHelper.pretty(model.getType().getName()) },
(constraintLocation==null ? location : constraintLocation));
}
}
else if (model.getType().getContentType() == SchemaType.MIXED_CONTENT)
{
if (!model.getType().getContentModel().isSkippable())
{
String constraintName = (model.isFixed() ? "fixed" : "default");
XmlObject constraintLocation = location.selectAttribute("", constraintName);
StscState.get().error(XmlErrorCodes.ELEM_DEFAULT_VALID$MIXED_AND_EMPTIABLE,
new Object[] { QNameHelper.pretty(model.getName()),
constraintName,
valueConstraint },
(constraintLocation==null ? location : constraintLocation));
}
else
{
// Element Default Valid (Immediate): cos-valid-default.2.2.2
// no need to validate the value; type is a xs:string
SchemaPropertyImpl sProp = (SchemaPropertyImpl)parentType.getElementProperty(model.getName());
if (sProp != null && sProp.getDefaultText() != null)
{
sProp.setDefaultValue(new XmlValueRef(XmlString.type.newValue(valueConstraint)));
}
}
}
else if (model.getType().getContentType() == SchemaType.ELEMENT_CONTENT)
{
XmlObject constraintLocation = location.selectAttribute("", "default");
StscState.get().error(XmlErrorCodes.ELEM_DEFAULT_VALID$SIMPLE_TYPE_OR_MIXED,
new Object[] { QNameHelper.pretty(model.getName()),
valueConstraint,
"element" },
(constraintLocation==null ? location : constraintLocation));
}
else if (model.getType().getContentType() == SchemaType.EMPTY_CONTENT)
{
XmlObject constraintLocation = location.selectAttribute("", "default");
StscState.get().error(XmlErrorCodes.ELEM_DEFAULT_VALID$SIMPLE_TYPE_OR_MIXED,
new Object[] { QNameHelper.pretty(model.getName()),
valueConstraint,
"empty" },
(constraintLocation==null ? location : constraintLocation));
}
}
// Checks if the type is one of the "attribute-specific" types
String warningType = null;
if (BuiltinSchemaTypeSystem.ST_ID.isAssignableFrom(model.getType()))
warningType = BuiltinSchemaTypeSystem.ST_ID.getName().getLocalPart();
else if (BuiltinSchemaTypeSystem.ST_IDREF.isAssignableFrom(model.getType()))
warningType = BuiltinSchemaTypeSystem.ST_IDREF.getName().getLocalPart();
else if (BuiltinSchemaTypeSystem.ST_IDREFS.isAssignableFrom(model.getType()))
warningType = BuiltinSchemaTypeSystem.ST_IDREFS.getName().getLocalPart();
else if (BuiltinSchemaTypeSystem.ST_ENTITY.isAssignableFrom(model.getType()))
warningType = BuiltinSchemaTypeSystem.ST_ENTITY.getName().getLocalPart();
else if (BuiltinSchemaTypeSystem.ST_ENTITIES.isAssignableFrom(model.getType()))
warningType = BuiltinSchemaTypeSystem.ST_ENTITIES.getName().getLocalPart();
else if (BuiltinSchemaTypeSystem.ST_NOTATION.isAssignableFrom(model.getType()))
{
if (model.getType().getBuiltinTypeCode() == SchemaType.BTC_NOTATION)
{
StscState.get().recover(XmlErrorCodes.ELEM_NOTATION_TYPE_FORBIDDEN,
new Object[]{ QNameHelper.pretty(model.getName()) },
((SchemaLocalElementImpl) model)._parseObject == null ? location :
((SchemaLocalElementImpl) model)._parseObject.selectAttribute("", "type"));
}
else
{
if (model.getType().getSimpleVariety() == SchemaType.UNION)
{
SchemaType[] members = model.getType().getUnionConstituentTypes();
for (int i = 0; i < members.length; i++)
if (members[i].getBuiltinTypeCode() == SchemaType.BTC_NOTATION)
StscState.get().recover(XmlErrorCodes.ELEM_NOTATION_TYPE_FORBIDDEN,
new Object[]{ QNameHelper.pretty(model.getName()) },
((SchemaLocalElementImpl) model)._parseObject == null ? location :
((SchemaLocalElementImpl) model)._parseObject.selectAttribute("", "type"));
}
warningType = BuiltinSchemaTypeSystem.ST_NOTATION.getName().getLocalPart();
}
// Check that the Schema in which this is present doesn't have a targetNS
boolean hasNS;
SchemaType t = parentType;
while (t.getOuterType() != null)
t = t.getOuterType();
if (t.isDocumentType())
hasNS = t.getDocumentElementName().getNamespaceURI().length() > 0;
else
hasNS = t.getName().getNamespaceURI().length() > 0;
if (hasNS)
StscState.get().warning(XmlErrorCodes.ELEM_COMPATIBILITY_TARGETNS,
new Object[] {QNameHelper.pretty(model.getName()) },
((SchemaLocalElementImpl) model)._parseObject == null ? location :
((SchemaLocalElementImpl) model)._parseObject);
}
if (warningType != null)
StscState.get().warning(XmlErrorCodes.ELEM_COMPATIBILITY_TYPE, new Object[]
{ QNameHelper.pretty(model.getName()), warningType },
((SchemaLocalElementImpl) model)._parseObject == null ? location :
((SchemaLocalElementImpl) model)._parseObject.selectAttribute("", "type"));
break;
default:
// nothing to do.
break;
}
}
/**
* The following code only checks rule #5 of http://www.w3.org/TR/xmlschema-1/#derivation-ok-restriction
* (Everything else can and should be done in StscResolver, because we can give more detailed line # info there
*/
public static boolean checkRestriction(SchemaTypeImpl sType)
{
if (sType.getDerivationType() == SchemaType.DT_RESTRICTION && !sType.isSimpleType())
{
StscState state = StscState.get();
// we don't remember very precise line number information, but it's better than nothin.
XmlObject location = sType.getParseObject();
SchemaType baseType = sType.getBaseType();
if (baseType.isSimpleType())
{
state.error(XmlErrorCodes.SCHEMA_COMPLEX_TYPE$COMPLEX_CONTENT,
new Object[] { QNameHelper.pretty(baseType.getName()) },
location);
return false;
}
// 5 The appropriate case among the following must be true:
switch (sType.getContentType())
{
case SchemaType.SIMPLE_CONTENT:
// 5.1 If the {content type} of the complex type definition is a simple type definition, then one of the following must be true:
switch (baseType.getContentType())
{
case SchemaType.SIMPLE_CONTENT:
// 5.1.1 The {content type} of the {base type definition} must be a simple type definition of which the {content type} is a �valid restriction� as defined in Derivation Valid (Restriction, Simple) (�3.14.6).
SchemaType cType = sType.getContentBasedOnType();
if (cType != baseType)
{
// We have to check that the contentType is legally derived
// from the base simple type in the hierarchy
SchemaType bType = baseType;
while (bType != null && !bType.isSimpleType())
bType = bType.getContentBasedOnType();
if (bType != null && !bType.isAssignableFrom(cType))
{
state.error(XmlErrorCodes.COMPLEX_TYPE_RESTRICTION$SC_NOT_DERIVED,
null, location);
return false;
}
}
break;
case SchemaType.MIXED_CONTENT:
// 5.1.2 The {base type definition} must be mixed and have a particle which is �emptiable� as defined in Particle Emptiable (�3.9.6).
if (baseType.getContentModel() != null && !baseType.getContentModel().isSkippable())
{
state.error(XmlErrorCodes.COMPLEX_TYPE_RESTRICTION$SC_AND_MIXED_EMPTIABLE,
null, location);
return false;
}
break;
default:
state.error(XmlErrorCodes.COMPLEX_TYPE_RESTRICTION$SC_AND_SIMPLE_TYPE_OR_MIXED,
null, location);
return false;
}
break;
case SchemaType.EMPTY_CONTENT:
// 5.2 If the {content type} of the complex type itself is empty , then one of the following must be true:
switch (baseType.getContentType())
{
case SchemaType.EMPTY_CONTENT:
// 5.2.1 The {content type} of the {base type definition} must also be empty.
break;
case SchemaType.MIXED_CONTENT:
case SchemaType.ELEMENT_CONTENT:
// 5.2.2 The {content type} of the {base type definition} must be elementOnly or mixed and have a particle which is �emptiable� as defined in Particle Emptiable (�3.9.6).
if (baseType.getContentModel() != null && !baseType.getContentModel().isSkippable())
{
state.error(XmlErrorCodes.COMPLEX_TYPE_RESTRICTION$EMPTY_AND_ELEMENT_OR_MIXED_EMPTIABLE,
null, location);
return false;
}
break;
default:
state.error(XmlErrorCodes.COMPLEX_TYPE_RESTRICTION$EMPTY_AND_NOT_SIMPLE,
null, location);
return false;
}
break;
case SchemaType.MIXED_CONTENT:
// 5.3 If the {content type} of the {base type definition} is mixed...
if (baseType.getContentType() != SchemaType.MIXED_CONTENT)
{
state.error(XmlErrorCodes.COMPLEX_TYPE_RESTRICTION$ELEMENT_OR_MIXED_AND_MIXED,
null, location);
return false;
}
// FALLTHROUGH
case SchemaType.ELEMENT_CONTENT:
// 5.3 ... or the {content type} of the complex type definition itself is element-only,...
if (baseType.getContentType() == SchemaType.EMPTY_CONTENT)
{
state.error(XmlErrorCodes.COMPLEX_TYPE_RESTRICTION$ELEMENT_OR_MIXED_AND_EMPTY,
null, location);
return false;
}
if (baseType.getContentType() == SchemaType.SIMPLE_CONTENT)
{
state.error(XmlErrorCodes.COMPLEX_TYPE_RESTRICTION$ELEMENT_OR_MIXED_AND_SIMPLE,
null, location);
return false;
}
// 5.3 ... then the particle of the complex type definition itself must be a �valid restriction� of the particle of the {content type} of the {base type definition}
SchemaParticle baseModel = baseType.getContentModel();
SchemaParticle derivedModel = sType.getContentModel();
if ( derivedModel == null && sType.getDerivationType()==SchemaType.DT_RESTRICTION )
{
// it is ok to have an empty contentModel if it's a restriction
// see Particle Valid (Restriction) (3.9.6) all three bulets 2.2.1
return true;
}
else if (baseModel == null || derivedModel == null)
{
XBeanDebug.logStackTrace("Null models that weren't caught by EMPTY_CONTENT: " + baseType + " (" + baseModel + "), " + sType + " (" + derivedModel + ")");
state.error(XmlErrorCodes.COMPLEX_TYPE_RESTRICTION$ELEMENT_OR_MIXED_AND_VALID, null, location);
return false;
}
// 5.3 ... as defined in Particle Valid (Restriction) (�3.9.6).
List errors = new ArrayList();
boolean isValid = isParticleValidRestriction(baseModel, derivedModel, errors, location);
if (!isValid)
{
// we only add the last error, because isParticleValidRestriction may add errors
// to the collection that it later changes its mind about, or it may (inadvertently)
// forget to describe an error into the collection....
if (errors.size() == 0)
state.error(XmlErrorCodes.COMPLEX_TYPE_RESTRICTION$ELEMENT_OR_MIXED_AND_VALID, null, location);
else
state.getErrorListener().add(errors.get(errors.size() - 1));
//state.getErrorListener().addAll(errors);
return false; // KHK: should return false, right?
}
}
}
return true;
}
/**
* This function takes in two schema particle types, a baseModel, and a derived model and returns true if the
* derivedModel can be egitimately be used for restriction. Errors are put into the errors collections.
* @param baseModel - The base schema particle
* @param derivedModel - The derived (restricted) schema particle
* @param errors - Invalid restriction errors are put into this collection
* @param context
* @return boolean, true if valid restruction, false if invalid restriction
* @
*/
public static boolean isParticleValidRestriction(SchemaParticle baseModel, SchemaParticle derivedModel, Collection errors, XmlObject context) {
boolean restrictionValid = false;
// 1 They are the same particle.
if (baseModel.equals(derivedModel)) {
restrictionValid = true;
} else {
// Implement table defined in schema spec on restrictions at:
// http://www.w3.org/TR/xmlschema-1/#cos-particle-restrict
switch (baseModel.getParticleType()) {
case SchemaParticle.ELEMENT:
switch (derivedModel.getParticleType()) {
case SchemaParticle.ELEMENT:
restrictionValid = nameAndTypeOK((SchemaLocalElement) baseModel, (SchemaLocalElement) derivedModel, errors, context);
break;
case SchemaParticle.WILDCARD:
case SchemaParticle.ALL:
case SchemaParticle.CHOICE:
case SchemaParticle.SEQUENCE:
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_RESTRICTION$INVALID_RESTRICTION,
new Object[] { printParticle(derivedModel), printParticle(baseModel) }, context));
restrictionValid = false;
break;
default:
assert false : XBeanDebug.logStackTrace("Unknown schema type for Derived Type");
}
break;
case SchemaParticle.WILDCARD:
switch (derivedModel.getParticleType()) {
case SchemaParticle.ELEMENT:
restrictionValid = nsCompat(baseModel, (SchemaLocalElement) derivedModel, errors, context);
break;
case SchemaParticle.WILDCARD:
restrictionValid = nsSubset(baseModel, derivedModel, errors, context);
break;
case SchemaParticle.ALL:
restrictionValid = nsRecurseCheckCardinality(baseModel, derivedModel, errors, context);
break;
case SchemaParticle.CHOICE:
restrictionValid = nsRecurseCheckCardinality(baseModel, derivedModel, errors, context);
break;
case SchemaParticle.SEQUENCE:
restrictionValid = nsRecurseCheckCardinality(baseModel, derivedModel, errors, context);
break;
default:
assert false : XBeanDebug.logStackTrace("Unknown schema type for Derived Type");
}
break;
case SchemaParticle.ALL:
switch (derivedModel.getParticleType()) {
case SchemaParticle.ELEMENT:
restrictionValid = recurseAsIfGroup(baseModel, derivedModel, errors, context);
break;
case SchemaParticle.WILDCARD:
case SchemaParticle.CHOICE:
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_RESTRICTION$INVALID_RESTRICTION,
new Object[] { printParticle(derivedModel), printParticle(baseModel) }, context));
restrictionValid = false;
break;
case SchemaParticle.ALL:
restrictionValid = recurse(baseModel, derivedModel, errors, context);
break;
case SchemaParticle.SEQUENCE:
restrictionValid = recurseUnordered(baseModel, derivedModel, errors, context);
break;
default:
assert false : XBeanDebug.logStackTrace("Unknown schema type for Derived Type");
}
break;
case SchemaParticle.CHOICE:
switch (derivedModel.getParticleType()) {
case SchemaParticle.ELEMENT:
restrictionValid = recurseAsIfGroup(baseModel, derivedModel, errors, context);
break;
case SchemaParticle.WILDCARD:
case SchemaParticle.ALL:
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_RESTRICTION$INVALID_RESTRICTION,
new Object[] { printParticle(derivedModel), printParticle(baseModel) }, context));
restrictionValid = false;
break;
case SchemaParticle.CHOICE:
restrictionValid = recurseLax(baseModel, derivedModel, errors, context);
break;
case SchemaParticle.SEQUENCE:
restrictionValid = mapAndSum(baseModel, derivedModel, errors, context);
break;
default:
assert false : XBeanDebug.logStackTrace("Unknown schema type for Derived Type");
}
break;
case SchemaParticle.SEQUENCE:
switch (derivedModel.getParticleType()) {
case SchemaParticle.ELEMENT:
restrictionValid = recurseAsIfGroup(baseModel, derivedModel, errors, context);
break;
case SchemaParticle.WILDCARD:
case SchemaParticle.ALL:
case SchemaParticle.CHOICE:
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_RESTRICTION$INVALID_RESTRICTION,
new Object[] { printParticle(derivedModel), printParticle(baseModel) }, context));
restrictionValid = false;
break;
case SchemaParticle.SEQUENCE:
restrictionValid = recurse(baseModel, derivedModel, errors, context);
break;
default:
assert false : XBeanDebug.logStackTrace("Unknown schema type for Derived Type");
}
break;
default:
assert false : XBeanDebug.logStackTrace("Unknown schema type for Base Type");
}
}
return restrictionValid;
}
private static boolean mapAndSum(SchemaParticle baseModel, SchemaParticle derivedModel, Collection errors, XmlObject context) {
// mapAndSum is call if base: CHOICE, derived: SEQUENCE
assert baseModel.getParticleType() == SchemaParticle.CHOICE;
assert derivedModel.getParticleType() == SchemaParticle.SEQUENCE;
boolean mapAndSumValid = true;
// Schema Component Constraint: Particle Derivation OK (Sequence:Choice -- MapAndSum)
// For a sequence group particle to be a �valid restriction� of a choice group particle all of the following
// must be true:
// 1 There is a complete functional mapping from the particles in the {particles} of R to the particles in the
// {particles} of B such that each particle in the {particles} of R is a �valid restriction� of the particle in
// the {particles} of B it maps to as defined by Particle Valid (Restriction) (�3.9.6).
// interpretation: each particle child in derived should have a match in base.
// 2 The pair consisting of the product of the {min occurs} of R and the length of its {particles} and unbounded
// if {max occurs} is unbounded otherwise the product of the {max occurs} of R and the length of its {particles}
// is a valid restriction of B's occurrence range as defined by Occurrence Range OK (�3.9.6).
// NOTE: This clause is in principle more restrictive than absolutely necessary, but in practice will cover
// all the likely cases, and is much easier to specify than the fully general version.
// NOTE: This case allows the "unfolding" of iterated disjunctions into sequences. It may be particularly useful
// when the disjunction is an implicit one arising from the use of substitution groups.
// Map step - for each member of the derived model's particle children search base model's particle children
// for match
SchemaParticle[] derivedParticleArray = derivedModel.getParticleChildren();
SchemaParticle[] baseParticleArray = baseModel.getParticleChildren();
for (int i = 0; i < derivedParticleArray.length; i++) {
SchemaParticle derivedParticle = derivedParticleArray[i];
boolean foundMatch = false;
for (int j = 0; j < baseParticleArray.length; j++) {
SchemaParticle baseParticle = baseParticleArray[j];
// recurse to check if there is a match
if (isParticleValidRestriction(baseParticle, derivedParticle, errors, context)) {
// if there is a match then no need to check base particles anymore
foundMatch = true;
break;
}
}
if (!foundMatch) {
mapAndSumValid = false;
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_DERIVATION_MAP_AND_SUM$MAP,
new Object[] { printParticle(derivedParticle) },
context));
// KHK: if we don't return false now, this error may get swallowed by an error produced below
return false;
//break;
}
}
// Sum step
BigInteger derivedRangeMin = derivedModel.getMinOccurs().multiply(BigInteger.valueOf(derivedModel.getParticleChildren().length));
BigInteger derivedRangeMax = null;
BigInteger UNBOUNDED = null;
if (derivedModel.getMaxOccurs() == UNBOUNDED) {
derivedRangeMax = null;
} else {
derivedRangeMax = derivedModel.getMaxOccurs().multiply(BigInteger.valueOf(derivedModel.getParticleChildren().length));
}
// Now check derivedRange (derivedRangeMin and derivedRangeMax) against base model occurrence range
// Schema Component Constraint: Occurrence Range OK
// For a particle's occurrence range to be a valid restriction of another's occurrence range all of the following must be true:
// 1 Its {min occurs} is greater than or equal to the other's {min occurs}.
// 2 one of the following must be true:
// 2.1 The other's {max occurs} is unbounded.
// 2.2 Both {max occurs} are numbers, and the particle's is less than or equal to the other's.
if (derivedRangeMin.compareTo(baseModel.getMinOccurs()) < 0) {
mapAndSumValid = false;
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_DERIVATION_MAP_AND_SUM$SUM_MIN_OCCURS_GTE_MIN_OCCURS,
new Object[] { derivedRangeMin.toString(), baseModel.getMinOccurs().toString() },
context));
} else if (baseModel.getMaxOccurs() != UNBOUNDED && (derivedRangeMax == UNBOUNDED || derivedRangeMax.compareTo(baseModel.getMaxOccurs()) > 0)) {
mapAndSumValid = false;
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_DERIVATION_MAP_AND_SUM$SUM_MAX_OCCURS_LTE_MAX_OCCURS,
new Object[] { derivedRangeMax == UNBOUNDED ? "unbounded" : derivedRangeMax.toString(), baseModel.getMaxOccurs().toString() },
context));
}
return mapAndSumValid;
}
private static boolean recurseAsIfGroup(SchemaParticle baseModel, SchemaParticle derivedModel, Collection errors, XmlObject context) {
// recurseAsIfGroup is called if:
// base: ALL, derived: ELEMENT
// base: CHOICE, derived: ELEMENT
// base: SEQUENCE, derived: ELEMENT
assert (baseModel.getParticleType() == SchemaParticle.ALL && derivedModel.getParticleType() == SchemaParticle.ELEMENT)
|| (baseModel.getParticleType() == SchemaParticle.CHOICE && derivedModel.getParticleType() == SchemaParticle.ELEMENT)
|| (baseModel.getParticleType() == SchemaParticle.SEQUENCE && derivedModel.getParticleType() == SchemaParticle.ELEMENT);
// Schema Component Constraint: Particle Derivation OK (Elt:All/Choice/Sequence -- RecurseAsIfGroup)
// For an element declaration particle to be a �valid restriction� of a group particle
// (all, choice or sequence) a group particle of the variety corresponding to B's, with {min occurs} and
// {max occurs} of 1 and with {particles} consisting of a single particle the same as the element declaration
// must be a �valid restriction� of the group as defined by Particle Derivation OK
// (All:All,Sequence:Sequence -- Recurse) (�3.9.6), Particle Derivation OK (Choice:Choice -- RecurseLax)
// (�3.9.6) or Particle Derivation OK (All:All,Sequence:Sequence -- Recurse) (�3.9.6), depending on whether
// the group is all, choice or sequence
// interpretation: make a fake group of the right type, with min occurs and max occurs of 1
SchemaParticleImpl asIfPart = new SchemaParticleImpl();
asIfPart.setParticleType(baseModel.getParticleType());
asIfPart.setMinOccurs(BigInteger.ONE);
asIfPart.setMaxOccurs(BigInteger.ONE);
asIfPart.setParticleChildren(new SchemaParticle[] { derivedModel });
// the recurse
return isParticleValidRestriction(baseModel, asIfPart, errors, context);
}
private static boolean recurseLax(SchemaParticle baseModel, SchemaParticle derivedModel, Collection errors, XmlObject context) {
// recurseLax is called if base: CHOICE, derived: CHOICE
assert baseModel.getParticleType() == SchemaParticle.CHOICE && derivedModel.getParticleType() == SchemaParticle.CHOICE;
boolean recurseLaxValid = true;
//Schema Component Constraint: Particle Derivation OK (Choice:Choice -- RecurseLax)
// For a choice group particle to be a �valid restriction� of another choice group particle all of the
// following must be true:
// 1 R's occurrence range is a valid restriction of B's occurrence range as defined by Occurrence
// Range OK (�3.9.6);
// 2 There is a complete �order-preserving� functional mapping from the particles in the {particles} of R
// to the particles in the {particles} of B such that each particle in the {particles} of R is a
// �valid restriction� of the particle in the {particles} of B it maps to as defined by
// Particle Valid (Restriction) (�3.9.6).
// NOTE: Although the �validation� semantics of a choice group does not depend on the order of its particles,
// derived choice groups are required to match the order of their base in order to simplify
// checking that the derivation is OK.
// interpretation: check derived choices for match in order, must get an in order match on a base particle,
// don't need to check if base particles are skippable. a lot like recurse
if (!occurrenceRangeOK(baseModel, derivedModel, errors, context)) {
return false;
}
// cycle thru both derived particle children and base particle children looking for matches
// if the derived particle does not match the base particle then base particle can be skipped
SchemaParticle[] derivedParticleArray = derivedModel.getParticleChildren();
SchemaParticle[] baseParticleArray = baseModel.getParticleChildren();
int i = 0, j = 0;
for (; i < derivedParticleArray.length && j < baseParticleArray.length;) {
SchemaParticle derivedParticle = derivedParticleArray[i];
SchemaParticle baseParticle = baseParticleArray[j];
// try to match the two particles by recursing
if (isParticleValidRestriction(baseParticle, derivedParticle, errors, context)) {
// cool found a match, increment both indexes
i++;
j++;
} else {
// did not match, increment the base particle array index only
// Ok, let's skip this base particle, increment base particle array index only
j++;
}
}
// ok, got to the end of one of the arrays
// if at end of base particle array and not at the end of derived particle array then remaining derived
// particles must not match
if (i < derivedParticleArray.length) {
recurseLaxValid = false;
//String message = "Found derived particles that are not matched in the base content model.";
//errors.add(XmlError.forObject(formatDerivedMappingError(message, baseModel, derivedModel), context));
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_DERIVATION_RECURSE_LAX$MAP,
new Object[] { printParticles(baseParticleArray, i) },
context));
}
return recurseLaxValid;
}
private static boolean recurseUnordered(SchemaParticle baseModel, SchemaParticle derivedModel, Collection errors, XmlObject context) {
// recurseUnorder is called when base: ALL and derived: SEQ
assert baseModel.getParticleType() == SchemaParticle.ALL && derivedModel.getParticleType() == SchemaParticle.SEQUENCE;
boolean recurseUnorderedValid = true;
// Schema Component Constraint: Particle Derivation OK (Sequence:All -- RecurseUnordered)
// For a sequence group particle to be a �valid restriction� of an all group particle all of the
// following must be true:
// 1 R's occurrence range is a valid restriction of B's occurrence range as defined by
// Occurrence Range OK (�3.9.6).
// 2 There is a complete functional mapping from the particles in the {particles} of R to the particles
// in the {particles} of B such that all of the following must be true:
// 2.1 No particle in the {particles} of B is mapped to by more than one of the particles in
// the {particles} of R;
// 2.2 Each particle in the {particles} of R is a �valid restriction� of the particle in the {particles} of B
// it maps to as defined by Particle Valid (Restriction) (�3.9.6);
// 2.3 All particles in the {particles} of B which are not mapped to by any particle in the {particles}
// of R are �emptiable� as defined by Particle Emptiable (�3.9.6).
// NOTE: Although this clause allows reordering, because of the limits on the contents of all groups the
// checking process can still be deterministic.
// 1, 2.2, and 2.3 are the same as recurse, so do 2.1 and then call recurse
if (!occurrenceRangeOK(baseModel, derivedModel, errors, context)) {
return false;
}
// read baseParticle array QNames into hashmap
SchemaParticle[] baseParticles = baseModel.getParticleChildren();
HashMap baseParticleMap = new HashMap(10);
Object MAPPED = new Object();
// Initialize the hashmap
for (int i = 0; i < baseParticles.length; i++)
baseParticleMap.put(baseParticles[i].getName(), baseParticles[i]);
// go thru the sequence (derived model's children) and check off from base particle map
SchemaParticle[] derivedParticles = derivedModel.getParticleChildren();
for (int i = 0; i < derivedParticles.length; i++) {
Object baseParticle = baseParticleMap.get(derivedParticles[i].getName());
if (baseParticle == null) {
recurseUnorderedValid = false;
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_DERIVATION_RECURSE_UNORDERED$MAP,
new Object[] { printParticle(derivedParticles[i]) }, context ));
break;
} else {
// got a match
if (baseParticle == MAPPED) {
// whoa, this base particle has already been matched (see 2.1 above)
recurseUnorderedValid = false;
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_DERIVATION_RECURSE_UNORDERED$MAP_UNIQUE,
new Object[] { printParticle(derivedParticles[i]) }, context ));
break;
} else {
SchemaParticle matchedBaseParticle = (SchemaParticle)baseParticle;
if (derivedParticles[i].getMaxOccurs() == null ||
derivedParticles[i].getMaxOccurs().compareTo(BigInteger.ONE) > 0) {
// no derived particles can have a max occurs greater than 1
recurseUnorderedValid = false;
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_DERIVATION_RECURSE_UNORDERED$MAP_MAX_OCCURS_1,
new Object[] { printParticle(derivedParticles[i]), printMaxOccurs(derivedParticles[i].getMinOccurs()) },
context));
break;
}
if (!isParticleValidRestriction(matchedBaseParticle, derivedParticles[i], errors, context))
{
// already have an error
recurseUnorderedValid = false;
break;
}
// everything is cool, got a match, update to MAPPED
baseParticleMap.put(derivedParticles[i].getName(), MAPPED);
}
}
}
// if everything is cool so far then check to see if any base particles are not matched
if (recurseUnorderedValid) {
// get all the hashmap keys and loop thru looking for NOT_MAPPED
Set baseParticleCollection = baseParticleMap.keySet();
for (Iterator iterator = baseParticleCollection.iterator(); iterator.hasNext();) {
QName baseParticleQName = (QName) iterator.next();
if (baseParticleMap.get(baseParticleQName) != MAPPED && !((SchemaParticle)baseParticleMap.get(baseParticleQName)).isSkippable()) {
// this base particle was not mapped and is not "particle emptiable" (skippable)
recurseUnorderedValid = false;
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_DERIVATION_RECURSE_UNORDERED$UNMAPPED_ARE_EMPTIABLE,
new Object[] { printParticle((SchemaParticle)baseParticleMap.get(baseParticleQName)) },
context));
}
}
}
return recurseUnorderedValid;
}
private static boolean recurse(SchemaParticle baseModel, SchemaParticle derivedModel, Collection errors, XmlObject context) {
// recurse is called when base: ALL derived: ALL or base: SEQUENCE derived: SEQUENCE
boolean recurseValid = true;
// For an all or sequence group particle to be a �valid restriction� of another group particle with the same
// {compositor} all of the following must be true:
// 1 R's occurrence range is a valid restriction of B's occurrence range as defined by
// Occurrence Range OK (�3.9.6).
// 2 There is a complete �order-preserving� functional mapping from the particles in the {particles} of R to
// the particles in the {particles} of B such that all of the following must be true:
// 2.1 Each particle in the {particles} of R is a �valid restriction� of the particle in the {particles}
// of B it maps to as defined by Particle Valid (Restriction) (�3.9.6).
// 2.2 All particles in the {particles} of B which are not mapped to by any particle in the {particles}
// of R are �emptiable� as defined by Particle Emptiable (�3.9.6).
// NOTE: Although the �validation� semantics of an all group does not depend on the order of its particles,
// derived all groups are required to match the order of their base in order to simplify checking that
// the derivation is OK.
// [Definition:] A complete functional mapping is order-preserving if each particle r in the domain R maps
// to a particle b in the range B which follows (not necessarily immediately) the particle in the range B
// mapped to by the predecessor of r, if any, where "predecessor" and "follows" are defined with respect to
// the order of the lists which constitute R and B.
if (!occurrenceRangeOK(baseModel, derivedModel, errors, context)) {
// error message is formatted in occurrencRangeOK ...
return false;
}
// cycle thru both derived particle children and base particle children looking for matches
// if the derived particle does not match the base particle then base particle can be skipped if it is
// skippable (same as "particle emptiable") otherwise is an invalid restriction.
// after the derived particles have been cycled if there are any base particles left over then they
// must be skippable or invalid restriction
SchemaParticle[] derivedParticleArray = derivedModel.getParticleChildren();
SchemaParticle[] baseParticleArray = baseModel.getParticleChildren();
int i = 0, j = 0;
for (; i < derivedParticleArray.length && j < baseParticleArray.length;) {
SchemaParticle derivedParticle = derivedParticleArray[i];
SchemaParticle baseParticle = baseParticleArray[j];
// try to match the two particles by recursing
if (isParticleValidRestriction(baseParticle, derivedParticle, errors, context)) {
// cool found a match, increment both indexes
i++;
j++;
} else {
// did not match, increment the base particle array index only
// that's ok if the base particle is skippable
if (baseParticle.isSkippable()) {
// Ok, let's skip this base particle, increment base particle array index only
j++;
} else {
// whoa, particles are not valid restrictions and base is not skippable - ERROR
recurseValid = false;
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_DERIVATION_RECURSE$MAP_VALID,
new Object[] { printParticle(derivedParticle), printParticle(derivedModel),
printParticle(baseParticle), printParticle(baseModel) },
context));
break;
}
}
}
// ok, got to the end of one of the arrays
// if at end of base particle array and not at the end of derived particle array then remaining derived
// particles must not match
if (i < derivedParticleArray.length) {
recurseValid = false;
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_DERIVATION_RECURSE$MAP,
new Object[] { printParticle(derivedModel), printParticle(baseModel), printParticles(derivedParticleArray, i) },
context));
} else {
// if at end of derived particle array and not at end of base particle array then chck remaining
// base particles to assure they are skippable
if (j < baseParticleArray.length) {
ArrayList particles = new ArrayList(baseParticleArray.length);
for (int k = j; k < baseParticleArray.length; k++) {
if (!baseParticleArray[k].isSkippable()) {
particles.add(baseParticleArray[k]);
}
}
if (particles.size() > 0)
{
recurseValid = false;
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_DERIVATION_RECURSE$UNMAPPED_ARE_EMPTIABLE,
new Object[] { printParticle(baseModel), printParticle(derivedModel), printParticles(particles)}, context));
}
}
}
return recurseValid;
}
private static boolean nsRecurseCheckCardinality(SchemaParticle baseModel, SchemaParticle derivedModel, Collection errors, XmlObject context) {
// nsRecurseCheckCardinality is called when:
// base: ANY, derived: ALL
// base: ANY, derived: CHOICE
// base: ANY, derived: SEQUENCE
assert baseModel.getParticleType() == SchemaParticle.WILDCARD;
assert (derivedModel.getParticleType() == SchemaParticle.ALL)
|| (derivedModel.getParticleType() == SchemaParticle.CHOICE)
|| (derivedModel.getParticleType() == SchemaParticle.SEQUENCE);
boolean nsRecurseCheckCardinality = true;
// For a group particle to be a �valid restriction� of a wildcard particle all of the following must be true:
// 1 Every member of the {particles} of the group is a �valid restriction� of the wildcard as defined by Particle Valid (Restriction) (�3.9.6).
// Note: not positive what this means. Interpreting to mean that every particle of the group must adhere to wildcard derivation rules
// in a recursive manner
// Loop thru the children particles of the group and invoke the appropriate function to check for wildcard restriction validity
// BAU - an errata should be submitted on this clause of the spec, because the
// spec makes no sense, as the xstc particlesR013.xsd test exemplifies.
// what we _should_ so is an "as if" on the wildcard, allowing it minOccurs="0" maxOccurs="unbounded"
// before recursing
SchemaParticleImpl asIfPart = new SchemaParticleImpl();
asIfPart.setParticleType(baseModel.getParticleType());
asIfPart.setWildcardProcess(baseModel.getWildcardProcess());
asIfPart.setWildcardSet(baseModel.getWildcardSet());
asIfPart.setMinOccurs(BigInteger.ZERO);
asIfPart.setMaxOccurs(null);
asIfPart.setTransitionRules(baseModel.getWildcardSet(), true);
asIfPart.setTransitionNotes(baseModel.getWildcardSet(), true);
SchemaParticle[] particleChildren = derivedModel.getParticleChildren();
for (int i = 0; i < particleChildren.length; i++) {
SchemaParticle particle = particleChildren[i];
switch (particle.getParticleType()) {
case SchemaParticle.ELEMENT:
// Check for valid Wildcard/Element derivation
nsRecurseCheckCardinality = nsCompat(asIfPart, (SchemaLocalElement) particle, errors, context);
break;
case SchemaParticle.WILDCARD:
// Check for valid Wildcard/Wildcard derivation
nsRecurseCheckCardinality = nsSubset(asIfPart, particle, errors, context);
break;
case SchemaParticle.ALL:
case SchemaParticle.CHOICE:
case SchemaParticle.SEQUENCE:
// Check for valid Wildcard/Group derivation
nsRecurseCheckCardinality = nsRecurseCheckCardinality(asIfPart, particle, errors, context);
break;
}
// If any particle is invalid then break the loop
if (!nsRecurseCheckCardinality) {
break;
}
}
// 2 The effective total range of the group, as defined by Effective Total Range (all and sequence) (�3.8.6)
// (if the group is all or sequence) or Effective Total Range (choice) (�3.8.6) (if it is choice) is a valid
// restriction of B's occurrence range as defined by Occurrence Range OK (�3.9.6).
if (nsRecurseCheckCardinality) {
nsRecurseCheckCardinality = checkGroupOccurrenceOK(baseModel, derivedModel, errors, context);
}
return nsRecurseCheckCardinality;
}
private static boolean checkGroupOccurrenceOK(SchemaParticle baseModel, SchemaParticle derivedModel, Collection errors, XmlObject context) {
boolean groupOccurrenceOK = true;
BigInteger minRange = BigInteger.ZERO;
BigInteger maxRange = BigInteger.ZERO;
switch (derivedModel.getParticleType()) {
case SchemaParticle.ALL:
case SchemaParticle.SEQUENCE:
minRange = getEffectiveMinRangeAllSeq(derivedModel);
maxRange = getEffectiveMaxRangeAllSeq(derivedModel);
break;
case SchemaParticle.CHOICE:
minRange = getEffectiveMinRangeChoice(derivedModel);
maxRange = getEffectiveMaxRangeChoice(derivedModel);
break;
}
// Check min occurs for validity
// derived min occurs is valid if its {min occurs} is greater than or equal to the other's {min occurs}.
if (minRange.compareTo(baseModel.getMinOccurs()) < 0) {
groupOccurrenceOK = false;
errors.add(XmlError.forObject(XmlErrorCodes.OCCURRENCE_RANGE$MIN_GTE_MIN,
new Object[] { printParticle(derivedModel), printParticle(baseModel) },
context));
}
// Check max occurs for validity
// one of the following must be true:
// The base model's {max occurs} is unbounded.
// or both {max occurs} are numbers, and the particle's is less than or equal to the other's
BigInteger UNBOUNDED = null;
if (baseModel.getMaxOccurs() != UNBOUNDED) {
if (maxRange == UNBOUNDED) {
groupOccurrenceOK = false;
errors.add(XmlError.forObject(XmlErrorCodes.OCCURRENCE_RANGE$MAX_LTE_MAX,
new Object[] { printParticle(derivedModel), printParticle(baseModel) },
context));
} else {
if (maxRange.compareTo(baseModel.getMaxOccurs()) > 0) {
groupOccurrenceOK = false;
errors.add(XmlError.forObject(XmlErrorCodes.OCCURRENCE_RANGE$MAX_LTE_MAX,
new Object[] { printParticle(derivedModel), printParticle(baseModel) },
context));
}
}
}
return groupOccurrenceOK;
}
private static BigInteger getEffectiveMaxRangeChoice(SchemaParticle derivedModel) {
BigInteger maxRange = BigInteger.ZERO;
BigInteger UNBOUNDED = null;
// Schema Component Constraint: Effective Total Range (choice)
// The effective total range of a particle whose {term} is a group whose {compositor} is choice
// is a pair of minimum and maximum, as follows:
// MAXIMUM
// 1) unbounded if the {max occurs} of any wildcard or element declaration particle in the group's {particles} or
// the maximum part of the effective total range of any of the group particles in the group's {particles} is
// unbounded (note: seems to be the same as Max Range All or Sequence),
// or 2) if any of those is non-zero and the {max occurs} of the particle itself is unbounded,
// otherwise 3) the product of the particle's {max occurs} and the maximum of the {max occurs} of every
// wildcard or element declaration particle in the group's {particles} and the *maximum* (note: this is the difference
// between MaxRange Choice ans MaxRange All or Sequence) part of the
// effective total range of each of the group particles in the group's {particles}
// (or 0 if there are no {particles}).
boolean nonZeroParticleChildFound = false;
BigInteger maxOccursInWildCardOrElement = BigInteger.ZERO;
BigInteger maxOccursInGroup = BigInteger.ZERO;
SchemaParticle[] particleChildren = derivedModel.getParticleChildren();
for (int i = 0; i < particleChildren.length; i++) {
SchemaParticle particle = particleChildren[i];
switch (particle.getParticleType()) {
case SchemaParticle.WILDCARD:
case SchemaParticle.ELEMENT:
// if unbounded then maxoccurs will be null
if (particle.getMaxOccurs() == UNBOUNDED) {
maxRange = UNBOUNDED;
} else {
if (particle.getIntMaxOccurs() > 0) {
// show tht at least one non-zero particle is found for later test
nonZeroParticleChildFound = true;
if (particle.getMaxOccurs().compareTo(maxOccursInWildCardOrElement) > 0) {
maxOccursInWildCardOrElement = particle.getMaxOccurs();
}
}
}
break;
case SchemaParticle.ALL:
case SchemaParticle.SEQUENCE:
maxRange = getEffectiveMaxRangeAllSeq(particle);
if (maxRange != UNBOUNDED) {
// keep highest maxoccurs found
if (maxRange.compareTo(maxOccursInGroup) > 0) {
maxOccursInGroup = maxRange;
}
}
break;
case SchemaParticle.CHOICE:
maxRange = getEffectiveMaxRangeChoice(particle);
if (maxRange != UNBOUNDED) {
// keep highest maxoccurs found
if (maxRange.compareTo(maxOccursInGroup) > 0) {
maxOccursInGroup = maxRange;
}
}
break;
}
// if an unbounded has been found then we are done
if (maxRange == UNBOUNDED) {
break;
}
}
// 1) unbounded if the {max occurs} of any wildcard or element declaration particle in the group's {particles} or
// the maximum part of the effective total range of any of the group particles in the group's {particles} is
// unbounded
if (maxRange != UNBOUNDED) {
// 2) if any of those is non-zero and the {max occurs} of the particle itself is unbounded
if (nonZeroParticleChildFound && derivedModel.getMaxOccurs() == UNBOUNDED) {
maxRange = UNBOUNDED;
} else {
// 3) the product of the particle's {max occurs} and the maximum of the {max occurs} of every
// wildcard or element declaration particle in the group's {particles} and the *maximum*
// part of the effective total range of each of the group particles in the group's {particles}
maxRange = derivedModel.getMaxOccurs().multiply(maxOccursInWildCardOrElement.add(maxOccursInGroup));
}
}
return maxRange;
}
private static BigInteger getEffectiveMaxRangeAllSeq(SchemaParticle derivedModel) {
BigInteger maxRange = BigInteger.ZERO;
BigInteger UNBOUNDED = null;
// Schema Component Constraint: Effective Total Range (all and sequence)
// The effective total range of a particle whose {term} is a group whose {compositor} is all or sequence is a
// pair of minimum and maximum, as follows:
// MAXIMUM
// 1) unbounded if the {max occurs} of any wildcard or element declaration particle in the group's {particles} or
// the maximum part of the effective total range of any of the group particles in the group's {particles} is
// unbounded, or 2) if any of those is non-zero and the {max occurs} of the particle itself is unbounded, otherwise
// 3) the product of the particle's {max occurs} and the *sum* of the {max occurs} of every wildcard or element
// declaration particle in the group's {particles} and the maximum part of the effective total range of each of
// the group particles in the group's {particles} (or 0 if there are no {particles}).
boolean nonZeroParticleChildFound = false;
BigInteger maxOccursTotal = BigInteger.ZERO;
BigInteger maxOccursInGroup = BigInteger.ZERO;
SchemaParticle[] particleChildren = derivedModel.getParticleChildren();
for (int i = 0; i < particleChildren.length; i++) {
SchemaParticle particle = particleChildren[i];
switch (particle.getParticleType()) {
case SchemaParticle.WILDCARD:
case SchemaParticle.ELEMENT:
// if unbounded then maxoccurs will be null
if (particle.getMaxOccurs() == UNBOUNDED) {
maxRange = UNBOUNDED;
} else {
if (particle.getIntMaxOccurs() > 0) {
// show tht at least one non-zero particle is found for later test
nonZeroParticleChildFound = true;
maxOccursTotal = maxOccursTotal.add(particle.getMaxOccurs());
}
}
break;
case SchemaParticle.ALL:
case SchemaParticle.SEQUENCE:
maxRange = getEffectiveMaxRangeAllSeq(particle);
if (maxRange != UNBOUNDED) {
// keep highest maxoccurs found
if (maxRange.compareTo(maxOccursInGroup) > 0) {
maxOccursInGroup = maxRange;
}
}
break;
case SchemaParticle.CHOICE:
maxRange = getEffectiveMaxRangeChoice(particle);
if (maxRange != UNBOUNDED) {
// keep highest maxoccurs found
if (maxRange.compareTo(maxOccursInGroup) > 0) {
maxOccursInGroup = maxRange;
}
}
break;
}
// if an unbounded has been found then we are done
if (maxRange == UNBOUNDED) {
break;
}
}
// 1) unbounded if the {max occurs} of any wildcard or element declaration particle in the group's {particles} or
// the maximum part of the effective total range of any of the group particles in the group's {particles} is
// unbounded
if (maxRange != UNBOUNDED) {
// 2) if any of those is non-zero and the {max occurs} of the particle itself is unbounded
if (nonZeroParticleChildFound && derivedModel.getMaxOccurs() == UNBOUNDED) {
maxRange = UNBOUNDED;
} else {
// 3) the product of the particle's {max occurs} and the sum of the {max occurs} of every wildcard or element
// declaration particle in the group's {particles} and the maximum part of the effective total range of each of
// the group particles in the group's {particles}
maxRange = derivedModel.getMaxOccurs().multiply(maxOccursTotal.add(maxOccursInGroup));
}
}
return maxRange;
}
private static BigInteger getEffectiveMinRangeChoice(SchemaParticle derivedModel) {
// Schema Component Constraint: Effective Total Range (choice)
// The effective total range of a particle whose {term} is a group whose {compositor} is choice is a pair of
// minimum and maximum, as follows:
// MINIMUM
// The product of the particle's {min occurs}
// and the *minimum* of the {min occurs} of every wildcard or element
// declaration particle in the group's {particles} and the minimum part of the effective total range of each of
// the group particles in the group's {particles} (or 0 if there are no {particles}).
SchemaParticle[] particleChildren = derivedModel.getParticleChildren();
if (particleChildren.length == 0)
return BigInteger.ZERO;
BigInteger minRange = null;
// get the minimum of every wildcard or element
// total up the effective total range for each group
for (int i = 0; i < particleChildren.length; i++) {
SchemaParticle particle = particleChildren[i];
switch (particle.getParticleType()) {
case SchemaParticle.WILDCARD:
case SchemaParticle.ELEMENT:
if (minRange == null || minRange.compareTo(particle.getMinOccurs()) > 0) {
minRange = particle.getMinOccurs();
}
break;
case SchemaParticle.ALL:
case SchemaParticle.SEQUENCE:
BigInteger mrs = getEffectiveMinRangeAllSeq(particle);
if (minRange == null || minRange.compareTo(mrs) > 0) {
minRange = mrs;
}
break;
case SchemaParticle.CHOICE:
BigInteger mrc = getEffectiveMinRangeChoice(particle);
if (minRange == null || minRange.compareTo(mrc) > 0) {
minRange = mrc;
}
break;
}
}
if (minRange == null)
minRange = BigInteger.ZERO;
// calculate the total
minRange = derivedModel.getMinOccurs().multiply(minRange);
return minRange;
}
private static BigInteger getEffectiveMinRangeAllSeq(SchemaParticle derivedModel) {
BigInteger minRange = BigInteger.ZERO;
// Schema Component Constraint: Effective Total Range (all and sequence)
// The effective total range of a particle whose {term} is a group whose {compositor} is all or sequence is a
// pair of minimum and maximum, as follows:
// MINIMUM
// The product of the particle's {min occurs}
// and the *sum* of the {min occurs} of every wildcard or element
// declaration particle in the group's {particles}
// and the minimum part of the effective total range of each
// of the group particles in the group's {particles} (or 0 if there are no {particles}).
SchemaParticle[] particleChildren = derivedModel.getParticleChildren();
BigInteger particleTotalMinOccurs = BigInteger.ZERO;
for (int i = 0; i < particleChildren.length; i++) {
SchemaParticle particle = particleChildren[i];
switch (particle.getParticleType()) {
case SchemaParticle.WILDCARD:
case SchemaParticle.ELEMENT:
particleTotalMinOccurs = particleTotalMinOccurs.add(particle.getMinOccurs());
break;
case SchemaParticle.ALL:
case SchemaParticle.SEQUENCE:
particleTotalMinOccurs = particleTotalMinOccurs.add(getEffectiveMinRangeAllSeq(particle));
break;
case SchemaParticle.CHOICE:
particleTotalMinOccurs = particleTotalMinOccurs.add(getEffectiveMinRangeChoice(particle));
break;
}
}
minRange = derivedModel.getMinOccurs().multiply(particleTotalMinOccurs);
return minRange;
}
private static boolean nsSubset(SchemaParticle baseModel, SchemaParticle derivedModel, Collection errors, XmlObject context) {
// nsSubset is called when base: ANY, derived: ANY
assert baseModel.getParticleType() == SchemaParticle.WILDCARD;
assert derivedModel.getParticleType() == SchemaParticle.WILDCARD;
boolean nsSubset = false;
// For a wildcard particle to be a �valid restriction� of another wildcard particle all of the following must be true:
// 1 R's occurrence range must be a valid restriction of B's occurrence range as defined by Occurrence Range OK (�3.9.6).
if (occurrenceRangeOK(baseModel, derivedModel, errors, context)) {
// 2 R's {namespace constraint} must be an intensional subset of B's {namespace constraint} as defined
// by Wildcard Subset (�3.10.6).
if (baseModel.getWildcardSet().inverse().isDisjoint(derivedModel.getWildcardSet())) {
nsSubset = true;
} else {
nsSubset = false;
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_DERIVATION_NS_SUBST$WILDCARD_SUBSET,
new Object[] { printParticle(derivedModel), printParticle(baseModel) }, context));
}
} else {
nsSubset = false;
// error already produced by occurrenceRangeOK
//errors.add(XmlError.forObject(formatNSIsNotSubsetError(baseModel, derivedModel), context));
}
return nsSubset;
}
private static boolean nsCompat(SchemaParticle baseModel, SchemaLocalElement derivedElement, Collection errors, XmlObject context) {
// nsCompat is called when base: ANY, derived: ELEMENT
assert baseModel.getParticleType() == SchemaParticle.WILDCARD;
boolean nsCompat = false;
// For an element declaration particle to be a �valid restriction� of a wildcard particle all of the following must be true:
// 1 The element declaration's {target namespace} is �valid� with respect to the wildcard's {namespace constraint}
// as defined by Wildcard allows Namespace Name (�3.10.4).
if (baseModel.getWildcardSet().contains(derivedElement.getName())) {
// 2 R's occurrence range is a valid restriction of B's occurrence range as defined by Occurrence Range OK (�3.9.6).
if (occurrenceRangeOK(baseModel, (SchemaParticle) derivedElement, errors, context)) {
nsCompat = true;
} else {
// error already produced by occurrenceRangeOK
//errors.add(XmlError.forObject(formatOccurenceRangeMinError(baseModel, (SchemaParticle) derivedElement), context));
}
} else {
nsCompat = false;
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_DERIVATION_NS_COMPAT$WILDCARD_VALID,
new Object[] { printParticle((SchemaParticle)derivedElement), printParticle(baseModel) },
context));
}
return nsCompat;
}
private static boolean nameAndTypeOK(SchemaLocalElement baseElement, SchemaLocalElement derivedElement, Collection errors, XmlObject context) {
// nameAndTypeOK called when base: ELEMENT and derived: ELEMENT
// Schema Component Constraint: Particle Restriction OK (Elt:Elt -- NameAndTypeOK)
// 1 The declarations' {name}s and {target namespace}s are the same.
if (!((SchemaParticle)baseElement).canStartWithElement(derivedElement.getName())) {
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_RESTRICTION_NAME_AND_TYPE$NAME,
new Object[] { printParticle((SchemaParticle)derivedElement), printParticle((SchemaParticle)baseElement) }, context));
return false;
}
// 2 Either B's {nillable} is true or R's {nillable} is false.
if (!baseElement.isNillable() && derivedElement.isNillable()) {
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_RESTRICTION_NAME_AND_TYPE$NILLABLE,
new Object[] { printParticle((SchemaParticle)derivedElement), printParticle((SchemaParticle)baseElement) }, context));
return false;
}
// 3 R's occurrence range is a valid restriction of B's occurrence range as defined by Occurrence Range OK (�3.9.6).
if (!occurrenceRangeOK((SchemaParticle) baseElement, (SchemaParticle) derivedElement, errors, context)) {
// error already produced
return false;
}
// 4 either B's declaration's {value constraint} is absent, or is not fixed,
// or R's declaration's {value constraint} is fixed with the same value.
if (!checkFixed(baseElement, derivedElement, errors, context))
{
// error already produced
return false;
}
// 5 R's declaration's {identity-constraint definitions} is a subset of B's declaration's {identity-constraint definitions}, if any.
if (!checkIdentityConstraints(baseElement, derivedElement, errors, context))
{
// error already produced
return false;
}
// 7 R's {type definition} is validly derived given {extension, list, union} from B's {type definition} as
// defined by Type Derivation OK (Complex) (�3.4.6) or Type Derivation OK (Simple) (�3.14.6), as appropriate.
if (!typeDerivationOK(baseElement.getType(), derivedElement.getType(), errors, context))
{
// error already produced
return false;
}
// 6 R's declaration's {disallowed substitutions} is a superset of B's declaration's {disallowed substitutions}.
if (!blockSetOK(baseElement, derivedElement, errors, context))
{
// error already produced
return false;
}
return true;
}
private static boolean blockSetOK(SchemaLocalElement baseElement, SchemaLocalElement derivedElement, Collection errors, XmlObject context)
{
if (baseElement.blockRestriction() && !derivedElement.blockRestriction())
{
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_RESTRICTION_NAME_AND_TYPE$DISALLOWED_SUBSTITUTIONS,
new Object[] { printParticle((SchemaParticle)derivedElement), "restriction", printParticle((SchemaParticle)baseElement) },
context));
return false;
}
if (baseElement.blockExtension() && !derivedElement.blockExtension())
{
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_RESTRICTION_NAME_AND_TYPE$DISALLOWED_SUBSTITUTIONS,
new Object[] { printParticle((SchemaParticle)derivedElement), "extension", printParticle((SchemaParticle)baseElement) },
context));
return false;
}
if (baseElement.blockSubstitution() && !derivedElement.blockSubstitution())
{
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_RESTRICTION_NAME_AND_TYPE$DISALLOWED_SUBSTITUTIONS,
new Object[] { printParticle((SchemaParticle)derivedElement), "substitution", printParticle((SchemaParticle)baseElement) },
context));
return false;
}
return true;
}
private static boolean typeDerivationOK(SchemaType baseType, SchemaType derivedType, Collection errors, XmlObject context){
boolean typeDerivationOK = false;
// 1 If B and D are not the same type definition, then the {derivation method} of D must not be in the subset.
// 2 One of the following must be true:
// 2.1 B and D must be the same type definition.
// 2.2 B must be D's {base type definition}.
// 2.3 All of the following must be true:
// 2.3.1 D's {base type definition} must not be the �ur-type definition�.
// 2.3.2 The appropriate case among the following must be true:
// 2.3.2.1 If D's {base type definition} is complex, then it must be validly derived from B given the subset as defined by this constraint.
// 2.3.2.2 If D's {base type definition} is simple, then it must be validly derived from B given the subset as defined in Type Derivation OK (Simple) (�3.14.6).
// This line will check if derivedType is a subType of baseType (should handle all of the 2.xx checks above)
if (baseType.isAssignableFrom(derivedType)) {
// Ok derived type is subtype but need to make sure that all of the derivations between the two types are by
// Restriction.
typeDerivationOK = checkAllDerivationsForRestriction(baseType, derivedType, errors, context);
} else {
// derived type is not a sub-type of base type
typeDerivationOK = false;
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_RESTRICTION_NAME_AND_TYPE$TYPE_VALID,
new Object[] { printType(derivedType), printType(baseType) }, context));
}
return typeDerivationOK;
}
private static boolean checkAllDerivationsForRestriction(SchemaType baseType, SchemaType derivedType, Collection errors, XmlObject context) {
boolean allDerivationsAreRestrictions = true;
SchemaType currentType = derivedType;
// XMLBEANS-66: if baseType is a union, check restriction is of one of the constituant types
Set possibleTypes = null;
if (baseType.getSimpleVariety() == SchemaType.UNION)
possibleTypes = new HashSet(Arrays.asList(baseType.getUnionConstituentTypes()));
// run up the types hierarchy from derived Type to base Type and make sure that all are derived by
// restriction. If any are not then this is not a valid restriction.
while (!baseType.equals(currentType) &&
possibleTypes != null && !possibleTypes.contains(currentType)) {
if (currentType.getDerivationType() == SchemaType.DT_RESTRICTION) {
currentType = currentType.getBaseType();
} else {
allDerivationsAreRestrictions = false;
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_RESTRICTION_NAME_AND_TYPE$TYPE_RESTRICTED,
new Object[] { printType(derivedType), printType(baseType), printType(currentType) }, context));
break;
}
}
return allDerivationsAreRestrictions;
}
private static boolean checkIdentityConstraints(SchemaLocalElement baseElement, SchemaLocalElement derivedElement, Collection errors, XmlObject context) {
// 5 R's declaration's {identity-constraint definitions} is a subset of B's declaration's {identity-constraint definitions}, if any.
boolean identityConstraintsOK = true;
SchemaIdentityConstraint[] baseConstraints = baseElement.getIdentityConstraints();
SchemaIdentityConstraint[] derivedConstraints = derivedElement.getIdentityConstraints();
// cycle thru derived's identity constraints and check each to assure they in the array of base constraints
for (int i = 0; i < derivedConstraints.length; i++) {
SchemaIdentityConstraint derivedConstraint = derivedConstraints[i];
if (checkForIdentityConstraintExistence(baseConstraints, derivedConstraint)) {
identityConstraintsOK = false;
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_RESTRICTION_NAME_AND_TYPE$IDENTITY_CONSTRAINTS,
new Object[] { printParticle((SchemaParticle)derivedElement), printParticle((SchemaParticle)baseElement) },
context));
break;
}
}
return identityConstraintsOK;
}
private static boolean checkForIdentityConstraintExistence(SchemaIdentityConstraint[] baseConstraints, SchemaIdentityConstraint derivedConstraint) {
// spin thru the base identity constraints check to see if derived constraint exists
boolean identityConstraintExists = false;
for (int i = 0; i < baseConstraints.length; i++) {
SchemaIdentityConstraint baseConstraint = baseConstraints[i];
if (baseConstraint.getName().equals(derivedConstraint.getName())) {
identityConstraintExists = true;
break;
}
}
return identityConstraintExists;
}
private static boolean checkFixed(SchemaLocalElement baseModel, SchemaLocalElement derivedModel, Collection errors, XmlObject context) {
// 4 either B's declaration's {value constraint} is absent, or is not fixed,
// or R's declaration's {value constraint} is fixed with the same value.
boolean checkFixed = false;
if (baseModel.isFixed()) {
if (baseModel.getDefaultText().equals(derivedModel.getDefaultText())) {
// R's declaration's {value constraint} is fixed with the same value.
checkFixed = true;
} else {
// The derived element has a fixed value that is different than the base element
errors.add(XmlError.forObject(XmlErrorCodes.PARTICLE_RESTRICTION_NAME_AND_TYPE$FIXED,
new Object[] { printParticle((SchemaParticle)derivedModel), derivedModel.getDefaultText(),
printParticle((SchemaParticle)baseModel), baseModel.getDefaultText() },
context));
checkFixed = false;
}
} else {
// B's declaration's {value constraint} is absent, or is not fixed,
checkFixed = true;
}
return checkFixed;
}
private static boolean occurrenceRangeOK(SchemaParticle baseParticle, SchemaParticle derivedParticle, Collection errors, XmlObject context) {
boolean occurrenceRangeOK = false;
// Note: in the following comments (from the schema spec) other is the baseModel
// 1 Its {min occurs} is greater than or equal to the other's {min occurs}.
if (derivedParticle.getMinOccurs().compareTo(baseParticle.getMinOccurs()) >= 0) {
// 2 one of the following must be true:
// 2.1 The other's {max occurs} is unbounded.
if (baseParticle.getMaxOccurs() == null) {
occurrenceRangeOK = true;
} else {
// 2.2 Both {max occurs} are numbers, and the particle's is less than or equal to the other's.
if (derivedParticle.getMaxOccurs() != null && baseParticle.getMaxOccurs() != null &&
derivedParticle.getMaxOccurs().compareTo(baseParticle.getMaxOccurs()) <= 0) {
occurrenceRangeOK = true;
} else {
occurrenceRangeOK = false;
errors.add(XmlError.forObject(XmlErrorCodes.OCCURRENCE_RANGE$MAX_LTE_MAX,
new Object[] { printParticle(derivedParticle), printMaxOccurs(derivedParticle.getMaxOccurs()),
printParticle(baseParticle), printMaxOccurs(baseParticle.getMaxOccurs()) },
context));
}
}
} else {
occurrenceRangeOK = false;
errors.add(XmlError.forObject(XmlErrorCodes.OCCURRENCE_RANGE$MIN_GTE_MIN,
new Object[] { printParticle(derivedParticle), derivedParticle.getMinOccurs().toString(),
printParticle(baseParticle), baseParticle.getMinOccurs().toString() },
context));
}
return occurrenceRangeOK;
}
private static String printParticles(List parts)
{
return printParticles((SchemaParticle[])parts.toArray(new SchemaParticle[parts.size()]));
}
private static String printParticles(SchemaParticle[] parts)
{
return printParticles(parts, 0, parts.length);
}
private static String printParticles(SchemaParticle[] parts, int start)
{
return printParticles(parts, start, parts.length);
}
private static String printParticles(SchemaParticle[] parts, int start, int end)
{
StringBuilder buf = new StringBuilder(parts.length * 30);
for (int i = start; i < end; )
{
buf.append(printParticle(parts[i]));
if (++i != end)
buf.append(", ");
}
return buf.toString();
}
private static String printParticle(SchemaParticle part)
{
switch (part.getParticleType()) {
case SchemaParticle.ALL:
return "<all>";
case SchemaParticle.CHOICE:
return "<choice>";
case SchemaParticle.ELEMENT:
return "<element name=\"" + QNameHelper.pretty(part.getName()) + "\">";
case SchemaParticle.SEQUENCE:
return "<sequence>";
case SchemaParticle.WILDCARD:
return "<any>";
default :
return "??";
}
}
private static String printMaxOccurs(BigInteger bi)
{
if (bi == null)
return "unbounded";
return bi.toString();
}
private static String printType(SchemaType type)
{
if (type.getName() != null)
return QNameHelper.pretty(type.getName());
return type.toString();
}
private static void checkSubstitutionGroups(SchemaGlobalElement[] elts)
{
StscState state = StscState.get();
for (int i = 0 ; i < elts.length ; i++)
{
SchemaGlobalElement elt = elts[i];
SchemaGlobalElement head = elt.substitutionGroup();
if (head != null)
{
SchemaType headType = head.getType();
SchemaType tailType = elt.getType();
XmlObject parseTree = ((SchemaGlobalElementImpl)elt)._parseObject;
if (! headType.isAssignableFrom(tailType))
{
state.error(XmlErrorCodes.ELEM_PROPERTIES$SUBSTITUTION_VALID,
new Object[] {QNameHelper.pretty(elt.getName()),
QNameHelper.pretty(head.getName())},
parseTree);
}
else if (head.finalExtension() && head.finalRestriction())
{
state.error(XmlErrorCodes.ELEM_PROPERTIES$SUBSTITUTION_FINAL,
new Object[] {QNameHelper.pretty(elt.getName()),
QNameHelper.pretty(head.getName()),
"#all"}, parseTree);
}
else if (! headType.equals(tailType))
{
if (head.finalExtension() &&
tailType.getDerivationType() == SchemaType.DT_EXTENSION)
{
state.error(XmlErrorCodes.ELEM_PROPERTIES$SUBSTITUTION_FINAL,
new Object[] {QNameHelper.pretty(elt.getName()),
QNameHelper.pretty(head.getName()),
"extension"}, parseTree);
}
else if (head.finalRestriction() &&
tailType.getDerivationType() == SchemaType.DT_RESTRICTION)
{
state.error(XmlErrorCodes.ELEM_PROPERTIES$SUBSTITUTION_FINAL,
new Object[] {QNameHelper.pretty(elt.getName()),
QNameHelper.pretty(head.getName()),
"restriction"}, parseTree);
}
}
}
}
}
}