/*
 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
 */
/*
 * 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 com.sun.org.apache.xerces.internal.impl.xs.traversers;

import com.sun.org.apache.xerces.internal.impl.dv.InvalidDatatypeValueException;
import com.sun.org.apache.xerces.internal.impl.dv.XSFacets;
import com.sun.org.apache.xerces.internal.impl.dv.XSSimpleType;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import com.sun.org.apache.xerces.internal.impl.dv.xs.XSSimpleTypeDecl;
import com.sun.org.apache.xerces.internal.impl.validation.ValidationState;
import com.sun.org.apache.xerces.internal.impl.xs.SchemaGrammar;
import com.sun.org.apache.xerces.internal.impl.xs.SchemaSymbols;
import com.sun.org.apache.xerces.internal.impl.xs.XSAnnotationImpl;
import com.sun.org.apache.xerces.internal.impl.xs.XSAttributeGroupDecl;
import com.sun.org.apache.xerces.internal.impl.xs.XSAttributeUseImpl;
import com.sun.org.apache.xerces.internal.impl.xs.XSComplexTypeDecl;
import com.sun.org.apache.xerces.internal.impl.xs.XSElementDecl;
import com.sun.org.apache.xerces.internal.impl.xs.XSParticleDecl;
import com.sun.org.apache.xerces.internal.impl.xs.XSWildcardDecl;
import com.sun.org.apache.xerces.internal.impl.xs.util.XInt;
import com.sun.org.apache.xerces.internal.impl.xs.util.XSObjectListImpl;
import com.sun.org.apache.xerces.internal.util.DOMUtil;
import com.sun.org.apache.xerces.internal.util.NamespaceSupport;
import com.sun.org.apache.xerces.internal.util.SymbolTable;
import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
import com.sun.org.apache.xerces.internal.xni.QName;
import com.sun.org.apache.xerces.internal.xs.XSAttributeUse;
import com.sun.org.apache.xerces.internal.xs.XSObjectList;
import com.sun.org.apache.xerces.internal.xs.XSSimpleTypeDefinition;
import com.sun.org.apache.xerces.internal.xs.XSTypeDefinition;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.w3c.dom.Element;

Class XSDAbstractTraverser serves as the base class for all other XSD???Traversers. It holds the common data and provide a unified way to initialize these data.
Author:Elena Litani, IBM, Rahul Srivastava, Sun Microsystems Inc., Neeraj Bajaj, Sun Microsystems Inc.
@xerces.internal
@LastModified: Oct 2017
/** * Class <code>XSDAbstractTraverser</code> serves as the base class for all * other <code>XSD???Traverser</code>s. It holds the common data and provide * 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. * * @LastModified: Oct 2017 */
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 <all> // PROCESSING_ALL_EL - processing an <element> in an <all> // GROUP_REF_WITH_ALL - processing <group> reference that contained <all> // CHILD_OF_GROUP - processing a child of a model group definition // PROCESSING_ALL_GP - processing an <all> 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 @SuppressWarnings("unchecked") List<String> annotationLocalAttrs = (ArrayList<String>)parentAttrs[XSAttributeChecker.ATTIDX_NONSCHEMA]; // optimize for case where there are no local attributes if(annotationLocalAttrs != null && !annotationLocalAttrs.isEmpty()) { StringBuilder localStrBuffer = new StringBuilder(64); localStrBuffer.append(" "); //ArrayList<>should contain rawname value pairs int i = 0; while (i < annotationLocalAttrs.size()) { String rawname = annotationLocalAttrs.get(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 = annotationLocalAttrs.get(i++); // search for pesky "s and <s within attr value: value = processAttValue(value); localStrBuffer.append(value) .append("\" "); } // and now splice it into place; immediately after the annotation token, for simplicity's sake StringBuilder contentBuffer = new StringBuilder(contents.length() + localStrBuffer.length()); int annotationTokenEnd = contents.indexOf(SchemaSymbols.ELT_ANNOTATION); // annotation must occur somewhere or we're in big trouble... if(annotationTokenEnd == -1) return null; annotationTokenEnd += SchemaSymbols.ELT_ANNOTATION.length(); contentBuffer.append(contents.substring(0,annotationTokenEnd)); contentBuffer.append(localStrBuffer.toString()); contentBuffer.append(contents.substring(annotationTokenEnd, contents.length())); final String annotation = contentBuffer.toString(); if (fValidateAnnotations) { schemaDoc.addAnnotation(new XSAnnotationInfo(annotation, annotationDecl)); } return new XSAnnotationImpl(annotation, grammar); } else { if (fValidateAnnotations) { schemaDoc.addAnnotation(new XSAnnotationInfo(contents, annotationDecl)); } return new XSAnnotationImpl(contents, grammar); } } XSAnnotationImpl traverseSyntheticAnnotation(Element annotationParent, String initialContent, Object[] parentAttrs, boolean isGlobal, XSDocumentInfo schemaDoc) { String contents = initialContent; // find the grammar; fSchemaHandler must be known! SchemaGrammar grammar = fSchemaHandler.getGrammar(schemaDoc.fTargetNamespace); // fish out local attributes passed from parent @SuppressWarnings("unchecked") List<String> annotationLocalAttrs = (ArrayList<String>)parentAttrs[XSAttributeChecker.ATTIDX_NONSCHEMA]; // optimize for case where there are no local attributes if (annotationLocalAttrs != null && !annotationLocalAttrs.isEmpty()) { StringBuilder localStrBuffer = new StringBuilder(64); localStrBuffer.append(" "); //ArrayList<>should contain rawname value pairs int i = 0; while (i < annotationLocalAttrs.size()) { String rawname = annotationLocalAttrs.get(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)); localStrBuffer.append(rawname) .append("=\""); String value = annotationLocalAttrs.get(i++); // search for pesky "s and <s within attr value: value = processAttValue(value); localStrBuffer.append(value) .append("\" "); } // and now splice it into place; immediately after the annotation token, for simplicity's sake StringBuilder contentBuffer = new StringBuilder(contents.length() + localStrBuffer.length()); int annotationTokenEnd = contents.indexOf(SchemaSymbols.ELT_ANNOTATION); // annotation must occur somewhere or we're in big trouble... if(annotationTokenEnd == -1) return null; annotationTokenEnd += SchemaSymbols.ELT_ANNOTATION.length(); contentBuffer.append(contents.substring(0,annotationTokenEnd)); contentBuffer.append(localStrBuffer.toString()); contentBuffer.append(contents.substring(annotationTokenEnd, contents.length())); final String annotation = contentBuffer.toString(); if (fValidateAnnotations) { schemaDoc.addAnnotation(new XSAnnotationInfo(annotation, annotationParent)); } return new XSAnnotationImpl(annotation, grammar); } else { if (fValidateAnnotations) { schemaDoc.addAnnotation(new XSAnnotationInfo(contents, annotationParent)); } return new XSAnnotationImpl(contents, grammar); } } // the QName simple type used to resolve qnames private static final XSSimpleType fQNameDV = (XSSimpleType)SchemaGrammar.SG_SchemaNS.getGlobalTypeDecl(SchemaSymbols.ATTVAL_QNAME); // Temp data structures to be re-used in traversing facets private StringBuilder fPattern = new StringBuilder(); private final XSFacets xsFacets = new XSFacets(); static final class FacetInfo { final XSFacets facetdata; final Element nodeAfterFacets; final short fPresentFacets; final short fFixedFacets; FacetInfo(XSFacets facets, Element nodeAfterFacets, short presentFacets, short fixedFacets) { facetdata = facets; this.nodeAfterFacets = nodeAfterFacets; fPresentFacets = presentFacets; fFixedFacets = fixedFacets; } } FacetInfo traverseFacets(Element content, XSTypeDefinition typeDef, XSSimpleType baseValidator, XSDocumentInfo schemaDoc) { short facetsPresent = 0 ; short facetsFixed = 0; // facets that have fixed="true" String facet; boolean hasQName = containsQName(baseValidator); List<String> enumData = null; XSObjectListImpl enumAnnotations = null; XSObjectListImpl patternAnnotations = null; List<NamespaceContext> enumNSDecls = hasQName ? new ArrayList<>() : null; int currentFacet = 0; xsFacets.reset(); boolean seenPattern = false; Element contextNode = (Element)content.getParentNode(); boolean hasLengthFacet = false, hasMinLengthFacet = false, hasMaxLengthFacet = false; while (content != null) { // General Attribute Checking Object[] attrs = null; facet = DOMUtil.getLocalName(content); if (facet.equals(SchemaSymbols.ELT_ENUMERATION)) { attrs = fAttrChecker.checkAttributes(content, false, schemaDoc, hasQName); String enumVal = (String)attrs[XSAttributeChecker.ATTIDX_VALUE]; // The facet can't be used if the value is missing. Ignore // this facet element. if (enumVal == null) { reportSchemaError("s4s-att-must-appear", new Object[]{SchemaSymbols.ELT_ENUMERATION, SchemaSymbols.ATT_VALUE}, content); fAttrChecker.returnAttrArray (attrs, schemaDoc); content = DOMUtil.getNextSiblingElement(content); continue; } NamespaceSupport nsDecls = (NamespaceSupport)attrs[XSAttributeChecker.ATTIDX_ENUMNSDECLS]; // for NOTATION types, need to check whether there is a notation // declared with the same name as the enumeration value. if (baseValidator.getVariety() == XSSimpleType.VARIETY_ATOMIC && baseValidator.getPrimitiveKind() == XSSimpleType.PRIMITIVE_NOTATION) { // need to use the namespace context returned from checkAttributes schemaDoc.fValidationContext.setNamespaceSupport(nsDecls); Object notation = null; try{ QName temp = (QName)fQNameDV.validate(enumVal, schemaDoc.fValidationContext, null); // try to get the notation decl. if failed, getGlobalDecl // reports an error, so we don't need to report one again. notation = fSchemaHandler.getGlobalDecl(schemaDoc, XSDHandler.NOTATION_TYPE, temp, content); }catch(InvalidDatatypeValueException ex){ reportSchemaError(ex.getKey(), ex.getArgs(), content); } if (notation == null) { // Either the QName value is invalid, or it doens't // resolve to a notation declaration. // Ignore this facet, to avoid instance validation problems fAttrChecker.returnAttrArray (attrs, schemaDoc); content = DOMUtil.getNextSiblingElement(content); continue; } // restore to the normal namespace context schemaDoc.fValidationContext.setNamespaceSupport(schemaDoc.fNamespaceSupport); } if (enumData == null){ enumData = new ArrayList<>(); enumAnnotations = new XSObjectListImpl(); } enumData.add(enumVal); enumAnnotations.addXSObject(null); if (hasQName) enumNSDecls.add(nsDecls); Element child = DOMUtil.getFirstChildElement( content ); if (child != null && DOMUtil.getLocalName(child).equals(SchemaSymbols.ELT_ANNOTATION)) { // traverse annotation if any enumAnnotations.addXSObject(enumAnnotations.getLength()-1,traverseAnnotationDecl(child, attrs, false, schemaDoc)); child = DOMUtil.getNextSiblingElement(child); } else { String text = DOMUtil.getSyntheticAnnotation(content); if (text != null) { enumAnnotations.addXSObject(enumAnnotations.getLength()-1, traverseSyntheticAnnotation(content, text, attrs, false, schemaDoc)); } } if (child !=null) { reportSchemaError("s4s-elt-must-match.1", new Object[]{"enumeration", "(annotation?)", DOMUtil.getLocalName(child)}, child); } } else if (facet.equals(SchemaSymbols.ELT_PATTERN)) { attrs = fAttrChecker.checkAttributes(content, false, schemaDoc); String patternVal = (String)attrs[XSAttributeChecker.ATTIDX_VALUE]; // The facet can't be used if the value is missing. Ignore // this facet element. if (patternVal == null) { reportSchemaError("s4s-att-must-appear", new Object[]{SchemaSymbols.ELT_PATTERN, SchemaSymbols.ATT_VALUE}, content); fAttrChecker.returnAttrArray (attrs, schemaDoc); content = DOMUtil.getNextSiblingElement(content); continue; } seenPattern = true; if (fPattern.length() == 0) { fPattern.append(patternVal); } else { // --------------------------------------------- //datatypes: 5.2.4 pattern: src-multiple-pattern // --------------------------------------------- fPattern.append("|"); fPattern.append(patternVal); } Element child = DOMUtil.getFirstChildElement( content ); if (child != null && DOMUtil.getLocalName(child).equals(SchemaSymbols.ELT_ANNOTATION)) { // traverse annotation if any if (patternAnnotations == null){ patternAnnotations = new XSObjectListImpl(); } patternAnnotations.addXSObject(traverseAnnotationDecl(child, attrs, false, schemaDoc)); child = DOMUtil.getNextSiblingElement(child); } else { String text = DOMUtil.getSyntheticAnnotation(content); if (text != null) { if (patternAnnotations == null){ patternAnnotations = new XSObjectListImpl(); } patternAnnotations.addXSObject(traverseSyntheticAnnotation(content, text, attrs, false, schemaDoc)); } } if (child !=null) { reportSchemaError("s4s-elt-must-match.1", new Object[]{"pattern", "(annotation?)", DOMUtil.getLocalName(child)}, child); } } else { if (facet.equals(SchemaSymbols.ELT_MINLENGTH)) { currentFacet = XSSimpleType.FACET_MINLENGTH; } else if (facet.equals(SchemaSymbols.ELT_MAXLENGTH)) { currentFacet = XSSimpleType.FACET_MAXLENGTH; } else if (facet.equals(SchemaSymbols.ELT_MAXEXCLUSIVE)) { currentFacet = XSSimpleType.FACET_MAXEXCLUSIVE; } else if (facet.equals(SchemaSymbols.ELT_MAXINCLUSIVE)) { currentFacet = XSSimpleType.FACET_MAXINCLUSIVE; } else if (facet.equals(SchemaSymbols.ELT_MINEXCLUSIVE)) { currentFacet = XSSimpleType.FACET_MINEXCLUSIVE; } else if (facet.equals(SchemaSymbols.ELT_MININCLUSIVE)) { currentFacet = XSSimpleType.FACET_MININCLUSIVE; } else if (facet.equals(SchemaSymbols.ELT_TOTALDIGITS)) { currentFacet = XSSimpleType.FACET_TOTALDIGITS; } else if (facet.equals(SchemaSymbols.ELT_FRACTIONDIGITS)) { currentFacet = XSSimpleType.FACET_FRACTIONDIGITS; } else if (facet.equals(SchemaSymbols.ELT_WHITESPACE)) { currentFacet = XSSimpleType.FACET_WHITESPACE; } else if (facet.equals(SchemaSymbols.ELT_LENGTH)) { currentFacet = XSSimpleType.FACET_LENGTH; } else { break; // a non-facet } attrs = fAttrChecker.checkAttributes(content, false, schemaDoc); // check for duplicate facets if ((facetsPresent & currentFacet) != 0) { // Ignore this facet, to avoid corrupting the previous facet reportSchemaError("src-single-facet-value", new Object[]{facet}, content); fAttrChecker.returnAttrArray (attrs, schemaDoc); content = DOMUtil.getNextSiblingElement(content); continue; } // The facet can't be used if the value is missing. Ignore // this facet element. if (attrs[XSAttributeChecker.ATTIDX_VALUE] == null) { // Report an error if the "value" attribute is missing. // If it's not missing, then its value is invalid, and an // error should have already been reported by the // attribute checker. if (content.getAttributeNodeNS(null, "value") == null) { reportSchemaError("s4s-att-must-appear", new Object[]{content.getLocalName(), SchemaSymbols.ATT_VALUE}, content); } fAttrChecker.returnAttrArray (attrs, schemaDoc); content = DOMUtil.getNextSiblingElement(content); continue; } facetsPresent |= currentFacet; // check for fixed facet if (((Boolean)attrs[XSAttributeChecker.ATTIDX_FIXED]).booleanValue()) { facetsFixed |= currentFacet; } switch (currentFacet) { case XSSimpleType.FACET_MINLENGTH: xsFacets.minLength = ((XInt)attrs[XSAttributeChecker.ATTIDX_VALUE]).intValue(); hasMinLengthFacet = true; break; case XSSimpleType.FACET_MAXLENGTH: xsFacets.maxLength = ((XInt)attrs[XSAttributeChecker.ATTIDX_VALUE]).intValue(); hasMaxLengthFacet = true; break; case XSSimpleType.FACET_MAXEXCLUSIVE: xsFacets.maxExclusive = (String)attrs[XSAttributeChecker.ATTIDX_VALUE]; break; case XSSimpleType.FACET_MAXINCLUSIVE: xsFacets.maxInclusive = (String)attrs[XSAttributeChecker.ATTIDX_VALUE]; break; case XSSimpleType.FACET_MINEXCLUSIVE: xsFacets.minExclusive = (String)attrs[XSAttributeChecker.ATTIDX_VALUE]; break; case XSSimpleType.FACET_MININCLUSIVE: xsFacets.minInclusive = (String)attrs[XSAttributeChecker.ATTIDX_VALUE]; break; case XSSimpleType.FACET_TOTALDIGITS: xsFacets.totalDigits = ((XInt)attrs[XSAttributeChecker.ATTIDX_VALUE]).intValue(); break; case XSSimpleType.FACET_FRACTIONDIGITS: xsFacets.fractionDigits = ((XInt)attrs[XSAttributeChecker.ATTIDX_VALUE]).intValue(); break; case XSSimpleType.FACET_WHITESPACE: xsFacets.whiteSpace = ((XInt)attrs[XSAttributeChecker.ATTIDX_VALUE]).shortValue(); break; case XSSimpleType.FACET_LENGTH: xsFacets.length = ((XInt)attrs[XSAttributeChecker.ATTIDX_VALUE]).intValue(); hasLengthFacet = true; break; } Element child = DOMUtil.getFirstChildElement( content ); XSAnnotationImpl annotation = null; if (child != null && DOMUtil.getLocalName(child).equals(SchemaSymbols.ELT_ANNOTATION)) { // traverse annotation if any annotation = traverseAnnotationDecl(child, attrs, false, schemaDoc); child = DOMUtil.getNextSiblingElement(child); } else { String text = DOMUtil.getSyntheticAnnotation(content); if (text != null) { annotation = traverseSyntheticAnnotation(content, text, attrs, false, schemaDoc); } } switch (currentFacet) { case XSSimpleType.FACET_MINLENGTH: xsFacets.minLengthAnnotation = annotation; break; case XSSimpleType.FACET_MAXLENGTH: xsFacets.maxLengthAnnotation = annotation; break; case XSSimpleType.FACET_MAXEXCLUSIVE: xsFacets.maxExclusiveAnnotation = annotation; break; case XSSimpleType.FACET_MAXINCLUSIVE: xsFacets.maxInclusiveAnnotation = annotation; break; case XSSimpleType.FACET_MINEXCLUSIVE: xsFacets.minExclusiveAnnotation = annotation; break; case XSSimpleType.FACET_MININCLUSIVE: xsFacets.minInclusiveAnnotation = annotation; break; case XSSimpleType.FACET_TOTALDIGITS: xsFacets.totalDigitsAnnotation = annotation; break; case XSSimpleType.FACET_FRACTIONDIGITS: xsFacets.fractionDigitsAnnotation = annotation; break; case XSSimpleType.FACET_WHITESPACE: xsFacets.whiteSpaceAnnotation = annotation; break; case XSSimpleType.FACET_LENGTH: xsFacets.lengthAnnotation = annotation; break; } if (child != null) { reportSchemaError("s4s-elt-must-match.1", new Object[]{facet, "(annotation?)", DOMUtil.getLocalName(child)}, child); } } fAttrChecker.returnAttrArray (attrs, schemaDoc); content = DOMUtil.getNextSiblingElement(content); } if (enumData !=null) { facetsPresent |= XSSimpleType.FACET_ENUMERATION; xsFacets.enumeration = enumData; xsFacets.enumNSDecls = enumNSDecls; xsFacets.enumAnnotations = enumAnnotations; } if (seenPattern) { facetsPresent |= XSSimpleType.FACET_PATTERN; xsFacets.pattern = fPattern.toString(); xsFacets.patternAnnotations = patternAnnotations; } fPattern.setLength(0); // check if length, minLength and maxLength facets contradict with enumeration facets. // currently considers the case when the baseValidator is a built-in type. if (enumData != null) { if (hasLengthFacet) { checkEnumerationAndLengthInconsistency(baseValidator, enumData, contextNode, getSchemaTypeName(typeDef)); } if (hasMinLengthFacet) { checkEnumerationAndMinLengthInconsistency(baseValidator, enumData, contextNode, getSchemaTypeName(typeDef)); } if (hasMaxLengthFacet) { checkEnumerationAndMaxLengthInconsistency(baseValidator, enumData, contextNode, getSchemaTypeName(typeDef)); } } return new FacetInfo(xsFacets, content, facetsPresent, facetsFixed); } /* * Get name of an XSD type definition as a string value (which will typically be the value of "name" attribute of a * type definition, or an internal name determined by the validator for anonymous types). */ public static String getSchemaTypeName(XSTypeDefinition typeDefn) { String typeNameStr = ""; if (typeDefn instanceof XSSimpleTypeDefinition) { typeNameStr = ((XSSimpleTypeDecl) typeDefn).getTypeName(); } else { typeNameStr = ((XSComplexTypeDecl) typeDefn).getTypeName(); } return typeNameStr; } // getSchemaTypeName /* * Check whether values of xs:maxLength and xs:enumeration are consistent. Report a warning message if they are not. */ private void checkEnumerationAndMaxLengthInconsistency(XSSimpleType baseValidator, List<String> 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 = (enumData.get(enumIdx)); if (enumVal.length() / 2 > 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 = (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 = (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, List<String> 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 = (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 = (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 = (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, List<String> 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 = (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 = (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 = (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<attrCount; i++) { oneAttrUse = (XSAttributeUseImpl)attrUseS.item(i); if (oneAttrUse.fUse == SchemaSymbols.USE_PROHIBITED) { attrGrp.addAttributeUse(oneAttrUse); continue; } otherUse = attrGrp.getAttributeUseNoProhibited( oneAttrUse.fAttrDecl.getNamespace(), oneAttrUse.fAttrDecl.getName()); if (otherUse==null) { String idName = attrGrp.addAttributeUse(oneAttrUse); 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, oneAttrUse.fAttrDecl.getName(), idName}, child); } } else if (oneAttrUse != otherUse) { 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, oneAttrUse.fAttrDecl.getName()}, child); } } if (tempAttrGrp.fAttributeWC != null) { if (attrGrp.fAttributeWC == null) { attrGrp.fAttributeWC = tempAttrGrp.fAttributeWC; } // perform intersection of attribute wildcard else { attrGrp.fAttributeWC = attrGrp.fAttributeWC. performIntersectionWith(tempAttrGrp.fAttributeWC, attrGrp.fAttributeWC.fProcessContents); if (attrGrp.fAttributeWC == null) { String code = (enclosingCT == null) ? "src-attribute_group.2" : "src-ct.4"; String name = (enclosingCT == null) ? attrGrp.fName : enclosingCT.getName(); reportSchemaError(code, new Object[]{name}, child); } } } } else break; } // for if (child != null) { childName = DOMUtil.getLocalName(child); if (childName.equals(SchemaSymbols.ELT_ANYATTRIBUTE)) { XSWildcardDecl tempAttrWC = fSchemaHandler.fWildCardTraverser. traverseAnyAttribute(child, schemaDoc, grammar); if (attrGrp.fAttributeWC == null) { attrGrp.fAttributeWC = tempAttrWC; } // perform intersection of attribute wildcard else { attrGrp.fAttributeWC = tempAttrWC. performIntersectionWith(attrGrp.fAttributeWC, tempAttrWC.fProcessContents); if (attrGrp.fAttributeWC == null) { String code = (enclosingCT == null) ? "src-attribute_group.2" : "src-ct.4"; String name = (enclosingCT == null) ? attrGrp.fName : enclosingCT.getName(); reportSchemaError(code, new Object[]{name}, child); } } child = DOMUtil.getNextSiblingElement(child); } } // Success return child; } void reportSchemaError (String key, Object[] args, Element ele) { fSchemaHandler.reportSchemaError(key, args, ele); } void reportSchemaWarning (String key, Object[] args, Element ele) { fSchemaHandler.reportSchemaWarning(key, args, ele); }
Element/Attribute traversers call this method to check whether the type is NOTATION without enumeration facet
/** * Element/Attribute traversers call this method to check whether * the type is NOTATION without enumeration facet */
void checkNotationType(String refName, XSTypeDefinition typeDecl, Element elem) { if (typeDecl.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE && ((XSSimpleType)typeDecl).getVariety() == XSSimpleType.VARIETY_ATOMIC && ((XSSimpleType)typeDecl).getPrimitiveKind() == XSSimpleType.PRIMITIVE_NOTATION) { if ((((XSSimpleType)typeDecl).getDefinedFacets() & XSSimpleType.FACET_ENUMERATION) == 0) { reportSchemaError("enumeration-required-notation", new Object[]{typeDecl.getName(), refName, DOMUtil.getLocalName(elem)}, elem); } } } // Checks constraints for minOccurs, maxOccurs protected XSParticleDecl checkOccurrences(XSParticleDecl particle, String particleName, Element parent, int allContextFlags, long defaultVals) { int min = particle.fMinOccurs; int max = particle.fMaxOccurs; boolean defaultMin = (defaultVals & (1 << XSAttributeChecker.ATTIDX_MINOCCURS)) != 0; boolean defaultMax = (defaultVals & (1 << XSAttributeChecker.ATTIDX_MAXOCCURS)) != 0; boolean processingAllEl = ((allContextFlags & PROCESSING_ALL_EL) != 0); boolean processingAllGP = ((allContextFlags & PROCESSING_ALL_GP) != 0); boolean groupRefWithAll = ((allContextFlags & GROUP_REF_WITH_ALL) != 0); boolean isGroupChild = ((allContextFlags & CHILD_OF_GROUP) != 0); // Neither minOccurs nor maxOccurs may be specified // for the child of a model group definition. if (isGroupChild) { if (!defaultMin) { Object[] args = new Object[]{particleName, "minOccurs"}; reportSchemaError("s4s-att-not-allowed", args, parent); min = 1; } if (!defaultMax) { Object[] args = new Object[]{particleName, "maxOccurs"}; reportSchemaError("s4s-att-not-allowed", args, parent); max = 1; } } // If minOccurs=maxOccurs=0, no component is specified if (min == 0 && max== 0) { particle.fType = XSParticleDecl.PARTICLE_EMPTY; return null; } // For the elements referenced in an <all>, minOccurs attribute // must be zero or one, and maxOccurs attribute must be one. // For a complex type definition that contains an <all> or a // reference a <group> 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(); StringBuilder newVal = new StringBuilder(length); newVal.append(original.substring(0, from)); for (i = from; i < length; ++i) { char currChar = original.charAt(i); if (currChar == '"') { newVal.append("&quot;"); } else if (currChar == '<') { newVal.append("&lt;"); } else if (currChar == '&') { newVal.append("&amp;"); } // 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("&#x9;"); } else if (currChar == 0x0A) { newVal.append("&#xA;"); } else if (currChar == 0x0D) { newVal.append("&#xD;"); } else { newVal.append(currChar); } } return newVal.toString(); } }