org.apache.xmlbeans.impl.schema.StscChecker Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com.liferay.document.library.opener.onedrive.web
Show all versions of com.liferay.document.library.opener.onedrive.web
Liferay Document Library Opener OneDrive Web
/* 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.
* 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.
* 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)
{
StringBuffer buf = new StringBuffer(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 "";
case SchemaParticle.CHOICE:
return "";
case SchemaParticle.ELEMENT:
return "";
case SchemaParticle.SEQUENCE:
return "";
case SchemaParticle.WILDCARD:
return "";
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);
}
}
}
}
}
}