org.apache.xerces.impl.xs.traversers.XSDAbstractTraverser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xercesImpl Show documentation
Show all versions of xercesImpl Show documentation
Xerces2 is the next generation of high performance, fully compliant XML parsers in the Apache Xerces family.
This new version of Xerces introduces the Xerces Native Interface (XNI), a complete framework for building
parser components and configurations that is extremely modular and easy to program. The Apache Xerces2 parser is
the reference implementation of XNI but other parser components, configurations, and parsers can be written
using the Xerces Native Interface. For complete design and implementation documents, refer to the XNI Manual.
Xerces2 is a fully conforming XML Schema 1.0 processor. A partial experimental implementation of the XML Schema
1.1 Structures and Datatypes Working Drafts (December 2009) and an experimental implementation of the XML Schema
Definition Language (XSD): Component Designators (SCD) Candidate Recommendation (January 2010) are provided for
evaluation. For more information, refer to the XML Schema page. Xerces2 also provides a complete implementation
of the Document Object Model Level 3 Core and Load/Save W3C Recommendations and provides a complete
implementation of the XML Inclusions (XInclude) W3C Recommendation. It also provides support for OASIS XML
Catalogs v1.1. Xerces2 is able to parse documents written according to the XML 1.1 Recommendation, except that
it does not yet provide an option to enable normalization checking as described in section 2.13 of this
specification. It also handles namespaces according to the XML Namespaces 1.1 Recommendation, and will correctly
serialize XML 1.1 documents if the DOM level 3 load/save APIs are in use.
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.xerces.impl.xs.traversers;
import java.util.Locale;
import java.util.Vector;
import org.apache.xerces.impl.dv.InvalidDatatypeValueException;
import org.apache.xerces.impl.dv.XSFacets;
import org.apache.xerces.impl.dv.XSSimpleType;
import org.apache.xerces.impl.dv.util.Base64;
import org.apache.xerces.impl.dv.xs.XSSimpleTypeDecl;
import org.apache.xerces.impl.validation.ValidationState;
import org.apache.xerces.impl.xs.SchemaGrammar;
import org.apache.xerces.impl.xs.SchemaSymbols;
import org.apache.xerces.impl.xs.XSAnnotationImpl;
import org.apache.xerces.impl.xs.XSAttributeGroupDecl;
import org.apache.xerces.impl.xs.XSAttributeUseImpl;
import org.apache.xerces.impl.xs.XSComplexTypeDecl;
import org.apache.xerces.impl.xs.XSElementDecl;
import org.apache.xerces.impl.xs.XSParticleDecl;
import org.apache.xerces.impl.xs.XSWildcardDecl;
import org.apache.xerces.impl.xs.util.XInt;
import org.apache.xerces.impl.xs.util.XSObjectListImpl;
import org.apache.xerces.util.DOMUtil;
import org.apache.xerces.util.NamespaceSupport;
import org.apache.xerces.util.SymbolTable;
import org.apache.xerces.xni.QName;
import org.apache.xerces.xs.XSAttributeUse;
import org.apache.xerces.xs.XSObjectList;
import org.apache.xerces.xs.XSSimpleTypeDefinition;
import org.apache.xerces.xs.XSTypeDefinition;
import org.w3c.dom.Element;
/**
* Class XSDAbstractTraverser
serves as the base class for all
* other XSD???Traverser
s. It holds the common data and provides
* a unified way to initialize these data.
*
* @xerces.internal
*
* @author Elena Litani, IBM
* @author Rahul Srivastava, Sun Microsystems Inc.
* @author Neeraj Bajaj, Sun Microsystems Inc.
*
* @version $Id: XSDAbstractTraverser.java 1631026 2014-10-11 07:30:37Z mukulg $
*/
abstract class XSDAbstractTraverser {
protected static final String NO_NAME = "(no name)";
// Flags for checkOccurrences to indicate any special
// restrictions on minOccurs and maxOccurs relating to "all".
// NOT_ALL_CONTEXT - not processing an
// PROCESSING_ALL_EL - processing an in an
// GROUP_REF_WITH_ALL - processing reference that contained
// CHILD_OF_GROUP - processing a child of a model group definition
// PROCESSING_ALL_GP - processing an group itself
protected static final int NOT_ALL_CONTEXT = 0;
protected static final int PROCESSING_ALL_EL = 1;
protected static final int GROUP_REF_WITH_ALL = 2;
protected static final int CHILD_OF_GROUP = 4;
protected static final int PROCESSING_ALL_GP = 8;
//Shared data
protected XSDHandler fSchemaHandler = null;
protected SymbolTable fSymbolTable = null;
protected XSAttributeChecker fAttrChecker = null;
protected boolean fValidateAnnotations = false;
// used to validate default/fixed attribute values
ValidationState fValidationState = new ValidationState();
XSDAbstractTraverser (XSDHandler handler,
XSAttributeChecker attrChecker) {
fSchemaHandler = handler;
fAttrChecker = attrChecker;
}
void reset(SymbolTable symbolTable, boolean validateAnnotations, Locale locale) {
fSymbolTable = symbolTable;
fValidateAnnotations = validateAnnotations;
fValidationState.setExtraChecking(false);
fValidationState.setSymbolTable(symbolTable);
fValidationState.setLocale(locale);
}
// traverse the annotation declaration
// REVISIT: how to pass the parentAttrs? as DOM attributes?
// as name/value pairs (string)? in parsed form?
// @return XSAnnotationImpl object
XSAnnotationImpl traverseAnnotationDecl(Element annotationDecl, Object[] parentAttrs,
boolean isGlobal, XSDocumentInfo schemaDoc) {
// General Attribute Checking
Object[] attrValues = fAttrChecker.checkAttributes(annotationDecl, isGlobal, schemaDoc);
fAttrChecker.returnAttrArray(attrValues, schemaDoc);
String contents = DOMUtil.getAnnotation(annotationDecl);
Element child = DOMUtil.getFirstChildElement(annotationDecl);
if (child != null) {
do {
String name = DOMUtil.getLocalName(child);
// the only valid children of "annotation" are
// "appinfo" and "documentation"
if (!((name.equals(SchemaSymbols.ELT_APPINFO)) ||
(name.equals(SchemaSymbols.ELT_DOCUMENTATION)))) {
reportSchemaError("src-annotation", new Object[]{name}, child);
}
else {
// General Attribute Checking
// There is no difference between global or local appinfo/documentation,
// so we assume it's always global.
attrValues = fAttrChecker.checkAttributes(child, true, schemaDoc);
fAttrChecker.returnAttrArray(attrValues, schemaDoc);
}
child = DOMUtil.getNextSiblingElement(child);
}
while (child != null);
}
// if contents was null, must have been some kind of error;
// nothing to contribute to PSVI
if (contents == null) return null;
// find the grammar; fSchemaHandler must be known!
SchemaGrammar grammar = fSchemaHandler.getGrammar(schemaDoc.fTargetNamespace);
// fish out local attributes passed from parent
Vector annotationLocalAttrs = (Vector)parentAttrs[XSAttributeChecker.ATTIDX_NONSCHEMA];
// optimize for case where there are no local attributes
if(annotationLocalAttrs != null && !annotationLocalAttrs.isEmpty()) {
StringBuffer localStrBuffer = new StringBuffer(64);
localStrBuffer.append(" ");
// Vector should contain rawname value pairs
int i = 0;
while (i < annotationLocalAttrs.size()) {
String rawname = (String)annotationLocalAttrs.elementAt(i++);
int colonIndex = rawname.indexOf(':');
String prefix, localpart;
if (colonIndex == -1) {
prefix = "";
localpart = rawname;
}
else {
prefix = rawname.substring(0,colonIndex);
localpart = rawname.substring(colonIndex+1);
}
String uri = schemaDoc.fNamespaceSupport.getURI(fSymbolTable.addSymbol(prefix));
if (annotationDecl.getAttributeNS(uri, localpart).length() != 0) {
i++; // skip the next value, too
continue;
}
localStrBuffer.append(rawname)
.append("=\"");
String value = (String)annotationLocalAttrs.elementAt(i++);
// search for pesky "s and xsFacets.maxLength) {
reportSchemaWarning("FacetsContradict", new Object[]{enumVal, SchemaSymbols.ELT_MAXLENGTH, typeName}, contextNode);
}
}
}
else if (SchemaSymbols.URI_SCHEMAFORSCHEMA.equals(baseValidator.getNamespace()) &&
SchemaSymbols.ATTVAL_BASE64BINARY.equals(baseValidator.getName())) {
for (int enumIdx = 0; enumIdx < enumData.size(); enumIdx++) {
String enumVal = ((String)enumData.get(enumIdx));
byte[] decodedVal = Base64.decode(enumVal);
if (decodedVal != null && (new String(decodedVal)).length() > xsFacets.maxLength) {
reportSchemaWarning("FacetsContradict", new Object[]{enumVal, SchemaSymbols.ELT_MAXLENGTH, typeName}, contextNode);
}
}
}
else {
for (int enumIdx = 0; enumIdx < enumData.size(); enumIdx++) {
String enumVal = ((String)enumData.get(enumIdx));
if (enumVal.length() > xsFacets.maxLength) {
reportSchemaWarning("FacetsContradict", new Object[]{enumVal, SchemaSymbols.ELT_MAXLENGTH, typeName}, contextNode);
}
}
}
} // checkEnumerationAndMaxLengthInconsistency
/*
* Check whether values of xs:minLength and xs:enumeration are consistent. Report a warning message if they are not.
*/
private void checkEnumerationAndMinLengthInconsistency(XSSimpleType baseValidator, Vector enumData, Element contextNode, String typeName) {
if (SchemaSymbols.URI_SCHEMAFORSCHEMA.equals(baseValidator.getNamespace()) &&
SchemaSymbols.ATTVAL_HEXBINARY.equals(baseValidator.getName())) {
for (int enumIdx = 0; enumIdx < enumData.size(); enumIdx++) {
String enumVal = ((String)enumData.get(enumIdx));
if (enumVal.length() / 2 < xsFacets.minLength) {
reportSchemaWarning("FacetsContradict", new Object[]{enumVal, SchemaSymbols.ELT_MINLENGTH, typeName}, contextNode);
}
}
}
else if (SchemaSymbols.URI_SCHEMAFORSCHEMA.equals(baseValidator.getNamespace()) &&
SchemaSymbols.ATTVAL_BASE64BINARY.equals(baseValidator.getName())) {
for (int enumIdx = 0; enumIdx < enumData.size(); enumIdx++) {
String enumVal = ((String)enumData.get(enumIdx));
byte[] decodedVal = Base64.decode(enumVal);
if (decodedVal != null && (new String(decodedVal)).length() < xsFacets.minLength) {
reportSchemaWarning("FacetsContradict", new Object[]{enumVal, SchemaSymbols.ELT_MINLENGTH, typeName}, contextNode);
}
}
}
else {
for (int enumIdx = 0; enumIdx < enumData.size(); enumIdx++) {
String enumVal = ((String)enumData.get(enumIdx));
if (enumVal.length() < xsFacets.minLength) {
reportSchemaWarning("FacetsContradict", new Object[]{enumVal, SchemaSymbols.ELT_MINLENGTH, typeName}, contextNode);
}
}
}
} // checkEnumerationAndMinLengthInconsistency
/*
* Check whether values of xs:length and xs:enumeration are consistent. Report a warning message if they are not.
*/
private void checkEnumerationAndLengthInconsistency(XSSimpleType baseValidator, Vector enumData, Element contextNode, String typeName) {
if (SchemaSymbols.URI_SCHEMAFORSCHEMA.equals(baseValidator.getNamespace()) &&
SchemaSymbols.ATTVAL_HEXBINARY.equals(baseValidator.getName())) {
for (int enumIdx = 0; enumIdx < enumData.size(); enumIdx++) {
String enumVal = ((String)enumData.get(enumIdx));
if (enumVal.length() / 2 != xsFacets.length) {
reportSchemaWarning("FacetsContradict", new Object[]{enumVal, SchemaSymbols.ELT_LENGTH, typeName}, contextNode);
}
}
}
else if (SchemaSymbols.URI_SCHEMAFORSCHEMA.equals(baseValidator.getNamespace()) &&
SchemaSymbols.ATTVAL_BASE64BINARY.equals(baseValidator.getName())) {
for (int enumIdx = 0; enumIdx < enumData.size(); enumIdx++) {
String enumVal = ((String)enumData.get(enumIdx));
byte[] decodedVal = Base64.decode(enumVal);
if (decodedVal != null && (new String(decodedVal)).length() != xsFacets.length) {
reportSchemaWarning("FacetsContradict", new Object[]{enumVal, SchemaSymbols.ELT_LENGTH, typeName}, contextNode);
}
}
}
else {
for (int enumIdx = 0; enumIdx < enumData.size(); enumIdx++) {
String enumVal = ((String)enumData.get(enumIdx));
if (enumVal.length() != xsFacets.length) {
reportSchemaWarning("FacetsContradict", new Object[]{enumVal, SchemaSymbols.ELT_LENGTH, typeName}, contextNode);
}
}
}
} // checkEnumerationAndLengthInconsistency
// return whether QName/NOTATION is part of the given type
private boolean containsQName(XSSimpleType type) {
if (type.getVariety() == XSSimpleType.VARIETY_ATOMIC) {
short primitive = type.getPrimitiveKind();
return (primitive == XSSimpleType.PRIMITIVE_QNAME ||
primitive == XSSimpleType.PRIMITIVE_NOTATION);
}
else if (type.getVariety() == XSSimpleType.VARIETY_LIST) {
return containsQName((XSSimpleType)type.getItemType());
}
else if (type.getVariety() == XSSimpleType.VARIETY_UNION) {
XSObjectList members = type.getMemberTypes();
for (int i = 0; i < members.getLength(); i++) {
if (containsQName((XSSimpleType)members.item(i)))
return true;
}
}
return false;
}
//
// Traverse a set of attribute and attribute group elements
// Needed by complexType and attributeGroup traversal
// This method will return the first non-attribute/attrgrp found
//
Element traverseAttrsAndAttrGrps(Element firstAttr, XSAttributeGroupDecl attrGrp,
XSDocumentInfo schemaDoc, SchemaGrammar grammar,
XSComplexTypeDecl enclosingCT) {
Element child=null;
XSAttributeGroupDecl tempAttrGrp = null;
XSAttributeUseImpl tempAttrUse = null;
XSAttributeUse otherUse = null;
String childName;
for (child=firstAttr; child!=null; child=DOMUtil.getNextSiblingElement(child)) {
childName = DOMUtil.getLocalName(child);
if (childName.equals(SchemaSymbols.ELT_ATTRIBUTE)) {
tempAttrUse = fSchemaHandler.fAttributeTraverser.traverseLocal(child,
schemaDoc,
grammar,
enclosingCT);
if (tempAttrUse == null) continue;
if (tempAttrUse.fUse == SchemaSymbols.USE_PROHIBITED) {
attrGrp.addAttributeUse(tempAttrUse);
continue;
}
otherUse = attrGrp.getAttributeUseNoProhibited(
tempAttrUse.fAttrDecl.getNamespace(),
tempAttrUse.fAttrDecl.getName());
if (otherUse==null) {
String idName = attrGrp.addAttributeUse(tempAttrUse);
if (idName != null) {
String code = (enclosingCT == null) ? "ag-props-correct.3" : "ct-props-correct.5";
String name = (enclosingCT == null) ? attrGrp.fName : enclosingCT.getName();
reportSchemaError(code, new Object[]{name, tempAttrUse.fAttrDecl.getName(), idName}, child);
}
}
else if (otherUse != tempAttrUse) {
String code = (enclosingCT == null) ? "ag-props-correct.2" : "ct-props-correct.4";
String name = (enclosingCT == null) ? attrGrp.fName : enclosingCT.getName();
reportSchemaError(code, new Object[]{name, tempAttrUse.fAttrDecl.getName()}, child);
}
}
else if (childName.equals(SchemaSymbols.ELT_ATTRIBUTEGROUP)) {
//REVISIT: do we need to save some state at this point??
tempAttrGrp = fSchemaHandler.fAttributeGroupTraverser.traverseLocal(
child, schemaDoc, grammar);
if(tempAttrGrp == null ) continue;
XSObjectList attrUseS = tempAttrGrp.getAttributeUses();
XSAttributeUseImpl oneAttrUse;
int attrCount = attrUseS.getLength();
for (int i=0; i, minOccurs attribute
// must be zero or one, and maxOccurs attribute must be one.
// For a complex type definition that contains an or a
// reference a whose model group is an all model group,
// minOccurs and maxOccurs must be one.
if (processingAllEl) {
if (max != 1) {
reportSchemaError("cos-all-limited.2", new Object[]{
(max == SchemaSymbols.OCCURRENCE_UNBOUNDED) ? SchemaSymbols.ATTVAL_UNBOUNDED : Integer.toString(max),
((XSElementDecl)particle.fValue).getName()}, parent);
max = 1;
if (min > 1)
min = 1;
}
}
else if (processingAllGP || groupRefWithAll) {
if (max != 1) {
reportSchemaError("cos-all-limited.1.2", null, parent);
if (min > 1)
min = 1;
max = 1;
}
}
particle.fMinOccurs = min;
particle.fMaxOccurs = max;
return particle;
}
private static String processAttValue(String original) {
final int length = original.length();
// normally, nothing will happen
for (int i = 0; i < length; ++i) {
char currChar = original.charAt(i);
if (currChar == '"' || currChar == '<' || currChar == '&' ||
currChar == 0x09 || currChar == 0x0A || currChar == 0x0D) {
return escapeAttValue(original, i);
}
}
return original;
}
// this is not terribly performant!
private static String escapeAttValue(String original, int from) {
int i;
final int length = original.length();
StringBuffer newVal = new StringBuffer(length);
newVal.append(original.substring(0, from));
for (i = from; i < length; ++i) {
char currChar = original.charAt(i);
if (currChar == '"') {
newVal.append(""");
}
else if (currChar == '<') {
newVal.append("<");
}
else if (currChar == '&') {
newVal.append("&");
}
// Must escape 0x09, 0x0A and 0x0D if they appear in attribute
// value so that they may be round-tripped. They would otherwise
// be transformed to a 0x20 during attribute value normalization.
else if (currChar == 0x09) {
newVal.append(" ");
}
else if (currChar == 0x0A) {
newVal.append("
");
}
else if (currChar == 0x0D) {
newVal.append("
");
}
else {
newVal.append(currChar);
}
}
return newVal.toString();
}
}