/*
* reserved comment block
* DO NOT REMOVE OR ALTER!
*/
/*
* Copyright 2001-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
*
*
* 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.
*/
/**
* Class <code>XSAttributeCheck</code> is used to check the validity of attributes
* appearing in the schema document. It
* - reports an error for invalid element (invalid namespace, invalid name)
* - reports an error for invalid attribute (invalid namespace, invalid name)
* - reports an error for invalid attribute value
* - return compiled values for attriute values
* - provide default value for missing optional attributes
* - provide default value for incorrect attribute values
*
* But it's the caller's responsibility to check whether a required attribute
* is present.
*
* Things need revisiting:
* - Whether to return non-schema attributes/values
* - Do we need to update NamespaceScope and ErrorReporter when reset()?
* - Should have the datatype validators return compiled value
* - use symbol table instead of many hashtables
*
* @xerces.internal
*
* @author Sandy Gao, IBM
* @version $Id: XSAttributeChecker.java,v 1.12 2010-11-01 04:40:02 joehw Exp $
*/
public class XSAttributeChecker {
// REVISIT: only local element and attribute are different from others.
// it's possible to have either name or ref. all the others
// are only allowed to have one of name or ref, or neither of them.
// we'd better move such checking to the traverser.
//public static final int ATTIDX_OTHERVALUES = ATTIDX_COUNT++;
// constants to return
// used to store the map from element name to attribute list
// for 14 global elements
// for 39 local elememnts
// used to initialize fEleAttrsMap
// step 1: all possible data types
// DT_??? >= 0 : validate using a validator, which is initialized staticly
// DT_??? < 0 : validate directly, which is done in "validate()"
// used to store extra datatype validators
static {
// step 5: register all datatype validators for new types
// anyURI
// ID
// QName
// string
// token
// NCName
// xpath = a subset of XPath expression
// xpath = a subset of XPath expression
// language
}
static {
// step 2: all possible attributes for all elements
int attCount = 0;
int ATT_ABSTRACT_D = attCount++;
int ATT_ATTRIBUTE_FD_D = attCount++;
int ATT_BASE_R = attCount++;
int ATT_BASE_N = attCount++;
int ATT_BLOCK_N = attCount++;
int ATT_BLOCK1_N = attCount++;
int ATT_BLOCK_D_D = attCount++;
int ATT_DEFAULT_N = attCount++;
int ATT_ELEMENT_FD_D = attCount++;
int ATT_FINAL_N = attCount++;
int ATT_FINAL1_N = attCount++;
int ATT_FINAL_D_D = attCount++;
int ATT_FIXED_N = attCount++;
int ATT_FIXED_D = attCount++;
int ATT_FORM_N = attCount++;
int ATT_ITEMTYPE_N = attCount++;
int ATT_MAXOCCURS_D = attCount++;
int ATT_MAXOCCURS1_D = attCount++;
int ATT_MEMBER_T_N = attCount++;
int ATT_MINOCCURS_D = attCount++;
int ATT_MINOCCURS1_D = attCount++;
int ATT_MIXED_D = attCount++;
int ATT_MIXED_N = attCount++;
int ATT_NAME_R = attCount++;
int ATT_NAMESPACE_D = attCount++;
int ATT_NAMESPACE_N = attCount++;
int ATT_NILLABLE_D = attCount++;
int ATT_PROCESS_C_D = attCount++;
int ATT_PUBLIC_R = attCount++;
int ATT_REFER_R = attCount++;
int ATT_SCHEMA_L_R = attCount++;
int ATT_SCHEMA_L_N = attCount++;
int ATT_SOURCE_N = attCount++;
int ATT_SUBSTITUTION_G_N = attCount++;
int ATT_SYSTEM_N = attCount++;
int ATT_TARGET_N_N = attCount++;
int ATT_TYPE_N = attCount++;
int ATT_VALUE_NNI_N = attCount++;
int ATT_VALUE_PI_N = attCount++;
int ATT_VALUE_STR_N = attCount++;
int ATT_VALUE_WS_N = attCount++;
int ATT_VERSION_N = attCount++;
int ATT_XML_LANG = attCount++;
int ATT_XPATH_R = attCount++;
int ATT_XPATH1_R = attCount++;
// step 3: store all these attributes in an array
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
null);
// step 4: for each element, make a list of possible attributes
// for element "attribute" - global
// default = string
// fixed = string
// id = ID
// name = NCName
// type = QName
// for element "attribute" - local name
// default = string
// fixed = string
// form = (qualified | unqualified)
// id = ID
// name = NCName
// type = QName
// use = (optional | prohibited | required) : optional
// for element "attribute" - local ref
// default = string
// fixed = string
// id = ID
// ref = QName
// use = (optional | prohibited | required) : optional
// for element "element" - global
// abstract = boolean : false
// block = (#all | List of (extension | restriction | substitution))
// default = string
// final = (#all | List of (extension | restriction))
// fixed = string
// id = ID
// name = NCName
// nillable = boolean : false
// substitutionGroup = QName
// type = QName
// for element "element" - local name
// block = (#all | List of (extension | restriction | substitution))
// default = string
// fixed = string
// form = (qualified | unqualified)
// id = ID
// maxOccurs = (nonNegativeInteger | unbounded) : 1
// minOccurs = nonNegativeInteger : 1
// name = NCName
// nillable = boolean : false
// type = QName
// for element "element" - local ref
// id = ID
// maxOccurs = (nonNegativeInteger | unbounded) : 1
// minOccurs = nonNegativeInteger : 1
// ref = QName
// for element "complexType" - global
// abstract = boolean : false
// block = (#all | List of (extension | restriction))
// final = (#all | List of (extension | restriction))
// id = ID
// mixed = boolean : false
// name = NCName
// for element "notation" - global
// id = ID
// name = NCName
// public = A public identifier, per ISO 8879
// system = anyURI
// for element "complexType" - local
// id = ID
// mixed = boolean : false
// for element "simpleContent" - local
// id = ID
// for element "restriction" - local
// base = QName
// id = ID
// for element "extension" - local
// base = QName
// id = ID
// for element "attributeGroup" - local ref
// id = ID
// ref = QName
// for element "anyAttribute" - local
// id = ID
// namespace = ((##any | ##other) | List of (anyURI | (##targetNamespace | ##local)) ) : ##any
// processContents = (lax | skip | strict) : strict
// for element "complexContent" - local
// id = ID
// mixed = boolean
// for element "attributeGroup" - global
// id = ID
// name = NCName
// for element "group" - global
// id = ID
// name = NCName
// for element "group" - local ref
// id = ID
// maxOccurs = (nonNegativeInteger | unbounded) : 1
// minOccurs = nonNegativeInteger : 1
// ref = QName
// for element "all" - local
// id = ID
// maxOccurs = 1 : 1
// minOccurs = (0 | 1) : 1
// for element "choice" - local
// id = ID
// maxOccurs = (nonNegativeInteger | unbounded) : 1
// minOccurs = nonNegativeInteger : 1
// for element "sequence" - local
// for element "any" - local
// id = ID
// maxOccurs = (nonNegativeInteger | unbounded) : 1
// minOccurs = nonNegativeInteger : 1
// namespace = ((##any | ##other) | List of (anyURI | (##targetNamespace | ##local)) ) : ##any
// processContents = (lax | skip | strict) : strict
// for element "unique" - local
// id = ID
// name = NCName
// for element "key" - local
// for element "keyref" - local
// id = ID
// name = NCName
// refer = QName
// for element "selector" - local
// id = ID
// xpath = a subset of XPath expression
// for element "field" - local
// id = ID
// xpath = a subset of XPath expression
// for element "annotation" - global
// id = ID
// for element "annotation" - local
// for element "appinfo" - local
// source = anyURI
// for element "documentation" - local
// source = anyURI
// xml:lang = language
// for element "simpleType" - global
// final = (#all | List of (list | union | restriction))
// id = ID
// name = NCName
// for element "simpleType" - local
// final = (#all | List of (list | union | restriction))
// id = ID
// for element "restriction" - local
// already registered for complexType
// for element "list" - local
// id = ID
// itemType = QName
// for element "union" - local
// id = ID
// memberTypes = List of QName
// for element "schema" - global
// attributeFormDefault = (qualified | unqualified) : unqualified
// blockDefault = (#all | List of (extension | restriction | substitution)) : ''
// elementFormDefault = (qualified | unqualified) : unqualified
// finalDefault = (#all | List of (extension | restriction | list | union)) : ''
// id = ID
// targetNamespace = anyURI
// version = token
// xml:lang = language
// for element "include" - global
// id = ID
// schemaLocation = anyURI
// for element "redefine" - global
// for element "import" - global
// id = ID
// namespace = anyURI
// schemaLocation = anyURI
// for element "length" - local
// id = ID
// value = nonNegativeInteger
// fixed = boolean : false
// for element "minLength" - local
// for element "maxLength" - local
// for element "fractionDigits" - local
// for element "totalDigits" - local
// id = ID
// value = positiveInteger
// fixed = boolean : false
// for element "pattern" - local
// id = ID
// value = string
// for element "enumeration" - local
// id = ID
// value = anySimpleType
// for element "whiteSpace" - local
// id = ID
// value = preserve | replace | collapse
// fixed = boolean : false
// for element "maxInclusive" - local
// id = ID
// value = anySimpleType
// fixed = boolean : false
// for element "maxExclusive" - local
// for element "minInclusive" - local
// for element "minExclusive" - local
}
// used to resolver namespace prefixes
// used to store symbols.
// used to store the mapping from processed element to attributes
// temprory vector, used to hold the namespace list
// whether this attribute appeared in the current element
// constructor. Sets fErrorReproter and get datatype validators
}
}
/**
* Check whether the specified element conforms to the attributes restriction
* an array of attribute values is returned. the caller must call
* <code>returnAttrArray</code> to return that array.
*
* @param element which element to check
* @param isGlobal whether a child of <schema> or <redefine>
* @param schemaDoc the document where the element lives in
* @return an array containing attribute values
*/
}
/**
* Check whether the specified element conforms to the attributes restriction
* an array of attribute values is returned. the caller must call
* <code>returnAttrArray</code> to return that array. This method also takes
* an extra parameter: if the element is "enumeration", whether to make a
* copy of the namespace context, so that the value can be resolved as a
* QName later.
*
* @param element which element to check
* @param isGlobal whether a child of <schema> or <redefine>
* @param schemaDoc the document where the element lives in
* @param enumAsQName whether to tread enumeration value as QName
* @return an array containing attribute values
*/
return null;
// get all attributes
// update NamespaceSupport
}
// REVISIT: only local element and attribute are different from others.
// it's possible to have either name or ref. all the others
// are only allowed to have one of name or ref, or neither of them.
// we'd better move such checking to the traverser.
if (!isGlobal) {
else
else
}
}
// get desired attribute list of this element
// should never gets here.
// when this method is called, the call already knows that
// the element can appear.
return null;
}
//Hashtable attrValues = new Hashtable();
//Hashtable otherValues = new Hashtable();
long fromDefault = 0;
// clear the "seen" flag.
// traverse all attributes
for (int i = 0; i < length; i++) {
//String attrName = DOMUtil.getLocalName(sattr);
// we don't want to add namespace declarations to the non-schema attributes
continue;
}
// Both <schema> and <documentation> may have an xml:lang attribute.
// Set the URI for this attribute to null so that we process it
// like any other schema attribute.
}
}
// for attributes with namespace prefix
//
// attributes with schema namespace are not allowed
// and not allowed on "document" and "appInfo"
}
else {
// these are usually small
}
// for attributes from other namespace
// store them in a list, and TRY to validate them after
// schema traversal (because it's "lax")
//otherValues.put(attrName, attrVal);
// REVISIT: actually use this some day...
// String attrRName = attrURI + "," + attrName;
// Vector values = (Vector)fNonSchemaAttrs.get(attrRName);
// if (values == null) {
// values = new Vector();
// values.addElement(attrName);
// values.addElement(elName);
// values.addElement(attrVal);
// fNonSchemaAttrs.put(attrRName, values);
// }
// else {
// values.addElement(elName);
// values.addElement(attrVal);
// }
}
continue;
}
// check whether this attribute is allowed
reportSchemaError ("s4s-att-not-allowed",
element);
continue;
}
// we've seen this attribute
// check the value against the datatype
try {
// no checking on string needs to be done here.
// no checking on xpath needs to be done here.
// xpath values are validated in xpath parser
}
} else {
}
}
else {
attrValues[oneAttr.valueIndex] = validate(attrValues, attrName, attrVal, oneAttr.dvIndex, schemaDoc);
}
} catch (InvalidDatatypeValueException ide) {
reportSchemaError ("s4s-att-invalid-value",
element);
//attrValues.put(attrName, oneAttr.dfltValue);
}
// For "enumeration", and type is possible to be a QName, we need
// to return namespace context for later QName resolution.
}
}
// apply default values
// if the attribute didn't apprear, and
// if the attribute is optional with default value, apply it
//attrValues.put(oneAttr.name, oneAttr.dfltValue);
}
}
//attrValues[ATTIDX_OTHERVALUES] = otherValues;
// Check that minOccurs isn't greater than maxOccurs.
// p-props-correct 2.1
// maxOccurLimit is only check in secure mode
// The maxOccurs restriction no longer applies to elements
// and wildcards in a sequence in which they are the only
// particle. These are now validated using a constant
// space algorithm. The restriction still applies to all
// other cases.
// Determine if constant-space algorithm can be applied
final boolean optimize =
if (!optimize) {
//Revisit :: IMO this is not right place to check
// maxOccurNodeLimit.
if (max > maxOccurNodeLimit) {
// reset max values in case processing continues on error
//new Integer(maxOccurNodeLimit);
}
}
}
reportSchemaError ("p-props-correct.2.1",
element);
}
}
}
return attrValues;
}
return null;
// To validate these types, we don't actually need to normalize the
// strings. We only need to remove the whitespace from both ends.
// In some special cases (list types), StringTokenizer can correctly
// process the un-normalized whitespace.
int choice;
switch (dvIndex) {
case DT_BOOLEAN:
} else {
throw new InvalidDatatypeValueException("cvc-datatype-valid.1.2.1", new Object[]{value, "boolean"});
}
break;
case DT_NONNEGINT:
try {
} catch (NumberFormatException e) {
throw new InvalidDatatypeValueException("cvc-datatype-valid.1.2.1", new Object[]{value, "nonNegativeInteger"});
}
throw new InvalidDatatypeValueException("cvc-datatype-valid.1.2.1", new Object[]{value, "nonNegativeInteger"});
break;
case DT_POSINT:
try {
} catch (NumberFormatException e) {
throw new InvalidDatatypeValueException("cvc-datatype-valid.1.2.1", new Object[]{value, "positiveInteger"});
}
throw new InvalidDatatypeValueException("cvc-datatype-valid.1.2.1", new Object[]{value, "positiveInteger"});
break;
case DT_BLOCK:
// block = (#all | List of (extension | restriction | substitution))
choice = 0;
}
else {
while (t.hasMoreTokens()) {
}
}
}
else {
throw new InvalidDatatypeValueException("cvc-datatype-valid.1.2.3", new Object[]{value, "(#all | List of (extension | restriction | substitution))"});
}
}
}
break;
case DT_BLOCK1:
case DT_FINAL:
// block = (#all | List of (extension | restriction))
// final = (#all | List of (extension | restriction))
choice = 0;
//choice = SchemaSymbols.EXTENSION|SchemaSymbols.RESTRICTION;
// REVISIT: if #all, then make the result the combination of
// everything: substitution/externsion/restriction/list/union.
// would this be a problem?
// the reason doing so is that when final/blockFinal on <schema>
// is #all, it's not always the same as the conbination of those
// values allowed by final/blockFinal.
// for example, finalDefault="#all" is not always the same as
// finalDefault="extension restriction".
// if finalDefault="#all", final on any simple type would be
// "extension restriction list union".
}
else {
while (t.hasMoreTokens()) {
}
}
else {
throw new InvalidDatatypeValueException("cvc-datatype-valid.1.2.3", new Object[]{value, "(#all | List of (extension | restriction))"});
}
}
}
break;
case DT_FINAL1:
// final = (#all | List of (list | union | restriction))
choice = 0;
//choice = SchemaSymbols.RESTRICTION|SchemaSymbols.LIST|
// SchemaSymbols.UNION;
// REVISIT: if #all, then make the result the combination of
// everything: substitution/externsion/restriction/list/union.
// would this be a problem?
}
else {
while (t.hasMoreTokens()) {
}
}
}
else {
throw new InvalidDatatypeValueException("cvc-datatype-valid.1.2.3", new Object[]{value, "(#all | List of (list | union | restriction))"});
}
}
}
break;
case DT_FINAL2:
// finalDefault = (#all | List of (extension | restriction | list | union))
choice = 0;
//choice = SchemaSymbols.RESTRICTION|SchemaSymbols.LIST|
// SchemaSymbols.UNION;
// REVISIT: if #all, then make the result the combination of
// everything: substitution/externsion/restriction/list/union.
// would this be a problem?
}
else {
while (t.hasMoreTokens()) {
}
}
}
}
else {
throw new InvalidDatatypeValueException("cvc-datatype-valid.1.2.3", new Object[]{value, "(#all | List of (extension | restriction | list | union))"});
}
}
}
break;
case DT_FORM:
// form = (qualified | unqualified)
else
throw new InvalidDatatypeValueException("cvc-enumeration-valid",
break;
case DT_MAXOCCURS:
// maxOccurs = (nonNegativeInteger | unbounded)
} else {
try {
} catch (NumberFormatException e) {
throw new InvalidDatatypeValueException("cvc-datatype-valid.1.2.3", new Object[]{value, "(nonNegativeInteger | unbounded)"});
}
}
break;
case DT_MAXOCCURS1:
// maxOccurs = 1
else
throw new InvalidDatatypeValueException("cvc-enumeration-valid",
break;
case DT_MEMBERTYPES:
// memberTypes = List of QName
memberType = new Vector();
try {
while (t.hasMoreTokens()) {
}
}
catch (InvalidDatatypeValueException ide) {
throw new InvalidDatatypeValueException("cvc-datatype-valid.1.2.2", new Object[]{value, "(List of QName)"});
}
break;
case DT_MINOCCURS1:
// minOccurs = (0 | 1)
else
throw new InvalidDatatypeValueException("cvc-enumeration-valid",
break;
case DT_NAMESPACE:
// namespace = ((##any | ##other) | List of (anyURI | (##targetNamespace | ##local)) )
// ##any
// ##other
} else {
// list
// tokenize
try {
while (tokens.hasMoreTokens()) {
} else {
// we have found namespace URI here
// need to add it to the symbol table
}
//check for duplicate namespaces in the list
}
}
} catch (InvalidDatatypeValueException ide) {
throw new InvalidDatatypeValueException("cvc-datatype-valid.1.2.3", new Object[]{value, "((##any | ##other) | List of (anyURI | (##targetNamespace | ##local)) )"});
}
// convert the vector to an array
}
break;
case DT_PROCESSCONTENTS:
// processContents = (lax | skip | strict)
else
throw new InvalidDatatypeValueException("cvc-enumeration-valid",
break;
case DT_USE:
// use = (optional | prohibited | required)
else
throw new InvalidDatatypeValueException("cvc-enumeration-valid",
break;
case DT_WHITESPACE:
// value = preserve | replace | collapse
else
throw new InvalidDatatypeValueException("cvc-enumeration-valid",
break;
}
return retValue;
}
}
}
// validate attriubtes from non-schema namespaces
// REVISIT: why we store the attributes in this way? why not just a list
// REVISIT: pass the proper element node to reportSchemaError
// for all attributes
// get name, uri, localpart
// find associated grammar
continue;
}
// and get the datatype validator, if there is one
continue;
}
continue;
}
// get all values appeared with this attribute name
// for each of the values
try {
// and validate it using the XSSimpleType
// REVISIT: what would be the proper validation context?
// guess we need to save that in the vectors too.
} catch(InvalidDatatypeValueException ide) {
reportSchemaError ("s4s-att-invalid-value",
null);
}
}
}
}
// normalize the string according to the whiteSpace facet
return content;
char ch;
// when it's replace, just replace #x9, #xa, #xd by #x20
for (int i = 0; i < len; i++) {
else
}
} else {
char ch;
int i;
boolean isLeading = true;
// when it's collapse
for (i = 0; i < len; i++) {
// append real characters, so we passed leading ws
isLeading = false;
}
else {
// for whitespaces, we skip all following ws
for (; i < len-1; i++) {
break;
}
// if it's not a leading or tailing ws, then append a space
}
}
}
}
// the following part implements an attribute-value-array pool.
// when checkAttribute is called, it calls getAvailableArray to get
// an array from the pool; when the caller is done with the array,
// it calls returnAttrArray to return that array to the pool.
// initial size of the array pool. 10 is big enough
// the incremental size of the array pool
// the array pool
// used to clear the returned array
// I think System.arrayCopy is more efficient than setting 35 fields to null
// current position of the array pool (# of arrays not returned)
// get the next available array
// if no array left in the pool, increase the pool size
// increase size
// initialize each *new* array
}
// get the next available one
// clear it from the pool. this is for GC: if a caller forget to
// return the array, we want that array to be GCed.
// to make sure that one array is not returned twice, we use
// the last entry to indicate whether an array is already returned
// now set it to false.
return retArray;
}
// return an array back to the pool
// pop the namespace context
// if 1. the pool is full; 2. the array is null;
// 3. the array is of wrong size; 4. the array is already returned
// then we can't accept this array to be returned
if (fPoolPos == 0 ||
return;
}
// mark this array as returned
// better clear nonschema vector
// and put it into the pool
}
// push the namespace context
// search for new namespace bindings
for (int i = 0; i < length; i++) {
}
}
}
}
class OneAttr {
// name of the attribute
// index of the datatype validator
public int dvIndex;
// whether it's optional, and has default value
public int valueIndex;
// the default value of this attribute
this.valueIndex = valueIndex;
}
}
abstract class Container {
return new LargeContainer(size);
else
return new SmallContainer(size);
}
}
}
}
for (int i = 0; i < pos; i++) {
return values[i];
}
}
return null;
}
}
}
}
return ret;
}
}