/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999,2000,2001 The Apache Software Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xerces" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 1999, International
* Business Machines, Inc., http://www.apache.org. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.xerces.validators.common;
import org.apache.xerces.framework.XMLAttrList;
import org.apache.xerces.framework.XMLContentSpec;
import org.apache.xerces.framework.XMLDocumentHandler;
import org.apache.xerces.framework.XMLDocumentScanner;
import org.apache.xerces.framework.XMLErrorReporter;
import org.apache.xerces.readers.DefaultEntityHandler;
import org.apache.xerces.readers.XMLEntityHandler;
import org.apache.xerces.utils.ChunkyCharArray;
import org.apache.xerces.utils.Hash2intTable;
import org.apache.xerces.utils.IntStack;
import org.apache.xerces.utils.NamespacesScope;
import org.apache.xerces.utils.QName;
import org.apache.xerces.utils.StringPool;
import org.apache.xerces.utils.XMLCharacterProperties;
import org.apache.xerces.utils.XMLMessages;
import org.apache.xerces.utils.ImplementationMessages;
import org.apache.xerces.parsers.DOMParser;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.xml.sax.EntityResolver;
import org.xml.sax.Locator;
import org.xml.sax.helpers.LocatorImpl;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.Vector;
import org.apache.xerces.validators.dtd.DTDGrammar;
import org.apache.xerces.validators.schema.SubstitutionGroupComparator;
import org.apache.xerces.validators.schema.SchemaGrammar;
import org.apache.xerces.validators.schema.SchemaMessageProvider;
import org.apache.xerces.validators.schema.SchemaSymbols;
import org.apache.xerces.validators.schema.TraverseSchema;
import org.apache.xerces.validators.schema.identity.Field;
import org.apache.xerces.validators.schema.identity.FieldActivator;
import org.apache.xerces.validators.schema.identity.IdentityConstraint;
import org.apache.xerces.validators.schema.identity.IDValue;
import org.apache.xerces.validators.schema.identity.Key;
import org.apache.xerces.validators.schema.identity.KeyRef;
import org.apache.xerces.validators.schema.identity.Selector;
import org.apache.xerces.validators.schema.identity.Unique;
import org.apache.xerces.validators.schema.identity.ValueStore;
import org.apache.xerces.validators.schema.identity.XPathMatcher;
import org.apache.xerces.validators.datatype.DatatypeValidatorFactoryImpl;
import org.apache.xerces.validators.datatype.DatatypeValidator;
import org.apache.xerces.validators.datatype.InvalidDatatypeValueException;
import org.apache.xerces.validators.datatype.StateMessageDatatype;
import org.apache.xerces.validators.datatype.IDREFDatatypeValidator;
import org.apache.xerces.validators.datatype.IDDatatypeValidator;
import org.apache.xerces.validators.datatype.ENTITYDatatypeValidator;
import org.apache.xerces.validators.datatype.NOTATIONDatatypeValidator;
import org.apache.xerces.validators.datatype.UnionDatatypeValidator;
/**
* This class is the super all-in-one validator used by the parser.
*
* @version $Id: XMLValidator.java,v 1.152 2001/05/18 20:31:14 neilg Exp $
*/
public final class XMLValidator
implements DefaultEntityHandler.EventHandler,
XMLEntityHandler.CharDataHandler,
XMLDocumentScanner.EventHandler,
NamespacesScope.NamespacesHandler,
FieldActivator // for identity constraints
{
//
// Constants
//
// debugging
private static final boolean PRINT_EXCEPTION_STACK_TRACE = false;
private static final boolean DEBUG_PRINT_ATTRIBUTES = false;
private static final boolean DEBUG_PRINT_CONTENT = false;
private static final boolean DEBUG_SCHEMA_VALIDATION = false;
private static final boolean DEBUG_ELEMENT_CHILDREN = false;
/** Compile to true to debug identity constraints. */
protected static final boolean DEBUG_IDENTITY_CONSTRAINTS = false;
/** Compile to true to debug value stores. */
protected static final boolean DEBUG_VALUE_STORES = false;
// Chunk size constants
private static final int CHUNK_SHIFT = 8; // 2^8 = 256
private static final int CHUNK_SIZE = (1 << CHUNK_SHIFT);
private static final int CHUNK_MASK = CHUNK_SIZE - 1;
private static final int INITIAL_CHUNK_COUNT = (1 << (10 - CHUNK_SHIFT)); // 2^10 = 1k
private Hashtable fIdDefs = null;
private StateMessageDatatype fStoreIDRef = new StateMessageDatatype() {
private Hashtable fIdDefs;
public Object getDatatypeObject(){
return(Object) fIdDefs;
}
public int getDatatypeState(){
return IDREFDatatypeValidator.IDREF_STORE;
}
public void setDatatypeObject( Object data ){
fIdDefs = (Hashtable) data;
}
};
private StateMessageDatatype fResetID = new StateMessageDatatype() {
public Object getDatatypeObject(){
return(Object) null;
}
public int getDatatypeState(){
return IDDatatypeValidator.ID_CLEAR;
}
public void setDatatypeObject( Object data ){
}
};
private StateMessageDatatype fResetIDRef = new StateMessageDatatype() {
public Object getDatatypeObject(){
return(Object) null;
}
public int getDatatypeState(){
return IDREFDatatypeValidator.IDREF_CLEAR;
}
public void setDatatypeObject( Object data ){
}
};
private StateMessageDatatype fValidateIDRef = new StateMessageDatatype() {
public Object getDatatypeObject(){
return(Object) null;
}
public int getDatatypeState(){
return IDREFDatatypeValidator.IDREF_VALIDATE;
}
public void setDatatypeObject( Object data ){
}
};
private StateMessageDatatype fValidateENTITYMsg = new StateMessageDatatype() {
private Object packagedMessage = null;
public Object getDatatypeObject(){
return packagedMessage;
}
public int getDatatypeState(){
return ENTITYDatatypeValidator.ENTITY_INITIALIZE;//No state
}
public void setDatatypeObject( Object data ){
packagedMessage = data;// Set Entity Handler
}
};
/*
private StateMessageDatatype fValidateNOTATIONMsg = new StateMessageDatatype() {
private Object packagedMessage = null;
public Object getDatatypeObject(){
return packagedMessage;
}
public int getDatatypeState(){
return NOTATIONDatatypeValidator.ENTITY_INITIALIZE;//No state
}
public void setDatatypeObject( Object data ){
packagedMessage = data;// Set Entity Handler
}
};
*/
//
// Data
//
// REVISIT: The data should be regrouped and re-organized so that
// it's easier to find a meaningful field.
// attribute validators
private AttributeValidator fAttValidatorNOTATION = new AttValidatorNOTATION();
private AttributeValidator fAttValidatorENUMERATION = new AttValidatorENUMERATION();
private AttributeValidator fAttValidatorDATATYPE = null;
// Package access for use by AttributeValidator classes.
StringPool fStringPool = null;
boolean fValidating = false;
boolean fInElementContent = false;
int fStandaloneReader = -1;
// settings
private boolean fValidationEnabled = false;
private boolean fDynamicValidation = false;
private boolean fSchemaValidation = true;
private boolean fSchemaValidationFullChecking = false;
private boolean fValidationEnabledByDynamic = false;
private boolean fDynamicDisabledByValidation = false;
private boolean fWarningOnDuplicateAttDef = false;
private boolean fWarningOnUndeclaredElements = false;
private boolean fNormalizeAttributeValues = true;
private boolean fLoadDTDGrammar = true;
// declarations
private DOMParser fSchemaGrammarParser = null;
private int fDeclaration[];
private XMLErrorReporter fErrorReporter = null;
private DefaultEntityHandler fEntityHandler = null;
private QName fCurrentElement = new QName();
private ContentLeafNameTypeVector[] fContentLeafStack = new ContentLeafNameTypeVector[8];
private int[] fValidationFlagStack = new int[8];
private int[] fScopeStack = new int[8];
private int[] fGrammarNameSpaceIndexStack = new int[8];
private int[] fElementEntityStack = new int[8];
private int[] fElementIndexStack = new int[8];
private int[] fContentSpecTypeStack = new int[8];
private static final int sizeQNameParts = 8;
private QName[] fElementQNamePartsStack = new QName[sizeQNameParts];
private QName[] fElementChildren = new QName[32];
private int fElementChildrenLength = 0;
private int[] fElementChildrenOffsetStack = new int[32];
private int fElementDepth = -1;
private boolean fNamespacesEnabled = false;
private NamespacesScope fNamespacesScope = null;
private int fNamespacesPrefix = -1;
private QName fRootElement = new QName();
private int fAttrListHandle = -1;
private int fCurrentElementEntity = -1;
private int fCurrentElementIndex = -1;
private int fCurrentContentSpecType = -1;
private boolean fSeenDoctypeDecl = false;
private final int TOP_LEVEL_SCOPE = -1;
private int fCurrentScope = TOP_LEVEL_SCOPE;
private int fCurrentSchemaURI = StringPool.EMPTY_STRING;
private int fEmptyURI = StringPool.EMPTY_STRING;
private int fXsiPrefix = - 1;
private int fXsiURI = -2;
private int fXsiTypeAttValue = -1;
private DatatypeValidator fXsiTypeValidator = null;
private boolean fNil = false;
private Grammar fGrammar = null;
private int fGrammarNameSpaceIndex = StringPool.EMPTY_STRING;
private GrammarResolver fGrammarResolver = null;
// state and stuff
private boolean fScanningDTD = false;
private XMLDocumentScanner fDocumentScanner = null;
private boolean fCalledStartDocument = false;
private XMLDocumentHandler fDocumentHandler = null;
private XMLDocumentHandler.DTDHandler fDTDHandler = null;
private boolean fSeenRootElement = false;
private XMLAttrList fAttrList = null;
private int fXMLLang = -1;
private LocatorImpl fAttrNameLocator = null;
private boolean fCheckedForSchema = false;
private boolean fDeclsAreExternal = false;
private StringPool.CharArrayRange fCurrentElementCharArrayRange = null;
private char[] fCharRefData = null;
private boolean fSendCharDataAsCharArray = false;
private boolean fBufferDatatype = false;
private StringBuffer fDatatypeBuffer = new StringBuffer();
private QName fTempQName = new QName();
private XMLAttributeDecl fTempAttDecl = new XMLAttributeDecl();
private XMLAttributeDecl fTempAttributeDecl = new XMLAttributeDecl();
private XMLElementDecl fTempElementDecl = new XMLElementDecl();
private boolean fGrammarIsDTDGrammar = false;
private boolean fGrammarIsSchemaGrammar = false;
private boolean fNeedValidationOff = false;
//Schema Normalization
private static final boolean DEBUG_NORMALIZATION = false;
private DatatypeValidator fCurrentDV = null; //current datatype validator
private boolean fFirstChunk = true; // got first chunk in characters() (SAX)
private boolean fTrailing = false; // Previous chunk had a trailing space
private short fWhiteSpace = DatatypeValidator.COLLAPSE; //whiteSpace: preserve/replace/collapse
private StringBuffer fStringBuffer = new StringBuffer(CHUNK_SIZE); //holds normalized str value
private StringBuffer fTempBuffer = new StringBuffer(CHUNK_SIZE); //holds unnormalized str value
// symbols
private int fEMPTYSymbol = -1;
private int fANYSymbol = -1;
private int fMIXEDSymbol = -1;
private int fCHILDRENSymbol = -1;
private int fCDATASymbol = -1;
private int fIDSymbol = -1;
private int fIDREFSymbol = -1;
private int fIDREFSSymbol = -1;
private int fENTITYSymbol = -1;
private int fENTITIESSymbol = -1;
private int fNMTOKENSymbol = -1;
private int fNMTOKENSSymbol = -1;
private int fNOTATIONSymbol = -1;
private int fENUMERATIONSymbol = -1;
private int fREQUIREDSymbol = -1;
private int fFIXEDSymbol = -1;
private int fDATATYPESymbol = -1;
private int fEpsilonIndex = -1;
//Datatype Registry
private DatatypeValidatorFactoryImpl fDataTypeReg = null;
private DatatypeValidator fValID = null;
private DatatypeValidator fValIDRef = null;
private DatatypeValidator fValIDRefs = null;
private DatatypeValidator fValENTITY = null;
private DatatypeValidator fValENTITIES = null;
private DatatypeValidator fValNMTOKEN = null;
private DatatypeValidator fValNMTOKENS = null;
private DatatypeValidator fValNOTATION = null;
// identity constraint information
/**
* Stack of active XPath matchers for identity constraints. All
* active XPath matchers are notified of startElement, characters
* and endElement callbacks in order to perform their matches.
* <p>
* For each element with identity constraints, the selector of
* each identity constraint is activated. When the selector matches
* its XPath, then all the fields of the identity constraint are
* activated.
* <p>
* <strong>Note:</strong> Once the activation scope is left, the
* XPath matchers are automatically removed from the stack of
* active matchers and no longer receive callbacks.
*/
protected XPathMatcherStack fMatcherStack = new XPathMatcherStack();
/** Cache of value stores for identity constraint fields. */
protected ValueStoreCache fValueStoreCache = new ValueStoreCache();
// store the substitution group comparator
protected SubstitutionGroupComparator fSGComparator = null;
//
// Constructors
//
/** Constructs an XML validator. */
public XMLValidator(StringPool stringPool,
XMLErrorReporter errorReporter,
DefaultEntityHandler entityHandler,
XMLDocumentScanner documentScanner) {
// keep references
fStringPool = stringPool;
fErrorReporter = errorReporter;
fEntityHandler = entityHandler;
fDocumentScanner = documentScanner;
fEmptyURI = fStringPool.addSymbol("");
fXsiURI = fStringPool.addSymbol(SchemaSymbols.URI_XSI);
// initialize
fAttrList = new XMLAttrList(fStringPool);
entityHandler.setEventHandler(this);
entityHandler.setCharDataHandler(this);
fDocumentScanner.setEventHandler(this);
for (int i = 0; i < sizeQNameParts; i++) {
fElementQNamePartsStack[i] = new QName();
}
init();
} // <init>(StringPool,XMLErrorReporter,DefaultEntityHandler,XMLDocumentScanner)
public void setGrammarResolver(GrammarResolver grammarResolver){
fGrammarResolver = grammarResolver;
fSGComparator = new SubstitutionGroupComparator(fGrammarResolver, fStringPool, fErrorReporter);
ElementWildcard.setErrReporter(fStringPool, fErrorReporter);
if (fValidating) { //optimization -el
initDataTypeValidators();
}
}
//
// Public methods
//
// initialization
/** Set char data processing preference and handlers. */
public void initHandlers(boolean sendCharDataAsCharArray,
XMLDocumentHandler docHandler,
XMLDocumentHandler.DTDHandler dtdHandler) {
fSendCharDataAsCharArray = sendCharDataAsCharArray;
fEntityHandler.setSendCharDataAsCharArray(fSendCharDataAsCharArray);
fDocumentHandler = docHandler;
fDTDHandler = dtdHandler;
} // initHandlers(boolean,XMLDocumentHandler,XMLDocumentHandler.DTDHandler)
/** Reset or copy. */
public void resetOrCopy(StringPool stringPool) throws Exception {
fAttrList = new XMLAttrList(stringPool);
resetCommon(stringPool);
}
/** Reset. */
public void reset(StringPool stringPool) throws Exception {
fAttrList.reset(stringPool);
resetCommon(stringPool);
}
// settings
/**
* Turning on validation/dynamic turns on validation if it is off, and
* this is remembered. Turning off validation DISABLES validation/dynamic
* if it is on. Turning off validation/dynamic DOES NOT turn off
* validation if it was explicitly turned on, only if it was turned on
* BECAUSE OF the call to turn validation/dynamic on. Turning on
* validation will REENABLE and turn validation/dynamic back on if it
* was disabled by a call that turned off validation while
* validation/dynamic was enabled.
*/
public void setValidationEnabled(boolean flag) throws Exception {
fValidationEnabled = flag;
fValidationEnabledByDynamic = false;
if (fValidationEnabled) {
if (fDynamicDisabledByValidation) {
fDynamicValidation = true;
fDynamicDisabledByValidation = false;
}
} else if (fDynamicValidation) {
fDynamicValidation = false;
fDynamicDisabledByValidation = true;
}
fValidating = fValidationEnabled;
//optimization: don't create unnecessary DatatypeValidators. - el
if (fValidating) {
initDataTypeValidators();
}
}
/** Returns true if validation is enabled. */
public boolean getValidationEnabled() {
return fValidationEnabled;
}
/** Sets whether Schema support is on/off. */
public void setSchemaValidationEnabled(boolean flag) {
fSchemaValidation = flag;
}
/** Returns true if Schema support is on. */
public boolean getSchemaValidationEnabled() {
return fSchemaValidation;
}
/** Sets whether full Schema error checking is on/off */
public void setSchemaFullCheckingEnabled(boolean flag) {
fSchemaValidationFullChecking = flag;
}
/** Returns true if full Schema checking is on. */
public boolean getSchemaFullCheckingEnabled() {
return fSchemaValidationFullChecking;
}
/** Sets whether validation is dynamic. */
public void setDynamicValidationEnabled(boolean flag) throws Exception {
fDynamicValidation = flag;
fDynamicDisabledByValidation = false;
if (!fDynamicValidation) {
if (fValidationEnabledByDynamic) {
fValidationEnabled = false;
fValidationEnabledByDynamic = false;
}
} else if (!fValidationEnabled) {
fValidationEnabled = true;
fValidationEnabledByDynamic = true;
}
fValidating = fValidationEnabled;
//optimization: don't create unnecessary DatatypeValidators. - el
if (fValidating) {
initDataTypeValidators();
}
}
/** Returns true if validation is dynamic. */
public boolean getDynamicValidationEnabled() {
return fDynamicValidation;
}
/** Sets fNormalizeAttributeValues **/
public void setNormalizeAttributeValues(boolean normalize){
fNormalizeAttributeValues = normalize;
}
/** Sets fLoadDTDGrammar when validation is off **/
public void setLoadDTDGrammar(boolean loadDG){
if (fValidating) {
fLoadDTDGrammar = true;
} else {
fLoadDTDGrammar = loadDG;
}
}
/** Returns fLoadDTDGrammar **/
public boolean getLoadDTDGrammar() {
return fLoadDTDGrammar;
}
/** Sets whether namespaces are enabled. */
public void setNamespacesEnabled(boolean flag) {
fNamespacesEnabled = flag;
}
/** Returns true if namespaces are enabled. */
public boolean getNamespacesEnabled() {
return fNamespacesEnabled;
}
/** Sets whether duplicate attribute definitions signal a warning. */
public void setWarningOnDuplicateAttDef(boolean flag) {
fWarningOnDuplicateAttDef = flag;
}
/** Returns true if duplicate attribute definitions signal a warning. */
public boolean getWarningOnDuplicateAttDef() {
return fWarningOnDuplicateAttDef;
}
/** Sets whether undeclared elements signal a warning. */
public void setWarningOnUndeclaredElements(boolean flag) {
fWarningOnUndeclaredElements = flag;
}
/** Returns true if undeclared elements signal a warning. */
public boolean getWarningOnUndeclaredElements() {
return fWarningOnUndeclaredElements;
}
//
// FieldActivator methods
//
/**
* Start the value scope for the specified identity constraint. This
* method is called when the selector matches in order to initialize
* the value store.
*
* @param identityConstraint The identity constraint.
*/
public void startValueScopeFor(IdentityConstraint identityConstraint)
throws Exception {
for(int i=0; i<identityConstraint.getFieldCount(); i++) {
Field field = identityConstraint.getFieldAt(i);
ValueStoreBase valueStore = fValueStoreCache.getValueStoreFor(field);
valueStore.startValueScope();
}
} // startValueScopeFor(IdentityConstraint identityConstraint)
/**
* Request to activate the specified field. This method returns the
* matcher for the field.
*
* @param field The field to activate.
*/
public XPathMatcher activateField(Field field) throws Exception {
if (DEBUG_IDENTITY_CONSTRAINTS) {
System.out.println("<IC>: activateField(\""+field+"\")");
}
ValueStore valueStore = fValueStoreCache.getValueStoreFor(field);
field.setMayMatch(true);
XPathMatcher matcher = field.createMatcher(valueStore);
fMatcherStack.addMatcher(matcher);
matcher.startDocumentFragment(fStringPool);
return matcher;
} // activateField(Field):XPathMatcher
/**
* Ends the value scope for the specified identity constraint.
*
* @param identityConstraint The identity constraint.
*/
public void endValueScopeFor(IdentityConstraint identityConstraint)
throws Exception {
ValueStoreBase valueStore = fValueStoreCache.getValueStoreFor(identityConstraint);
valueStore.endValueScope();
} // endValueScopeFor(IdentityConstraint)
//
// DefaultEntityHandler.EventHandler methods
//
/** Start entity reference. */
public void startEntityReference(int entityName, int entityType, int entityContext) throws Exception {
fDocumentHandler.startEntityReference(entityName, entityType, entityContext);
}
/** End entity reference. */
public void endEntityReference(int entityName, int entityType, int entityContext) throws Exception {
fDocumentHandler.endEntityReference(entityName, entityType, entityContext);
}
/** Send end of input notification. */
public void sendEndOfInputNotifications(int entityName, boolean moreToFollow) throws Exception {
fDocumentScanner.endOfInput(entityName, moreToFollow);
/***
if (fScanningDTD) {
fDTDImporter.sendEndOfInputNotifications(entityName, moreToFollow);
}
/***/
}
/** Send reader change notifications. */
public void sendReaderChangeNotifications(XMLEntityHandler.EntityReader reader, int readerId) throws Exception {
fDocumentScanner.readerChange(reader, readerId);
/***
if (fScanningDTD) {
fDTDImporter.sendReaderChangeNotifications(reader, readerId);
}
/***/
}
/** External entity standalone check. */
public boolean externalEntityStandaloneCheck() {
return(fStandaloneReader != -1 && fValidating);
}
/** Return true if validating. */
public boolean getValidating() {
return fValidating;
}
//
// XMLEntityHandler.CharDataHandler methods
//
/**
* Normalize whitespace in an XMLString according to the rules of attribute
* value normalization - converting all whitespace characters to space
* characters.
* In addition for attributes of type other than CDATA: trim leading and
* trailing spaces and collapse spaces (0x20 only).
*
* @param value The string to normalize.
* @returns 0 if no triming is done or if there is neither leading nor
* trailing whitespace,
* 1 if there is only leading whitespace,
* 2 if there is only trailing whitespace,
* 3 if there is both leading and trailing whitespace.
*/
private int normalizeWhitespace( StringBuffer chars, boolean collapse) {
int length = fTempBuffer.length();
fStringBuffer.setLength(0);
boolean skipSpace = collapse;
boolean sawNonWS = false;
int leading = 0;
int trailing = 0;
int c;
for (int i = 0; i < length; i++) {
c = chars.charAt(i);
if (c == 0x20 || c == 0x0D || c == 0x0A || c == 0x09) {
if (!skipSpace) {
// take the first whitespace as a space and skip the others
fStringBuffer.append(' ');
skipSpace = collapse;
}
if (!sawNonWS) {
// this is a leading whitespace, record it
leading = 1;
}
}
else {
fStringBuffer.append((char)c);
skipSpace = false;
sawNonWS = true;
}
}
if (skipSpace) {
c = fStringBuffer.length();
if ( c != 0) {
// if we finished on a space trim it but also record it
fStringBuffer.setLength (--c);
trailing = 2;
}
else if (leading != 0 && !sawNonWS) {
// if all we had was whitespace we skipped record it as
// trailing whitespace as well
trailing = 2;
}
}
//value.setValues(fStringBuffer);
return collapse ? leading + trailing : 0;
}
/** Process characters. Schema Normalization*/
public void processCharacters(char[] chars, int offset, int length) throws Exception {
if (DEBUG_NORMALIZATION) {
System.out.println("==>processCharacters(char[] chars, int offset, int length");
}
if (fValidating) {
if (fInElementContent || fCurrentContentSpecType == XMLElementDecl.TYPE_EMPTY) {
if (DEBUG_NORMALIZATION) {
System.out.println("==>charDataInContent()");
}
charDataInContent();
}
if (fBufferDatatype) {
if (fFirstChunk && fGrammar!=null) {
fGrammar.getElementDecl(fCurrentElementIndex, fTempElementDecl);
fCurrentDV = fTempElementDecl.datatypeValidator;
if (fCurrentDV !=null) {
fWhiteSpace = fCurrentDV.getWSFacet();
}
}
if (DEBUG_NORMALIZATION) {
System.out.println("Start schema datatype normalization <whiteSpace value=" +fWhiteSpace+">");
}
if (fWhiteSpace == DatatypeValidator.PRESERVE) { //do not normalize
fDatatypeBuffer.append(chars, offset, length);
}
else {
fTempBuffer.setLength(0);
fTempBuffer.append(chars, offset, length);
int spaces = normalizeWhitespace(fTempBuffer, (fWhiteSpace==DatatypeValidator.COLLAPSE));
int nLength = fStringBuffer.length();
if (nLength > 0) {
if (!fFirstChunk && (fWhiteSpace==DatatypeValidator.COLLAPSE) && fTrailing) {
fStringBuffer.insert(0, ' ');
nLength++;
}
if ((length-offset)!=nLength) {
char[] newChars = new char[nLength];
fStringBuffer.getChars(0, nLength , newChars, 0);
chars = newChars;
offset = 0;
length = nLength;
}
else {
fStringBuffer.getChars(0, nLength , chars, 0);
}
fDatatypeBuffer.append(chars, offset, length);
fDocumentHandler.characters(chars, offset, length);
// call all active identity constraints
int count = fMatcherStack.getMatcherCount();
for (int i = 0; i < count; i++) {
XPathMatcher matcher = fMatcherStack.getMatcherAt(i);
if (DEBUG_IDENTITY_CONSTRAINTS) {
String text = new String(chars, offset, length);
System.out.println("<IC>: "+matcher.toString()+"#characters("+text+")");
}
matcher.characters(chars, offset, length);
}
}
fTrailing = (spaces > 1)?true:false;
fFirstChunk = false;
return;
}
}
}
fFirstChunk = false;
fDocumentHandler.characters(chars, offset, length);
// call all active identity constraints
int count = fMatcherStack.getMatcherCount();
for (int i = 0; i < count; i++) {
XPathMatcher matcher = fMatcherStack.getMatcherAt(i);
if (DEBUG_IDENTITY_CONSTRAINTS) {
String text = new String(chars, offset, length);
System.out.println("<IC>: "+matcher.toString()+"#characters("+text+")");
}
matcher.characters(chars, offset, length);
}
}
/** Process characters. */
public void processCharacters(int data) throws Exception {
if (fValidating) {
if (fInElementContent || fCurrentContentSpecType == XMLElementDecl.TYPE_EMPTY) {
charDataInContent();
}
if (fBufferDatatype) {
fGrammar.getElementDecl(fCurrentElementIndex, fTempElementDecl);
//REVISIT: add normalization according to datatypes
fCurrentDV = fTempElementDecl.datatypeValidator;
if (fCurrentDV !=null) {
fWhiteSpace = fCurrentDV.getWSFacet();
}
if (fWhiteSpace == DatatypeValidator.PRESERVE) { //no normalization done
fDatatypeBuffer.append(fStringPool.toString(data));
}
else {
String str = fStringPool.toString(data);
int length = str.length();
fTempBuffer.setLength(0);
fTempBuffer.append(str);
int spaces = normalizeWhitespace(fTempBuffer, (fWhiteSpace == DatatypeValidator.COLLAPSE));
if (fWhiteSpace != DatatypeValidator.PRESERVE) {
//normalization was done.
fStringPool.releaseString(data);
data = fStringPool.addString(fStringBuffer.toString());
}
fDatatypeBuffer.append(fStringBuffer.toString());
}
}
}
fDocumentHandler.characters(data);
// call all active identity constraints
int count = fMatcherStack.getMatcherCount();
if (count > 0) {
String text = fStringPool.toString(data);
char[] chars = new char[text.length()];
int offset = 0;
int length = chars.length;
text.getChars(offset, length, chars, offset);
for (int i = 0; i < count; i++) {
XPathMatcher matcher = fMatcherStack.getMatcherAt(i);
if (DEBUG_IDENTITY_CONSTRAINTS) {
System.out.println("<IC>: "+matcher.toString()+"#characters("+text+")");
}
matcher.characters(chars, offset, length);
}
}
}
/** Process whitespace. */
public void processWhitespace(char[] chars, int offset, int length)
throws Exception {
if (fInElementContent) {
if (fStandaloneReader != -1 && fValidating && getElementDeclIsExternal(fCurrentElementIndex)) {
reportRecoverableXMLError(XMLMessages.MSG_WHITE_SPACE_IN_ELEMENT_CONTENT_WHEN_STANDALONE,
XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION);
}
fDocumentHandler.ignorableWhitespace(chars, offset, length);
} else {
if (fCurrentContentSpecType == XMLElementDecl.TYPE_EMPTY) {
charDataInContent();
}
fDocumentHandler.characters(chars, offset, length);
// call all active identity constraints
int count = fMatcherStack.getMatcherCount();
for (int i = 0; i < count; i++) {
XPathMatcher matcher = fMatcherStack.getMatcherAt(i);
if (DEBUG_IDENTITY_CONSTRAINTS) {
String text = new String(chars, offset, length);
System.out.println("<IC>: "+matcher.toString()+"#characters("+text+")");
}
matcher.characters(chars, offset, length);
}
}
} // processWhitespace(char[],int,int)
/** Process whitespace. */
public void processWhitespace(int data) throws Exception {
if (fInElementContent) {
if (fStandaloneReader != -1 && fValidating && getElementDeclIsExternal(fCurrentElementIndex)) {
reportRecoverableXMLError(XMLMessages.MSG_WHITE_SPACE_IN_ELEMENT_CONTENT_WHEN_STANDALONE,
XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION);
}
fDocumentHandler.ignorableWhitespace(data);
} else {
if (fCurrentContentSpecType == XMLElementDecl.TYPE_EMPTY) {
charDataInContent();
}
fDocumentHandler.characters(data);
// call all active identity constraints
int count = fMatcherStack.getMatcherCount();
if (count > 0) {
String text = fStringPool.toString(data);
char[] chars = new char[text.length()];
int offset = 0;
int length = chars.length;
text.getChars(length, length, chars, offset);
for (int i = 0; i < count; i++) {
XPathMatcher matcher = fMatcherStack.getMatcherAt(i);
if (DEBUG_IDENTITY_CONSTRAINTS) {
System.out.println("<IC>: "+matcher.toString()+"#characters("+text+")");
}
matcher.characters(chars, offset, length);
}
}
}
} // processWhitespace(int)
//
// XMLDocumentScanner.EventHandler methods
//
/** Scans element type. */
public void scanElementType(XMLEntityHandler.EntityReader entityReader,
char fastchar, QName element) throws Exception {
if (!fNamespacesEnabled) {
element.clear();
element.localpart = entityReader.scanName(fastchar);
element.rawname = element.localpart;
} else {
entityReader.scanQName(fastchar, element);
if (entityReader.lookingAtChar(':', false)) {
fErrorReporter.reportError(fErrorReporter.getLocator(),
XMLMessages.XML_DOMAIN,
XMLMessages.MSG_TWO_COLONS_IN_QNAME,
XMLMessages.P5_INVALID_CHARACTER,
null,
XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
entityReader.skipPastNmtoken(' ');
}
}
} // scanElementType(XMLEntityHandler.EntityReader,char,QName)
/** Scans expected element type. */
public boolean scanExpectedElementType(XMLEntityHandler.EntityReader entityReader,
char fastchar, QName element)
throws Exception {
if (fCurrentElementCharArrayRange == null) {
fCurrentElementCharArrayRange = fStringPool.createCharArrayRange();
}
fStringPool.getCharArrayRange(fCurrentElement.rawname, fCurrentElementCharArrayRange);
return entityReader.scanExpectedName(fastchar, fCurrentElementCharArrayRange);
} // scanExpectedElementType(XMLEntityHandler.EntityReader,char,QName)
/** Scans attribute name. */
public void scanAttributeName(XMLEntityHandler.EntityReader entityReader,
QName element, QName attribute)
throws Exception {
if (!fSeenRootElement) {
fSeenRootElement = true;
rootElementSpecified(element);
fStringPool.resetShuffleCount();
}
if (!fNamespacesEnabled) {
attribute.clear();
attribute.localpart = entityReader.scanName('=');
attribute.rawname = attribute.localpart;
} else {
entityReader.scanQName('=', attribute);
if (entityReader.lookingAtChar(':', false)) {
fErrorReporter.reportError(fErrorReporter.getLocator(),
XMLMessages.XML_DOMAIN,
XMLMessages.MSG_TWO_COLONS_IN_QNAME,
XMLMessages.P5_INVALID_CHARACTER,
null,
XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
entityReader.skipPastNmtoken(' ');
}
}
} // scanAttributeName(XMLEntityHandler.EntityReader,QName,QName)
/** Call start document. */
public void callStartDocument() throws Exception {
if (!fCalledStartDocument) {
fDocumentHandler.startDocument();
fCalledStartDocument = true;
if (fValidating) {
fValueStoreCache.startDocument();
}
}
}
/** Call end document. */
public void callEndDocument() throws Exception {
if (fCalledStartDocument) {
if (fValidating) {
if (DEBUG_IDENTITY_CONSTRAINTS) {
System.out.println("<IC>: ValueStoreCache#endDocument()");
}
fValueStoreCache.endDocument();
}
fDocumentHandler.endDocument();
}
}
/** Call XML declaration. */
public void callXMLDecl(int version, int encoding, int standalone) throws Exception {
fDocumentHandler.xmlDecl(version, encoding, standalone);
}
public void callStandaloneIsYes() throws Exception {
// standalone = "yes". said XMLDocumentScanner.
fStandaloneReader = fEntityHandler.getReaderId() ;
}
/** Call text declaration. */
public void callTextDecl(int version, int encoding) throws Exception {
fDocumentHandler.textDecl(version, encoding);
}
/**
* Signal the scanning of an element name in a start element tag.
*
* @param element Element name scanned.
*/
public void element(QName element) throws Exception {
fAttrListHandle = -1;
}
/**
* Signal the scanning of an attribute associated to the previous
* start element tag.
*
* @param element Element name scanned.
* @param attrName Attribute name scanned.
* @param attrValue The string pool index of the attribute value.
*/
public boolean attribute(QName element, QName attrName, int attrValue) throws Exception {
if (fAttrListHandle == -1) {
fAttrListHandle = fAttrList.startAttrList();
}
// if fAttrList.addAttr returns -1, indicates duplicate att in start tag of an element.
// specified: true, search : true
return fAttrList.addAttr(attrName, attrValue, fCDATASymbol, true, true) == -1;
}
/** Call start element. */
public void callStartElement(QName element) throws Exception {
if ( DEBUG_SCHEMA_VALIDATION )
System.out.println("\n=======StartElement : " + fStringPool.toString(element.localpart));
//
// Check after all specified attrs are scanned
// (1) report error for REQUIRED attrs that are missing (V_TAGc)
// (2) report error for PROHIBITED attrs that are present (V_TAGc)
// (3) add default attrs (FIXED and NOT_FIXED)
//
if (!fSeenRootElement) {
fSeenRootElement = true;
rootElementSpecified(element);
fStringPool.resetShuffleCount();
}
if (fGrammar != null && fGrammarIsDTDGrammar) {
fAttrListHandle = addDTDDefaultAttributes(element, fAttrList, fAttrListHandle, fValidating, fStandaloneReader != -1);
}
fCheckedForSchema = true;
if (fNamespacesEnabled) {
bindNamespacesToElementAndAttributes(element, fAttrList);
}
validateElementAndAttributes(element, fAttrList);
if (fAttrListHandle != -1) {
fAttrList.endAttrList();
}
// activate identity constraints
if (fValidating && fGrammar != null) {
if (DEBUG_IDENTITY_CONSTRAINTS) {
System.out.println("<IC>: pushing context - element: "+fStringPool.toString(element.rawname));
}
fValueStoreCache.startElement();
fMatcherStack.pushContext();
int eindex = fGrammar.getElementDeclIndex(element, -1);
if (eindex != -1) {
fGrammar.getElementDecl(eindex, fTempElementDecl);
fValueStoreCache.initValueStoresFor(fTempElementDecl);
int uCount = fTempElementDecl.unique.size();
for (int i = 0; i < uCount; i++) {
activateSelectorFor((IdentityConstraint)fTempElementDecl.unique.elementAt(i));
}
int kCount = fTempElementDecl.key.size();
for (int i = 0; i < kCount; i++) {
activateSelectorFor((IdentityConstraint)fTempElementDecl.key.elementAt(i));
}
int krCount = fTempElementDecl.keyRef.size();
for (int i = 0; i < krCount; i++) {
activateSelectorFor((IdentityConstraint)fTempElementDecl.keyRef.elementAt(i));
}
}
// call all active identity constraints
int count = fMatcherStack.getMatcherCount();
for (int i = 0; i < count; i++) {
XPathMatcher matcher = fMatcherStack.getMatcherAt(i);
if (DEBUG_IDENTITY_CONSTRAINTS) {
System.out.println("<IC>: "+matcher.toString()+"#startElement("+fStringPool.toString(element.rawname)+")");
}
matcher.startElement(element, fAttrList, fAttrListHandle, fCurrentElementIndex, (SchemaGrammar)fGrammar);
}
}
// call handler
fDocumentHandler.startElement(element, fAttrList, fAttrListHandle);
fElementDepth++;
fAttrListHandle = -1;
//if (fElementDepth >= 0) {
// REVISIT: Why are doing anything if the grammar is null? -Ac
if (fValidating) {
// push current length onto stack
if (fElementChildrenOffsetStack.length <= fElementDepth) {
int newarray[] = new int[fElementChildrenOffsetStack.length * 2];
System.arraycopy(fElementChildrenOffsetStack, 0, newarray, 0, fElementChildrenOffsetStack.length);
fElementChildrenOffsetStack = newarray;
}
fElementChildrenOffsetStack[fElementDepth] = fElementChildrenLength;
// add this element to children
if (fElementChildren.length <= fElementChildrenLength) {
QName[] newarray = new QName[fElementChildrenLength * 2];
System.arraycopy(fElementChildren, 0, newarray, 0, fElementChildren.length);
fElementChildren = newarray;
}
QName qname = fElementChildren[fElementChildrenLength];
if (qname == null) {
for (int i = fElementChildrenLength; i < fElementChildren.length; i++) {
fElementChildren[i] = new QName();
}
qname = fElementChildren[fElementChildrenLength];
}
qname.setValues(element);
fElementChildrenLength++;
if (DEBUG_ELEMENT_CHILDREN) {
printChildren();
printStack();
}
}
ensureStackCapacity(fElementDepth);
fCurrentElement.setValues(element);
fCurrentElementEntity = fEntityHandler.getReaderId();
fElementQNamePartsStack[fElementDepth].setValues(fCurrentElement);
fElementEntityStack[fElementDepth] = fCurrentElementEntity;
fElementIndexStack[fElementDepth] = fCurrentElementIndex;
fContentSpecTypeStack[fElementDepth] = fCurrentContentSpecType;
if (fNeedValidationOff) {
fValidating = false;
fNeedValidationOff = false;
}
if (fValidating && fGrammarIsSchemaGrammar) {
pushContentLeafStack();
}
fValidationFlagStack[fElementDepth] = fValidating ? 0 : -1;
fScopeStack[fElementDepth] = fCurrentScope;
fGrammarNameSpaceIndexStack[fElementDepth] = fGrammarNameSpaceIndex;
} // callStartElement(QName)
private void activateSelectorFor(IdentityConstraint ic) throws Exception {
Selector selector = ic.getSelector();
if (DEBUG_IDENTITY_CONSTRAINTS) {
System.out.println("<IC>: XMLValidator#activateSelectorFor("+selector+')');
}
FieldActivator activator = this;
if(selector == null)
return;
XPathMatcher matcher = selector.createMatcher(activator);
fMatcherStack.addMatcher(matcher);
if (DEBUG_IDENTITY_CONSTRAINTS) {
System.out.println("<IC>: "+matcher+"#startDocumentFragment()");
}
matcher.startDocumentFragment(fStringPool);
}
private void pushContentLeafStack() throws Exception {
int contentType = getContentSpecType(fCurrentElementIndex);
if ( contentType == XMLElementDecl.TYPE_CHILDREN ||
contentType == XMLElementDecl.TYPE_MIXED_COMPLEX) {
XMLContentModel cm = getElementContentModel(fCurrentElementIndex);
ContentLeafNameTypeVector cv = cm.getContentLeafNameTypeVector();
if (cm != null) {
fContentLeafStack[fElementDepth] = cv;
}
}
}
private void ensureStackCapacity ( int newElementDepth) {
if (newElementDepth == fElementQNamePartsStack.length ) {
int[] newStack = new int[newElementDepth * 2];
System.arraycopy(fScopeStack, 0, newStack, 0, newElementDepth);
fScopeStack = newStack;
newStack = new int[newElementDepth * 2];
System.arraycopy(fGrammarNameSpaceIndexStack, 0, newStack, 0, newElementDepth);
fGrammarNameSpaceIndexStack = newStack;
QName[] newStackOfQueue = new QName[newElementDepth * 2];
System.arraycopy(this.fElementQNamePartsStack, 0, newStackOfQueue, 0, newElementDepth );
fElementQNamePartsStack = newStackOfQueue;
QName qname = fElementQNamePartsStack[newElementDepth];
if (qname == null) {
for (int i = newElementDepth; i < fElementQNamePartsStack.length; i++) {
fElementQNamePartsStack[i] = new QName();
}
}
newStack = new int[newElementDepth * 2];
System.arraycopy(fElementEntityStack, 0, newStack, 0, newElementDepth);
fElementEntityStack = newStack;
newStack = new int[newElementDepth * 2];
System.arraycopy(fElementIndexStack, 0, newStack, 0, newElementDepth);
fElementIndexStack = newStack;
newStack = new int[newElementDepth * 2];
System.arraycopy(fContentSpecTypeStack, 0, newStack, 0, newElementDepth);
fContentSpecTypeStack = newStack;
newStack = new int[newElementDepth * 2];
System.arraycopy(fValidationFlagStack, 0, newStack, 0, newElementDepth);
fValidationFlagStack = newStack;
ContentLeafNameTypeVector[] newStackV = new ContentLeafNameTypeVector[newElementDepth * 2];
System.arraycopy(fContentLeafStack, 0, newStackV, 0, newElementDepth);
fContentLeafStack = newStackV;
}
}
/** Call end element. */
public void callEndElement(int readerId) throws Exception {
if ( DEBUG_SCHEMA_VALIDATION )
System.out.println("=======EndElement : " + fStringPool.toString(fCurrentElement.localpart)+"\n");
int prefixIndex = fCurrentElement.prefix;
int elementType = fCurrentElement.rawname;
if (fCurrentElementEntity != readerId) {
fErrorReporter.reportError(fErrorReporter.getLocator(),
XMLMessages.XML_DOMAIN,
XMLMessages.MSG_ELEMENT_ENTITY_MISMATCH,
XMLMessages.P78_NOT_WELLFORMED,
new Object[] { fStringPool.toString(elementType)},
XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
}
fElementDepth--;
if (fValidating) {
int elementIndex = fCurrentElementIndex;
if (elementIndex != -1 && fCurrentContentSpecType != -1) {
QName children[] = fElementChildren;
int childrenOffset = fElementChildrenOffsetStack[fElementDepth + 1] + 1;
int childrenLength = fElementChildrenLength - childrenOffset;
if (DEBUG_ELEMENT_CHILDREN) {
System.out.println("endElement("+fStringPool.toString(fCurrentElement.rawname)+')');
System.out.println("fCurrentContentSpecType : " + fCurrentContentSpecType );
System.out.print("offset: ");
System.out.print(childrenOffset);
System.out.print(", length: ");
System.out.print(childrenLength);
System.out.println();
printChildren();
printStack();
}
int result = checkContent(elementIndex,
children, childrenOffset, childrenLength);
if ( DEBUG_SCHEMA_VALIDATION )
System.out.println("!!!!!!!!In XMLValidator, the return value from checkContent : " + result);
if (result != -1) {
int majorCode = result != childrenLength ? XMLMessages.MSG_CONTENT_INVALID : XMLMessages.MSG_CONTENT_INCOMPLETE;
fGrammar.getElementDecl(elementIndex, fTempElementDecl);
if (fTempElementDecl.type == XMLElementDecl.TYPE_EMPTY) {
reportRecoverableXMLError(majorCode,
0,
fStringPool.toString(elementType),
"EMPTY");
} else
reportRecoverableXMLError(majorCode,
0,
fStringPool.toString(elementType),
XMLContentSpec.toString(fGrammar, fStringPool, fTempElementDecl.contentSpecIndex));
}
}
fElementChildrenLength = fElementChildrenOffsetStack[fElementDepth + 1] + 1;
// call matchers and de-activate context
int oldCount = fMatcherStack.getMatcherCount();
for (int i = oldCount - 1; i >= 0; i--) {
XPathMatcher matcher = fMatcherStack.getMatcherAt(i);
if (DEBUG_IDENTITY_CONSTRAINTS) {
System.out.println("<IC>: "+matcher+"#endElement("+fStringPool.toString(fCurrentElement.rawname)+")");
}
matcher.endElement(fCurrentElement, fCurrentElementIndex, (SchemaGrammar)fGrammar);
}
if (DEBUG_IDENTITY_CONSTRAINTS) {
System.out.println("<IC>: popping context - element: "+fStringPool.toString(fCurrentElement.rawname));
}
if (fMatcherStack.size() > 0) {
fMatcherStack.popContext();
}
int newCount = fMatcherStack.getMatcherCount();
// handle everything *but* keyref's.
for (int i = oldCount - 1; i >= newCount; i--) {
XPathMatcher matcher = fMatcherStack.getMatcherAt(i);
IdentityConstraint id;
if((id = matcher.getIDConstraint()) != null && id.getType() != IdentityConstraint.KEYREF) {
if (DEBUG_IDENTITY_CONSTRAINTS) {
System.out.println("<IC>: "+matcher+"#endDocumentFragment()");
}
matcher.endDocumentFragment();
fValueStoreCache.transplant(id);
} else if (id == null)
matcher.endDocumentFragment();
}
// now handle keyref's/...
for (int i = oldCount - 1; i >= newCount; i--) {
XPathMatcher matcher = fMatcherStack.getMatcherAt(i);
IdentityConstraint id;
if((id = matcher.getIDConstraint()) != null && id.getType() == IdentityConstraint.KEYREF) {
if (DEBUG_IDENTITY_CONSTRAINTS) {
System.out.println("<IC>: "+matcher+"#endDocumentFragment()");
}
ValueStoreBase values = fValueStoreCache.getValueStoreFor(id);
if(values != null) // nothing to do if nothing matched!
values.endDocumentFragment();
matcher.endDocumentFragment();
}
}
fValueStoreCache.endElement();
}
fDocumentHandler.endElement(fCurrentElement);
if (fNamespacesEnabled) {
fNamespacesScope.decreaseDepth();
}
// now pop this element off the top of the element stack
//if (fElementDepth-- < 0) {
if (fElementDepth < -1) {
throw new RuntimeException("FWK008 Element stack underflow");
}
if (fElementDepth < 0) {
fCurrentElement.clear();
fCurrentElementEntity = -1;
fCurrentElementIndex = -1;
fCurrentContentSpecType = -1;
fInElementContent = false;
//
// Check after document is fully parsed
// (1) check that there was an element with a matching id for every
// IDREF and IDREFS attr (V_IDREF0)
//
if (fValidating ) {
try {
this.fValIDRef.validate( null, this.fStoreIDRef ); //Store the ID list
this.fValIDRefs.validate( null, this.fStoreIDRef );
this.fValIDRef.validate( null, this.fValidateIDRef ); //Do final IDREF validation round
this.fValIDRefs.validate( null, this.fValidateIDRef );
this.fValID.validate( null, this.fResetID );//Reset ID, IDREF, IDREFS validators here
this.fValIDRef.validate(null, this.fResetIDRef );
this.fValIDRefs.validate(null, this.fResetID );
} catch ( InvalidDatatypeValueException ex ) {
reportRecoverableXMLError( ex.getMajorCode(), ex.getMinorCode(),
ex.getMessage() );
}
}
return;
}
//restore enclosing element to all the "current" variables
// REVISIT: Validation. This information needs to be stored.
fCurrentElement.prefix = -1;
if (fNamespacesEnabled) { //If Namespace enable then localName != rawName
fCurrentElement.localpart = fElementQNamePartsStack[fElementDepth].localpart;
} else {//REVISIT - jeffreyr - This is so we still do old behavior when namespace is off
fCurrentElement.localpart = fElementQNamePartsStack[fElementDepth].rawname;
}
fCurrentElement.rawname = fElementQNamePartsStack[fElementDepth].rawname;
fCurrentElement.uri = fElementQNamePartsStack[fElementDepth].uri;
fCurrentElement.prefix = fElementQNamePartsStack[fElementDepth].prefix;
fCurrentElementEntity = fElementEntityStack[fElementDepth];
fCurrentElementIndex = fElementIndexStack[fElementDepth];
fCurrentContentSpecType = fContentSpecTypeStack[fElementDepth];
fValidating = fValidationFlagStack[fElementDepth] == 0 ? true : false;
fCurrentScope = fScopeStack[fElementDepth];
if ( DEBUG_SCHEMA_VALIDATION ) {
System.out.println("+++++ currentElement : " + fStringPool.toString(elementType)+
"\n fCurrentElementIndex : " + fCurrentElementIndex +
"\n fCurrentScope : " + fCurrentScope +
"\n fCurrentContentSpecType : " + fCurrentContentSpecType +
"\n++++++++++++++++++++++++++++++++++++++++++++++++" );
}
// if enclosing element's Schema is different, need to switch "context"
if ( fGrammarNameSpaceIndex != fGrammarNameSpaceIndexStack[fElementDepth] ) {
fGrammarNameSpaceIndex = fGrammarNameSpaceIndexStack[fElementDepth];
if ( fValidating && fGrammarIsSchemaGrammar )
if (fGrammarNameSpaceIndex < StringPool.EMPTY_STRING) {
fGrammar = null;
fGrammarIsSchemaGrammar = false;
fGrammarIsDTDGrammar = false;
} else if (!switchGrammar(fGrammarNameSpaceIndex)) {
reportRecoverableXMLError(XMLMessages.MSG_GENERIC_SCHEMA_ERROR, XMLMessages.SCHEMA_GENERIC_ERROR,
"Grammar with uri 1: " + fStringPool.toString(fGrammarNameSpaceIndex)
+ " , can not be found");
}
}
if (fValidating) {
fBufferDatatype = false;
}
fInElementContent = (fCurrentContentSpecType == XMLElementDecl.TYPE_CHILDREN);
} // callEndElement(int)
/** Call start CDATA section. */
public void callStartCDATA() throws Exception {
if (fValidating && fInElementContent) {
charDataInContent();
}
fDocumentHandler.startCDATA();
}
/** Call end CDATA section. */
public void callEndCDATA() throws Exception {
fDocumentHandler.endCDATA();
}
/** Call characters. */
public void callCharacters(int ch) throws Exception {
if (fCharRefData == null) {
fCharRefData = new char[2];
}
int count = (ch < 0x10000) ? 1 : 2;
if (count == 1) {
fCharRefData[0] = (char)ch;
} else {
fCharRefData[0] = (char)(((ch-0x00010000)>>10)+0xd800);
fCharRefData[1] = (char)(((ch-0x00010000)&0x3ff)+0xdc00);
}
if (fValidating && (fInElementContent || fCurrentContentSpecType == XMLElementDecl.TYPE_EMPTY)) {
charDataInContent();
}
if (fValidating) {
if (fBufferDatatype) {
fDatatypeBuffer.append(fCharRefData,0,1);
}
}
if (fSendCharDataAsCharArray) {
fDocumentHandler.characters(fCharRefData, 0, count);
} else {
int index = fStringPool.addString(new String(fCharRefData, 0, count));
fDocumentHandler.characters(index);
}
// call all active identity constraints
int matcherCount = fMatcherStack.getMatcherCount();
for (int i = 0; i < matcherCount; i++) {
XPathMatcher matcher = fMatcherStack.getMatcherAt(i);
if (DEBUG_IDENTITY_CONSTRAINTS) {
String text = new String(fCharRefData, 0, count);
System.out.println("<IC>: "+matcher.toString()+"#characters("+text+")");
}
matcher.characters(fCharRefData, 0, count);
}
} // callCharacters(int)
/** Call processing instruction. */
public void callProcessingInstruction(int target, int data) throws Exception {
fDocumentHandler.processingInstruction(target, data);
}
/** Call comment. */
public void callComment(int comment) throws Exception {
fDocumentHandler.comment(comment);
}
//
// NamespacesScope.NamespacesHandler methods
//
/** Start a new namespace declaration scope. */
public void startNamespaceDeclScope(int prefix, int uri) throws Exception {
fDocumentHandler.startNamespaceDeclScope(prefix, uri);
}
/** End a namespace declaration scope. */
public void endNamespaceDeclScope(int prefix) throws Exception {
fDocumentHandler.endNamespaceDeclScope(prefix);
}
// attributes
// other
/** Sets the root element. */
public void setRootElementType(QName rootElement) {
fRootElement.setValues(rootElement);
}
/**
* Returns true if the element declaration is external.
* <p>
* <strong>Note:</strong> This method is primarilly useful for
* DTDs with internal and external subsets.
*/
private boolean getElementDeclIsExternal(int elementIndex) {
/*if (elementIndex < 0 || elementIndex >= fElementCount) {
return false;
}
int chunk = elementIndex >> CHUNK_SHIFT;
int index = elementIndex & CHUNK_MASK;
return (fElementDeclIsExternal[chunk][index] != 0);
*/
if (fGrammarIsDTDGrammar ) {
return((DTDGrammar) fGrammar).getElementDeclIsExternal(elementIndex);
}
return false;
}
/** Returns the content spec type for an element index. */
public int getContentSpecType(int elementIndex) {
int contentSpecType = -1;
if ( elementIndex > -1) {
if ( fGrammar.getElementDecl(elementIndex,fTempElementDecl) ) {
contentSpecType = fTempElementDecl.type;
}
}
return contentSpecType;
}
/** Returns the content spec handle for an element index. */
public int getContentSpecHandle(int elementIndex) {
int contentSpecHandle = -1;
if ( elementIndex > -1) {
if ( fGrammar.getElementDecl(elementIndex,fTempElementDecl) ) {
contentSpecHandle = fTempElementDecl.contentSpecIndex;
}
}
return contentSpecHandle;
}
//
// Protected methods
//
// error reporting
/** Report a recoverable schema error. */
private void reportSchemaError(int code, Object[] args) throws Exception {
fErrorReporter.reportError(fErrorReporter.getLocator(),
SchemaMessageProvider.SCHEMA_DOMAIN,
code,
SchemaMessageProvider.MSG_NONE,
args,
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
} // reportSchemaError(int,Object)
/** Report a recoverable xml error. */
protected void reportRecoverableXMLError(int majorCode, int minorCode)
throws Exception {
fErrorReporter.reportError(fErrorReporter.getLocator(),
XMLMessages.XML_DOMAIN,
majorCode,
minorCode,
null,
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
} // reportRecoverableXMLError(int,int)
/** Report a recoverable xml error. */
protected void reportRecoverableXMLError(int majorCode, int minorCode,
int stringIndex1)
throws Exception {
Object[] args = { fStringPool.toString(stringIndex1)};
fErrorReporter.reportError(fErrorReporter.getLocator(),
XMLMessages.XML_DOMAIN,
majorCode,
minorCode,
args,
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
} // reportRecoverableXMLError(int,int,int)
/** Report a recoverable xml error. */
protected void reportRecoverableXMLError(int majorCode, int minorCode,
String string1) throws Exception {
Object[] args = { string1};
fErrorReporter.reportError(fErrorReporter.getLocator(),
XMLMessages.XML_DOMAIN,
majorCode,
minorCode,
args,
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
} // reportRecoverableXMLError(int,int,String)
/** Report a recoverable xml error. */
protected void reportRecoverableXMLError(int majorCode, int minorCode,
int stringIndex1, int stringIndex2)
throws Exception {
Object[] args = { fStringPool.toString(stringIndex1), fStringPool.toString(stringIndex2)};
fErrorReporter.reportError(fErrorReporter.getLocator(),
XMLMessages.XML_DOMAIN,
majorCode,
minorCode,
args,
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
} // reportRecoverableXMLError(int,int,int,int)
/** Report a recoverable xml error. */
protected void reportRecoverableXMLError(int majorCode, int minorCode,
String string1, String string2)
throws Exception {
Object[] args = { string1, string2};
fErrorReporter.reportError(fErrorReporter.getLocator(),
XMLMessages.XML_DOMAIN,
majorCode,
minorCode,
args,
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
} // reportRecoverableXMLError(int,int,String,String)
/** Report a recoverable xml error. */
protected void reportRecoverableXMLError(int majorCode, int minorCode,
String string1, String string2,
String string3) throws Exception {
Object[] args = { string1, string2, string3};
fErrorReporter.reportError(fErrorReporter.getLocator(),
XMLMessages.XML_DOMAIN,
majorCode,
minorCode,
args,
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
} // reportRecoverableXMLError(int,int,String,String,String)
// content spec
/**
* Returns information about which elements can be placed at a particular point
* in the passed element's content model.
* <p>
* Note that the incoming content model to test must be valid at least up to
* the insertion point. If not, then -1 will be returned and the info object
* will not have been filled in.
* <p>
* If, on return, the info.isValidEOC flag is set, then the 'insert after'
* elemement is a valid end of content, i.e. nothing needs to be inserted
* after it to make the parent element's content model valid.
*
* @param elementIndex The index within the <code>ElementDeclPool</code> of the
* element which is being querying.
* @param fullyValid Only return elements that can be inserted and still
* maintain the validity of subsequent elements past the
* insertion point (if any). If the insertion point is at
* the end, and this is true, then only elements that can
* be legal final states will be returned.
* @param info An object that contains the required input data for the method,
* and which will contain the output information if successful.
*
* @return The value -1 if fully valid, else the 0 based index of the child
* that first failed before the insertion point. If the value
* returned is equal to the number of children, then the specified
* children are valid but additional content is required to reach a
* valid ending state.
*
* @exception Exception Thrown on error.
*
* @see InsertableElementsInfo
*/
protected int whatCanGoHere(int elementIndex, boolean fullyValid,
InsertableElementsInfo info) throws Exception {
//
// Do some basic sanity checking on the info packet. First, make sure
// that insertAt is not greater than the child count. It can be equal,
// which means to get appendable elements, but not greater. Or, if
// the current children array is null, that's bad too.
//
// Since the current children array must have a blank spot for where
// the insert is going to be, the child count must always be at least
// one.
//
// Make sure that the child count is not larger than the current children
// array. It can be equal, which means get appendable elements, but not
// greater.
//
if (info.insertAt > info.childCount || info.curChildren == null ||
info.childCount < 1 || info.childCount > info.curChildren.length) {
fErrorReporter.reportError(fErrorReporter.getLocator(),
ImplementationMessages.XERCES_IMPLEMENTATION_DOMAIN,
ImplementationMessages.VAL_WCGHI,
0,
null,
XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
}
int retVal = 0;
try {
// Get the content model for this element
final XMLContentModel cmElem = getElementContentModel(elementIndex);
// And delegate this call to it
retVal = cmElem.whatCanGoHere(fullyValid, info);
} catch (CMException excToCatch) {
// REVISIT - Translate caught error to the protected error handler interface
int majorCode = excToCatch.getErrorCode();
fErrorReporter.reportError(fErrorReporter.getLocator(),
ImplementationMessages.XERCES_IMPLEMENTATION_DOMAIN,
majorCode,
0,
null,
XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
throw excToCatch;
}
return retVal;
} // whatCanGoHere(int,boolean,InsertableElementsInfo):int
// attribute information
/** Protected for use by AttributeValidator classes. */
protected boolean getAttDefIsExternal(QName element, QName attribute) {
int attDefIndex = getAttDef(element, attribute);
if (fGrammarIsDTDGrammar ) {
return((DTDGrammar) fGrammar).getAttributeDeclIsExternal(attDefIndex);
}
return false;
}
//
// Private methods
//
// other
/** Returns true if using a standalone reader. */
private boolean usingStandaloneReader() {
return fStandaloneReader == -1 || fEntityHandler.getReaderId() == fStandaloneReader;
}
/** Returns a locator implementation. */
private LocatorImpl getLocatorImpl(LocatorImpl fillin) {
Locator here = fErrorReporter.getLocator();
if (fillin == null)
return new LocatorImpl(here);
fillin.setPublicId(here.getPublicId());
fillin.setSystemId(here.getSystemId());
fillin.setLineNumber(here.getLineNumber());
fillin.setColumnNumber(here.getColumnNumber());
return fillin;
} // getLocatorImpl(LocatorImpl):LocatorImpl
// initialization
/** Reset pool. */
private void poolReset() {
if (fValidating) { // - el
try {
this.fValID.validate( null, this.fResetID );
this.fValIDRef.validate(null, this.fResetIDRef );
this.fValIDRefs.validate(null, this.fResetIDRef );
} catch (InvalidDatatypeValueException ex) { //should use error reporter
System.err.println("Error re-Initializing: ID,IDRef,IDRefs pools" );
}
}
} // poolReset()
/** Reset common. */
private void resetCommon(StringPool stringPool) throws Exception {
fStringPool = stringPool;
fValidating = fValidationEnabled;
fValidationEnabledByDynamic = false;
fDynamicDisabledByValidation = false;
poolReset();
fCalledStartDocument = false;
fStandaloneReader = -1;
fElementChildrenLength = 0;
fElementDepth = -1;
fSeenRootElement = false;
fSeenDoctypeDecl = false;
fNamespacesScope = null;
fNamespacesPrefix = -1;
fRootElement.clear();
fAttrListHandle = -1;
fCheckedForSchema = false;
fCurrentScope = TOP_LEVEL_SCOPE;
fCurrentSchemaURI = StringPool.EMPTY_STRING;
fEmptyURI = StringPool.EMPTY_STRING;
fXsiPrefix = - 1;
fXsiTypeValidator = null;
// xsi:nill
fNil = false;
fGrammar = null;
fGrammarNameSpaceIndex = StringPool.EMPTY_STRING;
// we will reset fGrammarResolver in XMLParser before passing it to Validator
/*if (fGrammarResolver != null) {
fGrammarResolver.clearGrammarResolver(); //This also clears the Datatype registry
}*/
fGrammarIsDTDGrammar = false;
fGrammarIsSchemaGrammar = false;
//Normalization
fCurrentDV = null;
fFirstChunk = true;
fTrailing = false;
fWhiteSpace = DatatypeValidator.COLLAPSE;
fMatcherStack.clear();
init();
} // resetCommon(StringPool)
/** Initialize. */
private void init() {
fEmptyURI = fStringPool.addSymbol("");
fXsiURI = fStringPool.addSymbol(SchemaSymbols.URI_XSI);
fEMPTYSymbol = fStringPool.addSymbol("EMPTY");
fANYSymbol = fStringPool.addSymbol("ANY");
fMIXEDSymbol = fStringPool.addSymbol("MIXED");
fCHILDRENSymbol = fStringPool.addSymbol("CHILDREN");
fCDATASymbol = fStringPool.addSymbol("CDATA");
fIDSymbol = fStringPool.addSymbol("ID");
fIDREFSymbol = fStringPool.addSymbol("IDREF");
fIDREFSSymbol = fStringPool.addSymbol("IDREFS");
fENTITYSymbol = fStringPool.addSymbol("ENTITY");
fENTITIESSymbol = fStringPool.addSymbol("ENTITIES");
fNMTOKENSymbol = fStringPool.addSymbol("NMTOKEN");
fNMTOKENSSymbol = fStringPool.addSymbol("NMTOKENS");
fNOTATIONSymbol = fStringPool.addSymbol("NOTATION");
fENUMERATIONSymbol = fStringPool.addSymbol("ENUMERATION");
fREQUIREDSymbol = fStringPool.addSymbol("#REQUIRED");
fFIXEDSymbol = fStringPool.addSymbol("#FIXED");
fDATATYPESymbol = fStringPool.addSymbol("<<datatype>>");
fEpsilonIndex = fStringPool.addSymbol("<<CMNODE_EPSILON>>");
fXMLLang = fStringPool.addSymbol("xml:lang");
//optimization - must be called ONLY if validation is turned off. -el
//initDataTypeValidators();
} // init()
/**
* This method should only be invoked when validation
* is turn on.
* fDataTypeReg object of type DatatypeValidatorFactoryImpl
* needs to be initialized.
* In the XMLValidator the table will be by default
* first initialized to 9 validators used by DTD
* validation.
* These Validators are known.
* Later on if we ever find a Schema and need to do
* Schema validation then we will expand this
* registry table of fDataTypeReg.
*/
private void initDataTypeValidators() {
try {
//Initialize Validators
//Datatype Registry
if ( fGrammarResolver != null ) {
fDataTypeReg = (DatatypeValidatorFactoryImpl) fGrammarResolver.getDatatypeRegistry();
fDataTypeReg.initializeDTDRegistry();
}
if ( fDataTypeReg != null ) {
fValID = fDataTypeReg.getDatatypeValidator("ID" );
fValIDRef = fDataTypeReg.getDatatypeValidator("IDREF" );
fValIDRefs = fDataTypeReg.getDatatypeValidator("IDREFS" );
fValENTITY = fDataTypeReg.getDatatypeValidator("ENTITY" );
fValENTITIES = fDataTypeReg.getDatatypeValidator("ENTITIES" );
fValNMTOKEN = fDataTypeReg.getDatatypeValidator("NMTOKEN");
fValNMTOKENS = fDataTypeReg.getDatatypeValidator("NMTOKENS");
fValNOTATION = fDataTypeReg.getDatatypeValidator("NOTATION" );
//Initialize ENTITY & ENTITIES Validatorh
Object[] packageArgsEntityVal = { (Object) this.fEntityHandler,
(Object) this.fStringPool};
fValidateENTITYMsg.setDatatypeObject( (Object ) packageArgsEntityVal);
fValENTITY.validate( null, fValidateENTITYMsg );
fValENTITIES.validate( null, fValidateENTITYMsg );
}
} catch ( InvalidDatatypeValueException ex ) {
System.err.println("Error: " + ex.getLocalizedMessage() );//Should not happen
}
}
// other
// default attribute
/** addDefaultAttributes. */
private int addDefaultAttributes(int elementIndex, XMLAttrList attrList, int attrIndex, boolean validationEnabled, boolean standalone) throws Exception {
//System.out.println("XMLValidator#addDefaultAttributes");
//System.out.print(" ");
//fGrammar.printAttributes(elementIndex);
//
// Check after all specified attrs are scanned
// (1) report error for REQUIRED attrs that are missing (V_TAGc)
// (2) report error for PROHIBITED attrs that are present (V_TAGc)
// (3) check that FIXED attrs have matching value (V_TAGd)
// (4) add default attrs (FIXED and NOT_FIXED)
//
fGrammar.getElementDecl(elementIndex,fTempElementDecl);
int elementNameIndex = fTempElementDecl.name.localpart;
int attlistIndex = fGrammar.getFirstAttributeDeclIndex(elementIndex);
int firstCheck = attrIndex;
int lastCheck = -1;
while (attlistIndex != -1) {
fGrammar.getAttributeDecl(attlistIndex, fTempAttDecl);
int attPrefix = fTempAttDecl.name.prefix;
int attName = fTempAttDecl.name.localpart;
int attType = attributeTypeName(fTempAttDecl);
int attDefType =fTempAttDecl.defaultType;
int attValue = -1 ;
if (fTempAttDecl.defaultValue != null ) {
attValue = fStringPool.addSymbol(fTempAttDecl.defaultValue);
}
boolean specified = false;
boolean required = (attDefType & XMLAttributeDecl.DEFAULT_TYPE_REQUIRED) > 0;
boolean prohibited = (attDefType & XMLAttributeDecl.DEFAULT_TYPE_PROHIBITED) > 0;
boolean fixed = (attDefType & XMLAttributeDecl.DEFAULT_TYPE_FIXED) > 0;
if (firstCheck != -1) {
boolean cdata = attType == fCDATASymbol;
if (!cdata || required || prohibited || attValue != -1) {
int i = attrList.getFirstAttr(firstCheck);
while (i != -1 && (lastCheck == -1 || i <= lastCheck)) {
if ( (fGrammarIsDTDGrammar && (attrList.getAttrName(i) == fTempAttDecl.name.rawname)) ||
( fStringPool.equalNames(attrList.getAttrLocalpart(i), attName)
&& fStringPool.equalNames(attrList.getAttrURI(i), fTempAttDecl.name.uri) ) ) {
if (prohibited && validationEnabled) {
Object[] args = { fStringPool.toString(elementNameIndex),
fStringPool.toString(attName)};
fErrorReporter.reportError(fErrorReporter.getLocator(),
SchemaMessageProvider.SCHEMA_DOMAIN,
SchemaMessageProvider.ProhibitedAttributePresent,
SchemaMessageProvider.MSG_NONE,
args,
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
}
if (validationEnabled && fixed) {
int alistValue = attrList.getAttValue(i);
if (alistValue != attValue &&
!fStringPool.toString(alistValue).equals(fStringPool.toString(attValue))) {
Object[] args = { fStringPool.toString(elementNameIndex),
fStringPool.toString(attName),
fStringPool.toString(alistValue),
fStringPool.toString(attValue)};
fErrorReporter.reportError(fErrorReporter.getLocator(),
XMLMessages.XML_DOMAIN,
XMLMessages.MSG_FIXED_ATTVALUE_INVALID,
XMLMessages.VC_FIXED_ATTRIBUTE_DEFAULT,
args,
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
}
}
specified = true;
break;
}
i = attrList.getNextAttr(i);
}
}
}
if (!specified) {
if (required) {
if (validationEnabled) {
Object[] args = { fStringPool.toString(elementNameIndex),
fStringPool.toString(attName)};
fErrorReporter.reportError(fErrorReporter.getLocator(),
XMLMessages.XML_DOMAIN,
XMLMessages.MSG_REQUIRED_ATTRIBUTE_NOT_SPECIFIED,
XMLMessages.VC_REQUIRED_ATTRIBUTE,
args,
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
}
} else if (attValue != -1) {
if (validationEnabled && standalone ) {
if ( fGrammarIsDTDGrammar
&& ((DTDGrammar) fGrammar).getAttributeDeclIsExternal(attlistIndex) ) {
Object[] args = { fStringPool.toString(elementNameIndex),
fStringPool.toString(attName)};
fErrorReporter.reportError(fErrorReporter.getLocator(),
XMLMessages.XML_DOMAIN,
XMLMessages.MSG_DEFAULTED_ATTRIBUTE_NOT_SPECIFIED,
XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION,
args,
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
}
}
if (validationEnabled) {
if (attType == fIDREFSymbol) {
this.fValIDRef.validate( fStringPool.toString(attValue), null );
} else if (attType == fIDREFSSymbol) {
this.fValIDRefs.validate( fStringPool.toString(attValue), null );
}
}
if (attrIndex == -1) {
attrIndex = attrList.startAttrList();
}
// REVISIT: Validation. What should the prefix be?
fTempQName.setValues(attPrefix, attName, attName, fTempAttDecl.name.uri);
int newAttr = attrList.addAttr(fTempQName,
attValue, attType,
false, false);
if (lastCheck == -1) {
lastCheck = newAttr;
}
}
}
attlistIndex = fGrammar.getNextAttributeDeclIndex(attlistIndex);
}
return attrIndex;
} // addDefaultAttributes(int,XMLAttrList,int,boolean,boolean):int
/** addDTDDefaultAttributes. */
private int addDTDDefaultAttributes(QName element, XMLAttrList attrList, int attrIndex, boolean validationEnabled, boolean standalone) throws Exception {
//
// Check after all specified attrs are scanned
// (1) report error for REQUIRED attrs that are missing (V_TAGc)
// (2) check that FIXED attrs have matching value (V_TAGd)
// (3) add default attrs (FIXED and NOT_FIXED)
//
int elementIndex = fGrammar.getElementDeclIndex(element, -1);
if (elementIndex == -1) {
return attrIndex;
}
fGrammar.getElementDecl(elementIndex,fTempElementDecl);
int elementNameIndex = fTempElementDecl.name.rawname;
int attlistIndex = fGrammar.getFirstAttributeDeclIndex(elementIndex);
int firstCheck = attrIndex;
int lastCheck = -1;
while (attlistIndex != -1) {
fGrammar.getAttributeDecl(attlistIndex, fTempAttDecl);
// TO DO: For ericye Debug only
/***
if (fTempAttDecl != null) {
XMLElementDecl element = new XMLElementDecl();
fGrammar.getElementDecl(elementIndex, element);
System.out.println("element: "+fStringPool.toString(element.name.localpart));
System.out.println("attlistIndex " + attlistIndex + "\n"+
"attName : '"+fStringPool.toString(fTempAttDecl.name.localpart) + "'\n"
+ "attType : "+fTempAttDecl.type + "\n"
+ "attDefaultType : "+fTempAttDecl.defaultType + "\n"
+ "attDefaultValue : '"+fTempAttDecl.defaultValue + "'\n"
+ attrList.getLength() +"\n"
);
}
/***/
int attPrefix = fTempAttDecl.name.prefix;
int attName = fTempAttDecl.name.rawname;
int attLocalpart = fTempAttDecl.name.localpart;
int attType = attributeTypeName(fTempAttDecl);
int attDefType =fTempAttDecl.defaultType;
int attValue = -1 ;
if (fTempAttDecl.defaultValue != null ) {
attValue = fStringPool.addSymbol(fTempAttDecl.defaultValue);
}
boolean specified = false;
boolean required = (attDefType & XMLAttributeDecl.DEFAULT_TYPE_REQUIRED)>0;
/****
if (fValidating && fGrammar != null && fGrammarIsDTDGrammar && attValue != -1) {
normalizeAttValue(null, fTempAttDecl.name,
attValue,attType,fTempAttDecl.list,
fTempAttDecl.enumeration);
}
/****/
if (firstCheck != -1) {
boolean cdata = attType == fCDATASymbol;
if (!cdata || required || attValue != -1) {
int i = attrList.getFirstAttr(firstCheck);
while (i != -1 && (lastCheck == -1 || i <= lastCheck)) {
if ( attrList.getAttrName(i) == fTempAttDecl.name.rawname ) {
if (validationEnabled && (attDefType & XMLAttributeDecl.DEFAULT_TYPE_FIXED) > 0) {
int alistValue = attrList.getAttValue(i);
if (alistValue != attValue &&
!fStringPool.toString(alistValue).equals(fStringPool.toString(attValue))) {
Object[] args = { fStringPool.toString(elementNameIndex),
fStringPool.toString(attName),
fStringPool.toString(alistValue),
fStringPool.toString(attValue)};
fErrorReporter.reportError(fErrorReporter.getLocator(),
XMLMessages.XML_DOMAIN,
XMLMessages.MSG_FIXED_ATTVALUE_INVALID,
XMLMessages.VC_FIXED_ATTRIBUTE_DEFAULT,
args,
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
}
}
specified = true;
break;
}
i = attrList.getNextAttr(i);
}
}
}
if (!specified) {
if (required) {
if (validationEnabled) {
Object[] args = { fStringPool.toString(elementNameIndex),
fStringPool.toString(attName)};
fErrorReporter.reportError(fErrorReporter.getLocator(),
XMLMessages.XML_DOMAIN,
XMLMessages.MSG_REQUIRED_ATTRIBUTE_NOT_SPECIFIED,
XMLMessages.VC_REQUIRED_ATTRIBUTE,
args,
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
}
} else if (attValue != -1) {
if (validationEnabled && standalone ){
if ( fGrammarIsDTDGrammar
&& ((DTDGrammar) fGrammar).getAttributeDeclIsExternal(attlistIndex) ) {
Object[] args = { fStringPool.toString(elementNameIndex),
fStringPool.toString(attName)};
fErrorReporter.reportError(fErrorReporter.getLocator(),
XMLMessages.XML_DOMAIN,
XMLMessages.MSG_DEFAULTED_ATTRIBUTE_NOT_SPECIFIED,
XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION,
args,
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
}
}
if (validationEnabled) {
if (attType == fIDREFSymbol) {
this.fValIDRef.validate( fStringPool.toString(attValue), null );
}
else if (attType == fIDREFSSymbol) {
this.fValIDRefs.validate( fStringPool.toString(attValue), null );
}
}
if (attrIndex == -1) {
attrIndex = attrList.startAttrList();
}
fTempQName.setValues(attPrefix, attLocalpart, attName, fTempAttDecl.name.uri);
int newAttr = attrList.addAttr(fTempQName,
attValue, attType,
false, false);
if (lastCheck == -1) {
lastCheck = newAttr;
}
}
}
attlistIndex = fGrammar.getNextAttributeDeclIndex(attlistIndex);
}
return attrIndex;
} // addDTDDefaultAttributes(int,XMLAttrList,int,boolean,boolean):int
// content models
/** Queries the content model for the specified element index. */
private XMLContentModel getElementContentModel(int elementIndex) throws CMException {
XMLContentModel contentModel = null;
if ( elementIndex > -1) {
if ( fGrammar.getElementDecl(elementIndex,fTempElementDecl) ) {
contentModel = fGrammar.getElementContentModel(elementIndex, fSGComparator);
}
}
//return fGrammar.getElementContentModel(elementIndex);
return contentModel;
}
// query attribute information
/** Returns an attribute definition for an element type. */
// this is only used by DTD validation.
private int getAttDef(QName element, QName attribute) {
if (fGrammar != null) {
int scope = fCurrentScope;
if (element.uri > -1) {
scope = TOP_LEVEL_SCOPE;
}
int elementIndex = fGrammar.getElementDeclIndex(element,scope);
if (elementIndex == -1) {
return -1;
}
int attDefIndex = fGrammar.getFirstAttributeDeclIndex(elementIndex);
while (attDefIndex != -1) {
fGrammar.getAttributeDecl(attDefIndex, fTempAttributeDecl);
if (fTempAttributeDecl.name.localpart == attribute.localpart &&
fTempAttributeDecl.name.uri == attribute.uri ) {
return attDefIndex;
}
attDefIndex = fGrammar.getNextAttributeDeclIndex(attDefIndex);
}
}
return -1;
} // getAttDef(QName,QName)
/** Returns an attribute definition for an element type. */
private int getAttDefByElementIndex(int elementIndex, QName attribute) {
if (fGrammar != null && elementIndex > -1) {
if (elementIndex == -1) {
return -1;
}
int attDefIndex = fGrammar.getFirstAttributeDeclIndex(elementIndex);
while (attDefIndex != -1) {
fGrammar.getAttributeDecl(attDefIndex, fTempAttDecl);
if (fGrammarIsDTDGrammar) {
if (fTempAttDecl.name.rawname == attribute.rawname )
return attDefIndex;
} else
if (fTempAttDecl.name.localpart == attribute.localpart &&
fTempAttDecl.name.uri == attribute.uri ) {
return attDefIndex;
}
if (fGrammarIsSchemaGrammar) {
if (fTempAttDecl.type == XMLAttributeDecl.TYPE_ANY_ANY) {
return attDefIndex;
} else if (fTempAttDecl.type == XMLAttributeDecl.TYPE_ANY_OTHER) {
if (attribute.uri != fTempAttDecl.name.uri) {
return attDefIndex;
}
} else if (fTempAttDecl.type == XMLAttributeDecl.TYPE_ANY_LIST) {
if (fStringPool.stringInList(fTempAttDecl.enumeration, attribute.uri)) {
return attDefIndex;
}
}
}
attDefIndex = fGrammar.getNextAttributeDeclIndex(attDefIndex);
}
}
return -1;
} // getAttDef(QName,QName)
// validation
/** Root element specified. */
private void rootElementSpecified(QName rootElement) throws Exception {
if ( fLoadDTDGrammar )
// initialize the grammar to be the default one,
// it definitely should be a DTD Grammar at this case;
if (fGrammar == null) {
fGrammar = fGrammarResolver.getGrammar("");
if (fDynamicValidation && fGrammar==null) {
fValidating = false;
}
if (fGrammar != null) {
if (fGrammar instanceof DTDGrammar) {
fGrammarIsDTDGrammar = true;
fGrammarIsSchemaGrammar = false;
} else if ( fGrammar instanceof SchemaGrammar ) {
fGrammarIsSchemaGrammar = true;
fGrammarIsDTDGrammar = false;
}
fGrammarNameSpaceIndex = fEmptyURI;
}
}
if (fValidating) {
if ( fGrammarIsDTDGrammar &&
((DTDGrammar) fGrammar).getRootElementQName(fRootElement) ) {
String root1 = fStringPool.toString(fRootElement.rawname);
String root2 = fStringPool.toString(rootElement.rawname);
if (!root1.equals(root2)) {
reportRecoverableXMLError(XMLMessages.MSG_ROOT_ELEMENT_TYPE,
XMLMessages.VC_ROOT_ELEMENT_TYPE,
fRootElement.rawname,
rootElement.rawname);
}
}
}
if (fNamespacesEnabled) {
if (fNamespacesScope == null) {
fNamespacesScope = new NamespacesScope(this);
fNamespacesPrefix = fStringPool.addSymbol("xmlns");
//fNamespacesScope.setNamespaceForPrefix(fNamespacesPrefix, StringPool.EMPTY_STRING);
// xxxxx
fNamespacesScope.setNamespaceForPrefix(fNamespacesPrefix, -1);
int xmlSymbol = fStringPool.addSymbol("xml");
int xmlNamespace = fStringPool.addSymbol("http://www.w3.org/XML/1998/namespace");
fNamespacesScope.setNamespaceForPrefix(xmlSymbol, xmlNamespace);
}
}
} // rootElementSpecified(QName)
/** Switchs to correct validating symbol tables when Schema changes.*/
private boolean switchGrammar(int newGrammarNameSpaceIndex) throws Exception {
Grammar tempGrammar = fGrammarResolver.getGrammar(fStringPool.toString(newGrammarNameSpaceIndex));
if (tempGrammar == null) {
// This is a case where namespaces is on with a DTD grammar.
tempGrammar = fGrammarResolver.getGrammar("");
}
if (tempGrammar == null) {
return false;
} else {
fGrammar = tempGrammar;
if (fGrammar instanceof DTDGrammar) {
fGrammarIsDTDGrammar = true;
fGrammarIsSchemaGrammar = false;
} else if ( fGrammar instanceof SchemaGrammar ) {
fGrammarIsSchemaGrammar = true;
fGrammarIsDTDGrammar = false;
}
return true;
}
}
/** Binds namespaces to the element and attributes. */
private void bindNamespacesToElementAndAttributes(QName element,
XMLAttrList attrList)
throws Exception {
fNamespacesScope.increaseDepth();
//Vector schemaCandidateURIs = null;
Hashtable locationUriPairs = null;
if (fAttrListHandle != -1) {
int index = attrList.getFirstAttr(fAttrListHandle);
while (index != -1) {
int attName = attrList.getAttrName(index);
int attPrefix = attrList.getAttrPrefix(index);
if (fStringPool.equalNames(attName, fXMLLang)) {
/***
// NOTE: This check is done in the validateElementsAndAttributes
// method.
fDocumentScanner.checkXMLLangAttributeValue(attrList.getAttValue(index));
/***/
} else if (fStringPool.equalNames(attName, fNamespacesPrefix)) {
int uri = fStringPool.addSymbol(attrList.getAttValue(index));
fNamespacesScope.setNamespaceForPrefix(StringPool.EMPTY_STRING, uri);
} else {
if (attPrefix == fNamespacesPrefix) {
int nsPrefix = attrList.getAttrLocalpart(index);
int uri = fStringPool.addSymbol(attrList.getAttValue(index));
fNamespacesScope.setNamespaceForPrefix(nsPrefix, uri);
if (fValidating && fSchemaValidation) {
boolean seeXsi = false;
String attrValue = fStringPool.toString(attrList.getAttValue(index));
if (attrValue.equals(SchemaSymbols.URI_XSI)) {
fXsiPrefix = nsPrefix;
seeXsi = true;
}
if (!seeXsi) {
/***
if (schemaCandidateURIs == null) {
schemaCandidateURIs = new Vector();
}
schemaCandidateURIs.addElement( fStringPool.toString(uri) );
/***/
}
}
}
}
index = attrList.getNextAttr(index);
}
// if validating, walk through the list again to deal with "xsi:...."
if (fValidating && fSchemaValidation) {
fXsiTypeAttValue = -1;
index = attrList.getFirstAttr(fAttrListHandle);
while (index != -1) {
int attName = attrList.getAttrName(index);
int attPrefix = attrList.getAttrPrefix(index);
if (fStringPool.equalNames(attName, fNamespacesPrefix)) {
// REVISIT
} else {
if ( DEBUG_SCHEMA_VALIDATION ) {
System.out.println("deal with XSI");
System.out.println("before find XSI: "+fStringPool.toString(attPrefix)
+","+fStringPool.toString(fXsiPrefix) );
}
if ( fXsiPrefix != -1 && attPrefix == fXsiPrefix ) {
if (DEBUG_SCHEMA_VALIDATION) {
System.out.println("find XSI: "+fStringPool.toString(attPrefix)
+","+fStringPool.toString(attName) );
}
int localpart = attrList.getAttrLocalpart(index);
if (localpart == fStringPool.addSymbol(SchemaSymbols.XSI_SCHEMALOCACTION)) {
if (locationUriPairs == null) {
locationUriPairs = new Hashtable();
}
parseSchemaLocation(fStringPool.toString(attrList.getAttValue(index)), locationUriPairs);
} else if (localpart == fStringPool.addSymbol(SchemaSymbols.XSI_NONAMESPACESCHEMALOCACTION)) {
if (locationUriPairs == null) {
locationUriPairs = new Hashtable();
}
locationUriPairs.put(fStringPool.toString(attrList.getAttValue(index)), "");
/***/
// NOTE: This is the *wrong* solution to the problem
// of finding the grammar associated to elements
// when the grammar does *not* have a target
// namespace!!! -Ac
if (fNamespacesScope != null) {
//bind prefix "" to URI "" in this case
fNamespacesScope.setNamespaceForPrefix( fStringPool.addSymbol(""),
fStringPool.addSymbol(""));
}
/***/
}
else if ( localpart == fStringPool.addSymbol(SchemaSymbols.XSI_TYPE) ) {
fXsiTypeAttValue = attrList.getAttValue(index);
}
else if ( localpart == fStringPool.addSymbol(SchemaSymbols.ATT_NIL) ) {
fNil = (fStringPool.toString(attrList.getAttValue(index)).equals("true")) ? true: false;
}
// REVISIT: should we break here?
//break;
}
}
index = attrList.getNextAttr(index);
}
// try to resolve all the grammars here
if (locationUriPairs != null) {
Enumeration locations = locationUriPairs.keys();
while (locations.hasMoreElements()) {
String loc = (String) locations.nextElement();
String uri = (String) locationUriPairs.get(loc);
resolveSchemaGrammar( loc, uri);
//schemaCandidateURIs.removeElement(uri);
}
}
//TO DO: This should be a feature that can be turned on or off
/*****
for (int i=0; i< schemaCandidateURIs.size(); i++) {
String uri = (String) schemaCandidateURIs.elementAt(i);
resolveSchemaGrammar(uri);
}
/*****/
}
}
// bind element to URI
int prefix = element.prefix != -1 ? element.prefix : 0;
int uri = fNamespacesScope.getNamespaceForPrefix(prefix);
if (element.prefix != -1 || uri != StringPool.EMPTY_STRING) {
element.uri = uri;
if (element.uri == StringPool.EMPTY_STRING) {
Object[] args = { fStringPool.toString(element.prefix)};
fErrorReporter.reportError(fErrorReporter.getLocator(),
XMLMessages.XMLNS_DOMAIN,
XMLMessages.MSG_PREFIX_DECLARED,
XMLMessages.NC_PREFIX_DECLARED,
args,
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
}
}
if (fAttrListHandle != -1) {
int index = attrList.getFirstAttr(fAttrListHandle);
while (index != -1) {
int attName = attrList.getAttrName(index);
if (!fStringPool.equalNames(attName, fNamespacesPrefix)) {
int attPrefix = attrList.getAttrPrefix(index);
if (attPrefix != fNamespacesPrefix) {
if (attPrefix != -1 ) {
int attrUri = fNamespacesScope.getNamespaceForPrefix(attPrefix);
if (attrUri == -1) {
Object[] args = { fStringPool.toString(attPrefix)};
fErrorReporter.reportError(fErrorReporter.getLocator(),
XMLMessages.XMLNS_DOMAIN,
XMLMessages.MSG_PREFIX_DECLARED,
XMLMessages.NC_PREFIX_DECLARED,
args,
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
}
attrList.setAttrURI(index, attrUri);
}
}
}
index = attrList.getNextAttr(index);
}
}
} // bindNamespacesToElementAndAttributes(QName,XMLAttrList)
void parseSchemaLocation(String schemaLocationStr, Hashtable locationUriPairs){
if (locationUriPairs != null) {
StringTokenizer tokenizer = new StringTokenizer(schemaLocationStr, " \n\t\r", false);
int tokenTotal = tokenizer.countTokens();
if (tokenTotal % 2 != 0 ) {
// TO DO: report warning - malformed schemaLocation string
} else {
while (tokenizer.hasMoreTokens()) {
String uri = tokenizer.nextToken();
String location = tokenizer.nextToken();
locationUriPairs.put(location, uri);
}
}
} else {
// TO DO: should report internal error here
}
}// parseSchemaLocaltion(String, Hashtable)
private void resolveSchemaGrammar( String loc, String uri) throws Exception {
SchemaGrammar grammar = (SchemaGrammar) fGrammarResolver.getGrammar(uri);
if (grammar == null) {
if (fSchemaGrammarParser == null) {
//
// creating a parser for schema only once per parser instance
// leads to less objects, but increases time we spend in reset()
//
fSchemaGrammarParser = new DOMParser();
fSchemaGrammarParser.setEntityResolver( new Resolver(fEntityHandler) );
fSchemaGrammarParser.setErrorHandler( new ErrorHandler() );
try {
fSchemaGrammarParser.setFeature("http://xml.org/sax/features/validation", false);
fSchemaGrammarParser.setFeature("http://xml.org/sax/features/namespaces", true);
fSchemaGrammarParser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", false);
} catch ( org.xml.sax.SAXNotRecognizedException e ) {
e.printStackTrace();
} catch ( org.xml.sax.SAXNotSupportedException e ) {
e.printStackTrace();
}
}
// expand it before passing it to the parser
InputSource source = null;
EntityResolver currentER = fSchemaGrammarParser.getEntityResolver();
if (currentER != null) {
source = currentER.resolveEntity("", loc);
}
if (source == null) {
loc = fEntityHandler.expandSystemId(loc);
source = new InputSource(loc);
}
try {
fSchemaGrammarParser.parse( source );
} catch ( IOException e ) {
e.printStackTrace();
} catch ( SAXException e ) {
reportRecoverableXMLError( XMLMessages.MSG_GENERIC_SCHEMA_ERROR,
XMLMessages.SCHEMA_GENERIC_ERROR, e.getMessage() );
}
Document document = fSchemaGrammarParser.getDocument(); //Our Grammar
TraverseSchema tst = null;
try {
if (DEBUG_SCHEMA_VALIDATION) {
System.out.println("I am geting the Schema Document");
}
Element root = null;
if (document != null) {
root = document.getDocumentElement();// This is what we pass to TraverserSchema
}
if (root == null) {
reportRecoverableXMLError(XMLMessages.MSG_GENERIC_SCHEMA_ERROR, XMLMessages.SCHEMA_GENERIC_ERROR, "Can't get back Schema document's root element :" + loc);
} else {
if (uri == null || !uri.equals(root.getAttribute(SchemaSymbols.ATT_TARGETNAMESPACE)) ) {
reportRecoverableXMLError(XMLMessages.MSG_GENERIC_SCHEMA_ERROR, XMLMessages.SCHEMA_GENERIC_ERROR, "Schema in " + loc + " has a different target namespace " +
"from the one specified in the instance document :" + uri);
}
grammar = new SchemaGrammar();
grammar.setGrammarDocument(document);
// Since we've just constructed a schema grammar, we should make sure we know what we've done.
fGrammarIsSchemaGrammar = true;
fGrammarIsDTDGrammar = false;
//At this point we should expand the registry table.
// pass parser's entity resolver (local Resolver), which also has reference to user's
// entity resolver, and also can fall-back to entityhandler's expandSystemId()
tst = new TraverseSchema( root, fStringPool, (SchemaGrammar)grammar, fGrammarResolver, fErrorReporter, source.getSystemId(), currentER, getSchemaFullCheckingEnabled());
//allowing xsi:schemaLocation to appear on any element
String targetNS = root.getAttribute("targetNamespace");
fGrammarNameSpaceIndex = fStringPool.addSymbol(targetNS);
fGrammarResolver.putGrammar(targetNS, grammar);
fGrammar = (SchemaGrammar)grammar;
}
} catch (Exception e) {
e.printStackTrace(System.err);
}
}
}
private void resolveSchemaGrammar(String uri) throws Exception{
resolveSchemaGrammar(uri, uri);
}
/*
* for <notation> must resolve values "b:myNotation"
*
* @param value string value of element or attribute
* @return
* @exception Exception
*/
private String bindNotationURI (String value) throws Exception{
int colonP = value.indexOf(":");
String prefix = "";
String localpart = value;
if (colonP > -1) {
prefix = value.substring(0,colonP);
localpart = value.substring(colonP+1);
}
String uri = "";
int uriIndex = StringPool.EMPTY_STRING;
if (fNamespacesScope != null) {
//if prefix.equals("") it would bing to xmlns URI
uriIndex = fNamespacesScope.getNamespaceForPrefix(fStringPool.addSymbol(prefix));
if (uriIndex > 0) {
return fStringPool.toString(uriIndex)+":"+localpart;
} else if (fGrammarNameSpaceIndex!=-1){
// REVISIT: try binding to current namespace
// trying to solve the case:
// <v01:root xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
// xmlns:my ="http://www.schemaTest.org/my"
// might not work in all cases (need clarification from schema?)
return fStringPool.toString( fGrammarNameSpaceIndex)+":"+localpart;
}
}
return value;
}
static class Resolver implements EntityResolver {
//
// Constants
//
private static final String SYSTEM[] = {
"http://www.w3.org/2001/XMLSchema.dtd",
"http://www.w3.org/XMLSchema/datatypes.dtd",
"http://www.w3.org/XMLSchema/versionInfo.ent",
};
private static final String PATH[] = {
"structures.dtd",
"datatypes.dtd",
"versionInfo.ent",
};
//
// Data
//
private DefaultEntityHandler fEntityHandler;
//
// Constructors
//
public Resolver(DefaultEntityHandler handler) {
fEntityHandler = handler;
}
//
// EntityResolver methods
//
public InputSource resolveEntity(String publicId, String systemId)
throws IOException, SAXException {
// looking for the schema DTDs?
for (int i = 0; i < SYSTEM.length; i++) {
if (systemId.equals(SYSTEM[i])) {
InputSource source = new InputSource(getClass().getResourceAsStream(PATH[i]));
source.setPublicId(publicId);
source.setSystemId(systemId);
return source;
}
}
// first try to resolve using user's entity resolver
EntityResolver resolver = fEntityHandler.getEntityResolver();
if (resolver != null) {
InputSource source = resolver.resolveEntity(publicId, systemId);
if (source != null) {
return source;
}
}
// use default resolution
return new InputSource(fEntityHandler.expandSystemId(systemId));
} // resolveEntity(String,String):InputSource
} // class Resolver
static class ErrorHandler implements org.xml.sax.ErrorHandler {
/** Warning. */
public void warning(SAXParseException ex) {
System.err.println("[Warning] "+
getLocationString(ex)+": "+
ex.getMessage());
}
/** Error. */
public void error(SAXParseException ex) {
System.err.println("[Error] "+
getLocationString(ex)+": "+
ex.getMessage());
}
/** Fatal error. */
public void fatalError(SAXParseException ex) {
System.err.println("[Fatal Error] "+
getLocationString(ex)+": "+
ex.getMessage());
//throw ex;
}
//
// Private methods
//
/** Returns a string of the location. */
private String getLocationString(SAXParseException ex) {
StringBuffer str = new StringBuffer();
String systemId_ = ex.getSystemId();
if (systemId_ != null) {
int index = systemId_.lastIndexOf('/');
if (index != -1)
systemId_ = systemId_.substring(index + 1);
str.append(systemId_);
}
str.append(':');
str.append(ex.getLineNumber());
str.append(':');
str.append(ex.getColumnNumber());
return str.toString();
} // getLocationString(SAXParseException):String
}
private int attributeTypeName(XMLAttributeDecl attrDecl) {
switch (attrDecl.type) {
case XMLAttributeDecl.TYPE_ENTITY: {
return attrDecl.list ? fENTITIESSymbol : fENTITYSymbol;
}
case XMLAttributeDecl.TYPE_ENUMERATION: {
String enumeration = fStringPool.stringListAsString(attrDecl.enumeration);
return fStringPool.addSymbol(enumeration);
}
case XMLAttributeDecl.TYPE_ID: {
return fIDSymbol;
}
case XMLAttributeDecl.TYPE_IDREF: {
return attrDecl.list ? fIDREFSSymbol : fIDREFSymbol;
}
case XMLAttributeDecl.TYPE_NMTOKEN: {
return attrDecl.list ? fNMTOKENSSymbol : fNMTOKENSSymbol;
}
case XMLAttributeDecl.TYPE_NOTATION: {
return fNOTATIONSymbol;
}
}
return fCDATASymbol;
}
/** Validates element and attributes. */
private void validateElementAndAttributes(QName element,
XMLAttrList attrList)
throws Exception {
if ((fGrammarIsSchemaGrammar && fElementDepth >= 0 && fValidationFlagStack[fElementDepth] != 0 )||
(fGrammar == null && !fValidating && !fNamespacesEnabled) ) {
fCurrentElementIndex = -1;
fCurrentContentSpecType = -1;
fInElementContent = false;
if (fAttrListHandle != -1) {
fAttrList.endAttrList();
int index = fAttrList.getFirstAttr(fAttrListHandle);
while (index != -1) {
if (fStringPool.equalNames(fAttrList.getAttrName(index), fXMLLang)) {
fDocumentScanner.checkXMLLangAttributeValue(fAttrList.getAttValue(index));
break;
}
index = fAttrList.getNextAttr(index);
}
}
return;
}
int elementIndex = -1;
int contentSpecType = -1;
boolean skipThisOne = false;
boolean laxThisOne = false;
if ( fGrammarIsSchemaGrammar && fElementDepth > -1 && fContentLeafStack[fElementDepth] != null ) {
ContentLeafNameTypeVector cv = fContentLeafStack[fElementDepth];
QName[] fElemMap = cv.leafNames;
for (int i=0; i<cv.leafCount; i++) {
int type = cv.leafTypes[i] ;
//System.out.println("******* see a ANY_OTHER_SKIP, "+type+","+element+","+fElemMap[i]+"\n*******");
if (type == XMLContentSpec.CONTENTSPECNODE_LEAF) {
if (fElemMap[i].uri==element.uri
&& fElemMap[i].localpart == element.localpart)
break;
} else if (type == XMLContentSpec.CONTENTSPECNODE_ANY) {
int uri = fElemMap[i].uri;
if (uri == StringPool.EMPTY_STRING || uri == element.uri) {
break;
}
} else if (type == XMLContentSpec.CONTENTSPECNODE_ANY_LOCAL) {
if (element.uri == StringPool.EMPTY_STRING) {
break;
}
} else if (type == XMLContentSpec.CONTENTSPECNODE_ANY_OTHER) {
if (fElemMap[i].uri != element.uri) {
break;
}
} else if (type == XMLContentSpec.CONTENTSPECNODE_ANY_SKIP) {
int uri = fElemMap[i].uri;
if (uri == StringPool.EMPTY_STRING || uri == element.uri) {
skipThisOne = true;
break;
}
} else if (type == XMLContentSpec.CONTENTSPECNODE_ANY_LOCAL_SKIP) {
if (element.uri == StringPool.EMPTY_STRING) {
skipThisOne = true;
break;
}
} else if (type == XMLContentSpec.CONTENTSPECNODE_ANY_OTHER_SKIP) {
if (fElemMap[i].uri != element.uri) {
skipThisOne = true;
break;
}
} else if (type == XMLContentSpec.CONTENTSPECNODE_ANY_LAX) {
int uri = fElemMap[i].uri;
if (uri == StringPool.EMPTY_STRING || uri == element.uri) {
laxThisOne = true;
break;
}
} else if (type == XMLContentSpec.CONTENTSPECNODE_ANY_LOCAL_LAX) {
if (element.uri == StringPool.EMPTY_STRING) {
laxThisOne = true;
break;
}
} else if (type == XMLContentSpec.CONTENTSPECNODE_ANY_OTHER_LAX) {
if (fElemMap[i].uri != element.uri) {
laxThisOne = true;
break;
}
}
}
}
if (skipThisOne) {
fNeedValidationOff = true;
} else {
//REVISIT: is this the right place to check on if the Schema has changed?
if ( fNamespacesEnabled && fValidating &&
element.uri != fGrammarNameSpaceIndex &&
element.uri != StringPool.EMPTY_STRING) {
fGrammarNameSpaceIndex = element.uri;
boolean success = switchGrammar(fGrammarNameSpaceIndex);
if (!success && !laxThisOne) {
reportRecoverableXMLError(XMLMessages.MSG_GENERIC_SCHEMA_ERROR, XMLMessages.SCHEMA_GENERIC_ERROR,
"Grammar with uri 2: " + fStringPool.toString(fGrammarNameSpaceIndex)
+ " , can not be found");
}
}
if ( fGrammar != null ) {
if (DEBUG_SCHEMA_VALIDATION) {
System.out.println("*******Lookup element: uri: " + fStringPool.toString(element.uri)+
" localpart: '" + fStringPool.toString(element.localpart)
+"' and scope : " + fCurrentScope+"\n");
}
elementIndex = fGrammar.getElementDeclIndex(element,fCurrentScope);
if (elementIndex == -1 ) {
elementIndex = fGrammar.getElementDeclIndex(element, TOP_LEVEL_SCOPE);
}
if (elementIndex == -1) {
// if validating based on a Schema, try to resolve the element again by searching in its type's ancestor types
if (fGrammarIsSchemaGrammar && fCurrentElementIndex != -1) {
TraverseSchema.ComplexTypeInfo baseTypeInfo = null;
baseTypeInfo = ((SchemaGrammar)fGrammar).getElementComplexTypeInfo(fCurrentElementIndex);
int aGrammarNSIndex = fGrammarNameSpaceIndex;
while (baseTypeInfo != null) {
elementIndex = fGrammar.getElementDeclIndex(element, baseTypeInfo.scopeDefined);
if (elementIndex > -1 ) {
//System.out.println("found element index for " + fStringPool.toString(element.localpart));
// update the current Grammar NS index if resolving element succeed.
fGrammarNameSpaceIndex = aGrammarNSIndex;
break;
}
baseTypeInfo = baseTypeInfo.baseComplexTypeInfo;
if (baseTypeInfo != null) {
String baseTName = baseTypeInfo.typeName;
if (!baseTName.startsWith("#")) {
int comma = baseTName.indexOf(',');
aGrammarNSIndex = fStringPool.addSymbol(baseTName.substring(0,comma).trim());
if (aGrammarNSIndex != fGrammarNameSpaceIndex) {
if ( !switchGrammar(aGrammarNSIndex) ) {
break; //exit the loop in this case
}
}
}
}
}
if (elementIndex == -1) {
switchGrammar(fGrammarNameSpaceIndex);
}
}
//if still can't resolve it, try TOP_LEVEL_SCOPE AGAIN
/****/
if ( element.uri == StringPool.EMPTY_STRING && elementIndex == -1
&& fNamespacesScope != null
&& fNamespacesScope.getNamespaceForPrefix(StringPool.EMPTY_STRING) != StringPool.EMPTY_STRING ) {
elementIndex = fGrammar.getElementDeclIndex(element.localpart, TOP_LEVEL_SCOPE);
// REVISIT:
// this is a hack to handle the situation where namespace prefix "" is bound to nothing, and there
// is a "noNamespaceSchemaLocation" specified, and element
element.uri = StringPool.EMPTY_STRING;
}
/****/
/****/
if (elementIndex == -1) {
if (laxThisOne) {
fNeedValidationOff = true;
} else
if (DEBUG_SCHEMA_VALIDATION)
System.out.println("!!! can not find elementDecl in the grammar, " +
" the element localpart: " + element.localpart +
"["+fStringPool.toString(element.localpart) +"]" +
" the element uri: " + element.uri +
"["+fStringPool.toString(element.uri) +"]" +
" and the current enclosing scope: " + fCurrentScope );
}
/****/
}
if (DEBUG_SCHEMA_VALIDATION) {
fGrammar.getElementDecl(elementIndex, fTempElementDecl);
System.out.println("elementIndex: " + elementIndex+" \n and itsName : '"
+ fStringPool.toString(fTempElementDecl.name.localpart)
+"' \n its ContentType:" + fTempElementDecl.type
+"\n its ContentSpecIndex : " + fTempElementDecl.contentSpecIndex +"\n"+
" and the current enclosing scope: " + fCurrentScope);
}
}
contentSpecType = getContentSpecType(elementIndex);
if (fGrammarIsSchemaGrammar && elementIndex != -1) {
// handle "xsi:type" right here
if (fXsiTypeAttValue > -1) {
String xsiType = fStringPool.toString(fXsiTypeAttValue);
int colonP = xsiType.indexOf(":");
String prefix = "";
String localpart = xsiType;
if (colonP > -1) {
prefix = xsiType.substring(0,colonP);
localpart = xsiType.substring(colonP+1);
}
String uri = "";
int uriIndex = StringPool.EMPTY_STRING;
if (fNamespacesScope != null) {
uriIndex = fNamespacesScope.getNamespaceForPrefix(fStringPool.addSymbol(prefix));
if (uriIndex > 0) {
uri = fStringPool.toString(uriIndex);
if (uriIndex != fGrammarNameSpaceIndex) {
fGrammarNameSpaceIndex = fCurrentSchemaURI = uriIndex;
boolean success = switchGrammar(fCurrentSchemaURI);
if (!success && !fNeedValidationOff) {
reportRecoverableXMLError(XMLMessages.MSG_GENERIC_SCHEMA_ERROR,
XMLMessages.SCHEMA_GENERIC_ERROR,
"Grammar with uri 3: "
+ fStringPool.toString(fCurrentSchemaURI)
+ " , can not be found");
}
}
}
}
Hashtable complexRegistry = ((SchemaGrammar)fGrammar).getComplexTypeRegistry();
DatatypeValidatorFactoryImpl dataTypeReg = ((SchemaGrammar)fGrammar).getDatatypeRegistry();
if (complexRegistry==null || dataTypeReg == null) {
reportRecoverableXMLError(XMLMessages.MSG_GENERIC_SCHEMA_ERROR,
XMLMessages.SCHEMA_GENERIC_ERROR,
fErrorReporter.getLocator().getSystemId()
+" line"+fErrorReporter.getLocator().getLineNumber()
+", canot resolve xsi:type = " + xsiType+" ---2");
} else {
TraverseSchema.ComplexTypeInfo typeInfo =
(TraverseSchema.ComplexTypeInfo) complexRegistry.get(uri+","+localpart);
if (typeInfo==null) {
if (uri.length() == 0 || uri.equals(SchemaSymbols.URI_SCHEMAFORSCHEMA) ) {
fXsiTypeValidator = dataTypeReg.getDatatypeValidator(localpart);
} else
fXsiTypeValidator = dataTypeReg.getDatatypeValidator(uri+","+localpart);
if ( fXsiTypeValidator == null )
reportRecoverableXMLError(XMLMessages.MSG_GENERIC_SCHEMA_ERROR,
XMLMessages.SCHEMA_GENERIC_ERROR,
"unresolved type : "+uri+","+localpart
+" found in xsi:type handling");
else {
// make sure the new type is related to the
// type of the expected element
XMLElementDecl tempElementDecl = new XMLElementDecl();
fGrammar.getElementDecl(elementIndex, tempElementDecl);
DatatypeValidator ancestorValidator = tempElementDecl.datatypeValidator;
DatatypeValidator tempVal = fXsiTypeValidator;
for(; tempVal != null; tempVal = tempVal.getBaseValidator())
// WARNING!!! Comparison by reference.
if(tempVal == ancestorValidator) break;
if(tempVal == null) {
// now if ancestorValidator is a union, then we must
// look through its members to see whether we derive from any of them.
if(ancestorValidator instanceof UnionDatatypeValidator) {
// fXsiTypeValidator must derive from one of its members...
Vector subUnionMemberDV = ((UnionDatatypeValidator)ancestorValidator).getBaseValidators();
int subUnionSize = subUnionMemberDV.size();
boolean found = false;
for (int i=0; i<subUnionSize && !found; i++) {
DatatypeValidator dTempSub = (DatatypeValidator)subUnionMemberDV.elementAt(i);
DatatypeValidator dTemp = fXsiTypeValidator;
for(; dTemp != null; dTemp = dTemp.getBaseValidator()) {
// WARNING!!! This uses comparison by reference andTemp is thus inherently suspect!
if(dTempSub == dTemp) {
found = true;
break;
}
}
}
if(!found) {
reportRecoverableXMLError(XMLMessages.MSG_GENERIC_SCHEMA_ERROR,
XMLMessages.SCHEMA_GENERIC_ERROR,
"Type : "+uri+","+localpart
+" does not derive from the type of element " + fStringPool.toString(tempElementDecl.name.localpart));
}
} else {
reportRecoverableXMLError(XMLMessages.MSG_GENERIC_SCHEMA_ERROR,
XMLMessages.SCHEMA_GENERIC_ERROR,
"Type : "+uri+","+localpart
+" does not derive from the type of element " + fStringPool.toString(tempElementDecl.name.localpart));
}
} else { // check if element has block set
if((((SchemaGrammar)fGrammar).getElementDeclBlockSet(elementIndex) & SchemaSymbols.RESTRICTION) != 0) {
reportRecoverableXMLError(XMLMessages.MSG_GENERIC_SCHEMA_ERROR,
XMLMessages.SCHEMA_GENERIC_ERROR,
"Element " + fStringPool.toString(tempElementDecl.name.localpart)
+ "does not permit substitution by a type such as "+uri+","+localpart);
}
}
}
} else {
//
// The type must not be abstract
//
if (typeInfo.isAbstractType()) {
reportRecoverableXMLError(XMLMessages.MSG_GENERIC_SCHEMA_ERROR,
XMLMessages.SCHEMA_GENERIC_ERROR,
"Abstract type " + xsiType + " should not be used in xsi:type");
}
// now we look at whether there is a type
// relation and whether the type (and element) allow themselves to be substituted for.
TraverseSchema.ComplexTypeInfo tempType = typeInfo;
TraverseSchema.ComplexTypeInfo destType = ((SchemaGrammar)fGrammar).getElementComplexTypeInfo(elementIndex);
for(; tempType != null; tempType = tempType.baseComplexTypeInfo) {
if(tempType.typeName.equals(destType.typeName))
break;
}
if(tempType == null) {
reportRecoverableXMLError(XMLMessages.MSG_GENERIC_SCHEMA_ERROR,
XMLMessages.SCHEMA_GENERIC_ERROR,
"Type : "+uri+","+localpart
+" does not derive from the type " + destType.typeName);
} else { // now check whether the element or typeInfo's baseType blocks us.
int derivationMethod = typeInfo.derivedBy;
if((((SchemaGrammar)fGrammar).getElementDeclBlockSet(elementIndex) & derivationMethod) != 0) {
XMLElementDecl tempElementDecl = new XMLElementDecl();
fGrammar.getElementDecl(elementIndex, tempElementDecl);
reportRecoverableXMLError(XMLMessages.MSG_GENERIC_SCHEMA_ERROR,
XMLMessages.SCHEMA_GENERIC_ERROR,
"Element " + fStringPool.toString(tempElementDecl.name.localpart) +
" does not permit xsi:type substitution in the manner required by type "+uri+","+localpart);
} else if ((typeInfo.baseComplexTypeInfo.blockSet & derivationMethod) != 0) {
reportRecoverableXMLError(XMLMessages.MSG_GENERIC_SCHEMA_ERROR,
XMLMessages.SCHEMA_GENERIC_ERROR,
"Type " + typeInfo.baseComplexTypeInfo.typeName + " does not permit other types, such as "
+uri+","+localpart + " to be substituted for itself using xsi:type");
}
}
elementIndex = typeInfo.templateElementIndex;
}
}
fXsiTypeAttValue = -1;
}
else {
//
// xsi:type was not specified...
// If the corresponding type is abstract, detect an error
//
TraverseSchema.ComplexTypeInfo typeInfo =
((SchemaGrammar) fGrammar).getElementComplexTypeInfo(elementIndex);
if (typeInfo != null &&
typeInfo.isAbstractType()) {
reportRecoverableXMLError(XMLMessages.MSG_GENERIC_SCHEMA_ERROR,
XMLMessages.SCHEMA_GENERIC_ERROR,
"Element " + fStringPool.toString(element.rawname) + " is declared with a type that is abstract. Use xsi:type to specify a non-abstract type");
}
}
//
// Check whether this element is abstract. If so, an error
//
int miscFlags = ((SchemaGrammar) fGrammar).getElementDeclMiscFlags(elementIndex);
if ((miscFlags & SchemaSymbols.ABSTRACT) != 0) {
reportRecoverableXMLError(XMLMessages.MSG_GENERIC_SCHEMA_ERROR,
XMLMessages.SCHEMA_GENERIC_ERROR,
"A member of abstract element " + fStringPool.toString(element.rawname) + "'s substitution group must be specified");
}
if (fNil && (miscFlags & SchemaSymbols.NILLABLE) == 0 ) {
fNil = false;
reportRecoverableXMLError(XMLMessages.MSG_GENERIC_SCHEMA_ERROR,
XMLMessages.SCHEMA_GENERIC_ERROR,
"xsi:nil must not be specified for the element "+ fStringPool.toString(element.rawname)+
" with {nillable} equals 'false'");
}
//Change the current scope to be the one defined by this element.
fCurrentScope = ((SchemaGrammar) fGrammar).getElementDefinedScope(elementIndex);
// here need to check if we need to switch Grammar by asking SchemaGrammar whether
// this element actually is of a type in another Schema.
String anotherSchemaURI = ((SchemaGrammar)fGrammar).getElementFromAnotherSchemaURI(elementIndex);
if (anotherSchemaURI != null) {
//before switch Grammar, set the elementIndex to be the template elementIndex of its type
// but if the content type is empty, we don't bother switching the grammar.
if (contentSpecType != -1
&& contentSpecType != XMLElementDecl.TYPE_EMPTY ) {
TraverseSchema.ComplexTypeInfo typeInfo = ((SchemaGrammar) fGrammar).getElementComplexTypeInfo(elementIndex);
if (typeInfo != null) {
elementIndex = typeInfo.templateElementIndex;
}
}
// now switch the grammar
fGrammarNameSpaceIndex = fCurrentSchemaURI = fStringPool.addSymbol(anotherSchemaURI);
boolean success = switchGrammar(fCurrentSchemaURI);
if (!success && !fNeedValidationOff) {
reportRecoverableXMLError(XMLMessages.MSG_GENERIC_SCHEMA_ERROR,
XMLMessages.SCHEMA_GENERIC_ERROR,
"Grammar with uri 4: "
+ fStringPool.toString(fCurrentSchemaURI)
+ " , can not be found");
}
}
}
// since the elementIndex could change since last time we query the content type, so do it again.
contentSpecType = getContentSpecType(elementIndex);
if (contentSpecType == -1 && fValidating && !fNeedValidationOff ) {
reportRecoverableXMLError(XMLMessages.MSG_ELEMENT_NOT_DECLARED,
XMLMessages.VC_ELEMENT_VALID,
element.rawname);
}
if (fGrammar != null && fGrammarIsSchemaGrammar && elementIndex != -1) {
fAttrListHandle = addDefaultAttributes(elementIndex, attrList, fAttrListHandle, fValidating, fStandaloneReader != -1);
}
if (fAttrListHandle != -1) {
fAttrList.endAttrList();
}
if (DEBUG_PRINT_ATTRIBUTES) {
String elementStr = fStringPool.toString(element.rawname);
System.out.print("startElement: <" + elementStr);
if (fAttrListHandle != -1) {
int index = attrList.getFirstAttr(fAttrListHandle);
while (index != -1) {
System.out.print(" " + fStringPool.toString(attrList.getAttrName(index)) + "=\"" +
fStringPool.toString(attrList.getAttValue(index)) + "\"");
index = attrList.getNextAttr(index);
}
}
System.out.println(">");
}
// REVISIT: Validation. Do we need to recheck for the xml:lang
// attribute? It was already checked above -- perhaps
// this is to check values that are defaulted in? If
// so, this check could move to the attribute decl
// callback so we can check the default value before
// it is used.
if (fAttrListHandle != -1 && !fNeedValidationOff ) {
int index = fAttrList.getFirstAttr(fAttrListHandle);
while (index != -1) {
int attrNameIndex = attrList.getAttrName(index);
if (fStringPool.equalNames(attrNameIndex, fXMLLang)) {
fDocumentScanner.checkXMLLangAttributeValue(attrList.getAttValue(index));
// break;
}
// here, we validate every "user-defined" attributes
int _xmlns = fStringPool.addSymbol("xmlns");
if (attrNameIndex != _xmlns && attrList.getAttrPrefix(index) != _xmlns)
if (fGrammar != null) {
fTempQName.setValues(attrList.getAttrPrefix(index),
attrList.getAttrLocalpart(index),
attrList.getAttrName(index),
attrList.getAttrURI(index) );
int attDefIndex = getAttDefByElementIndex(elementIndex, fTempQName);
if (fTempQName.uri != fXsiURI)
if (attDefIndex == -1 ) {
if (fValidating) {
// REVISIT - cache the elem/attr tuple so that we only give
// this error once for each unique occurrence
Object[] args = { fStringPool.toString(element.rawname),
fStringPool.toString(attrList.getAttrName(index))};
/*****/
fAttrNameLocator = getLocatorImpl(fAttrNameLocator);
fErrorReporter.reportError(fAttrNameLocator,
XMLMessages.XML_DOMAIN,
XMLMessages.MSG_ATTRIBUTE_NOT_DECLARED,
XMLMessages.VC_ATTRIBUTE_VALUE_TYPE,
args,
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
/******/
}
} else {
fGrammar.getAttributeDecl(attDefIndex, fTempAttDecl);
int attributeType = attributeTypeName(fTempAttDecl);
attrList.setAttType(index, attributeType);
if (fValidating) {
if (fGrammarIsDTDGrammar) {
int normalizedValue = validateDTDattribute(element, attrList.getAttValue(index), fTempAttDecl);
attrList.setAttValue(index, normalizedValue);
}
// check to see if this attribute matched an attribute wildcard
else if ( fGrammarIsSchemaGrammar &&
(fTempAttDecl.type == XMLAttributeDecl.TYPE_ANY_ANY
||fTempAttDecl.type == XMLAttributeDecl.TYPE_ANY_LIST
||fTempAttDecl.type == XMLAttributeDecl.TYPE_ANY_OTHER) ) {
if ((fTempAttDecl.defaultType & XMLAttributeDecl.PROCESSCONTENTS_SKIP) > 0) {
// attribute should just be bypassed,
} else if ( (fTempAttDecl.defaultType & XMLAttributeDecl.PROCESSCONTENTS_STRICT) > 0
|| (fTempAttDecl.defaultType & XMLAttributeDecl.PROCESSCONTENTS_LAX) > 0) {
boolean reportError = false;
boolean processContentStrict =
(fTempAttDecl.defaultType & XMLAttributeDecl.PROCESSCONTENTS_STRICT) > 0;
// ??? REVISIT: can't tell whether it's a local attribute
// or a global one with empty namespace
//if (fTempQName.uri == StringPool.EMPTY_STRING) {
// if (processContentStrict) {
// reportError = true;
// }
//} else {
{
Grammar aGrammar =
fGrammarResolver.getGrammar(fStringPool.toString(fTempQName.uri));
if (aGrammar == null || !(aGrammar instanceof SchemaGrammar) ) {
if (processContentStrict) {
reportError = true;
}
} else {
SchemaGrammar sGrammar = (SchemaGrammar) aGrammar;
Hashtable attRegistry = sGrammar.getAttributeDeclRegistry();
if (attRegistry == null) {
if (processContentStrict) {
reportError = true;
}
} else {
XMLAttributeDecl attDecl = (XMLAttributeDecl) attRegistry.get(fStringPool.toString(fTempQName.localpart));
if (attDecl == null) {
if (processContentStrict) {
reportError = true;
}
} else {
DatatypeValidator attDV = attDecl.datatypeValidator;
if (attDV == null) {
if (processContentStrict) {
reportError = true;
}
} else {
try {
String unTrimValue = fStringPool.toString(attrList.getAttValue(index));
String value = unTrimValue.trim();
if (attDV instanceof IDDatatypeValidator) {
this.fStoreIDRef.setDatatypeObject( attDV.validate( value, null ) );
}
if (attDV instanceof IDREFDatatypeValidator) {
attDV.validate(value, null );
}
else {
fWhiteSpace = attDV.getWSFacet();
if (fWhiteSpace == DatatypeValidator.REPLACE) { //CDATA
attDV.validate(unTrimValue, null );
}
else { // normalize
int normalizedValue = fStringPool.addString(value);
attrList.setAttValue(index,normalizedValue );
if (attDV instanceof NOTATIONDatatypeValidator
&& value !=null) {
value=bindNotationURI(value);
}
attDV.validate(value, null );
}
}
} catch (InvalidDatatypeValueException idve) {
fErrorReporter.reportError(fErrorReporter.getLocator(),
SchemaMessageProvider.SCHEMA_DOMAIN,
SchemaMessageProvider.DatatypeError,
SchemaMessageProvider.MSG_NONE,
new Object [] { idve.getMessage()},
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
}
}
}
}
}
}
if (reportError) {
Object[] args = { fStringPool.toString(element.rawname),
"ANY---"+fStringPool.toString(attrList.getAttrName(index))};
fAttrNameLocator = getLocatorImpl(fAttrNameLocator);
fErrorReporter.reportError(fAttrNameLocator,
XMLMessages.XML_DOMAIN,
XMLMessages.MSG_ATTRIBUTE_NOT_DECLARED,
XMLMessages.VC_ATTRIBUTE_VALUE_TYPE,
args,
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
}
}
} else if (fTempAttDecl.datatypeValidator == null) {
Object[] args = { fStringPool.toString(element.rawname),
fStringPool.toString(attrList.getAttrName(index))};
System.out.println("[Error] Datatypevalidator for attribute " + fStringPool.toString(attrList.getAttrName(index))
+ " not found in element type " + fStringPool.toString(element.rawname));
/****/
fAttrNameLocator = getLocatorImpl(fAttrNameLocator);
fErrorReporter.reportError(fAttrNameLocator,
XMLMessages.XML_DOMAIN,
XMLMessages.MSG_ATTRIBUTE_NOT_DECLARED,
XMLMessages.VC_ATTRIBUTE_VALUE_TYPE,
args,
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
/****/
} else {
try {
String unTrimValue = fStringPool.toString(attrList.getAttValue(index));
String value = unTrimValue.trim();
DatatypeValidator tempDV = fTempAttDecl.datatypeValidator;
if (tempDV instanceof IDDatatypeValidator) {
this.fStoreIDRef.setDatatypeObject( tempDV.validate( value, null ) );
} else if (tempDV instanceof IDREFDatatypeValidator) {
tempDV.validate(value, null );
}
else {
fWhiteSpace = tempDV.getWSFacet();
if (fWhiteSpace == DatatypeValidator.REPLACE) { //CDATA
tempDV.validate(unTrimValue, null );
}
else { // normalize
int normalizedValue = fStringPool.addString(value);
attrList.setAttValue(index,normalizedValue );
if (tempDV instanceof NOTATIONDatatypeValidator
&& value !=null) {
value=bindNotationURI(value);
}
tempDV.validate(value, null );
}
}
} catch (InvalidDatatypeValueException idve) {
fErrorReporter.reportError(fErrorReporter.getLocator(),
SchemaMessageProvider.SCHEMA_DOMAIN,
SchemaMessageProvider.DatatypeError,
SchemaMessageProvider.MSG_NONE,
new Object [] { idve.getMessage()},
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
}
}
} // end of if (fValidating)
} // end of if (attDefIndex == -1) else
}// end of if (fGrammar != null)
index = fAttrList.getNextAttr(index);
}
}
}
if (fAttrListHandle != -1) {
int index = attrList.getFirstAttr(fAttrListHandle);
while (index != -1) {
int attName = attrList.getAttrName(index);
if (!fStringPool.equalNames(attName, fNamespacesPrefix)) {
int attPrefix = attrList.getAttrPrefix(index);
if (attPrefix != fNamespacesPrefix) {
if (attPrefix != -1) {
int uri = fNamespacesScope.getNamespaceForPrefix(attPrefix);
if (uri == StringPool.EMPTY_STRING) {
Object[] args = { fStringPool.toString(attPrefix)};
fErrorReporter.reportError(fErrorReporter.getLocator(),
XMLMessages.XMLNS_DOMAIN,
XMLMessages.MSG_PREFIX_DECLARED,
XMLMessages.NC_PREFIX_DECLARED,
args,
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
}
attrList.setAttrURI(index, uri);
}
}
}
index = attrList.getNextAttr(index);
}
}
fCurrentElementIndex = elementIndex;
fCurrentContentSpecType = contentSpecType;
if (fValidating && contentSpecType == XMLElementDecl.TYPE_SIMPLE) {
fBufferDatatype = true;
fDatatypeBuffer.setLength(0);
}
fInElementContent = (contentSpecType == XMLElementDecl.TYPE_CHILDREN);
} // validateElementAndAttributes(QName,XMLAttrList)
/**
* Validate attributes in DTD fashion.
* @return normalized attribute value
*/
private int validateDTDattribute(QName element, int attValue,
XMLAttributeDecl attributeDecl) throws Exception{
AttributeValidator av = null;
switch (attributeDecl.type) {
case XMLAttributeDecl.TYPE_ENTITY:
{
boolean isAlistAttribute = attributeDecl.list;//Caveat - Save this information because invalidStandaloneAttDef
String unTrimValue = fStringPool.toString(attValue);
String value = unTrimValue.trim();
//System.out.println("value = " + value );
//changes fTempAttDef
if (fValidationEnabled) {
if (value != unTrimValue) {
if (invalidStandaloneAttDef(element, attributeDecl.name)) {
reportRecoverableXMLError(XMLMessages.MSG_ATTVALUE_CHANGED_DURING_NORMALIZATION_WHEN_STANDALONE,
XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION,
fStringPool.toString(attributeDecl.name.rawname), unTrimValue, value);
}
}
}
try {
if ( isAlistAttribute ) {
fValENTITIES.validate( value, null );
} else {
fValENTITY.validate( value, null );
}
} catch ( InvalidDatatypeValueException ex ) {
if ( ex.getMajorCode() != 1 && ex.getMinorCode() != -1 ) {
reportRecoverableXMLError(ex.getMajorCode(),
ex.getMinorCode(),
fStringPool.toString( attributeDecl.name.rawname), value );
} else {
System.err.println("Error: " + ex.getLocalizedMessage() );//Should not happen
}
}
/*if (attributeDecl.list) {
av = fAttValidatorENTITIES;
}
else {
av = fAttValidatorENTITY;
}*/
if (fNormalizeAttributeValues) {
if (attributeDecl.list) {
attValue = normalizeListAttribute(value, attValue, unTrimValue);
} else {
if (value != unTrimValue) {
attValue = fStringPool.addSymbol(value);
}
}
}
}
break;
case XMLAttributeDecl.TYPE_ENUMERATION:
av = fAttValidatorENUMERATION;
break;
case XMLAttributeDecl.TYPE_ID:
{
String unTrimValue = fStringPool.toString(attValue);
String value = unTrimValue.trim();
if (fValidationEnabled) {
if (value != unTrimValue) {
if (invalidStandaloneAttDef(element, attributeDecl.name)) {
reportRecoverableXMLError(XMLMessages.MSG_ATTVALUE_CHANGED_DURING_NORMALIZATION_WHEN_STANDALONE,
XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION,
fStringPool.toString(attributeDecl.name.rawname), unTrimValue, value);
}
}
}
try {
this.fStoreIDRef.setDatatypeObject( fValID.validate( value, null ) );
fValIDRef.validate( value, null ); //just in case we called id after IDREF
} catch ( InvalidDatatypeValueException ex ) {
reportRecoverableXMLError(ex.getMajorCode(),
ex.getMinorCode(),
fStringPool.toString( attributeDecl.name.rawname), value );
}
if (fNormalizeAttributeValues && value != unTrimValue) {
attValue = fStringPool.addSymbol(value);
}
}
break;
case XMLAttributeDecl.TYPE_IDREF:
{
String unTrimValue = fStringPool.toString(attValue);
String value = unTrimValue.trim();
boolean isAlistAttribute = attributeDecl.list;//Caveat - Save this information because invalidStandaloneAttDef
//changes fTempAttDef
if (fValidationEnabled) {
if (value != unTrimValue) {
if (invalidStandaloneAttDef(element, attributeDecl.name)) {
reportRecoverableXMLError(XMLMessages.MSG_ATTVALUE_CHANGED_DURING_NORMALIZATION_WHEN_STANDALONE,
XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION,
fStringPool.toString(attributeDecl.name.rawname), unTrimValue, value);
}
}
if (attributeDecl.list && value.length() == 0 ) {
reportRecoverableXMLError(XMLMessages.MSG_IDREFS_INVALID, XMLMessages.VC_IDREF,
fStringPool.toString(attributeDecl.name.rawname) ) ;
}
}
try {
if ( isAlistAttribute ) {
fValIDRefs.validate( value, null );
} else {
fValIDRef.validate( value, null );
}
} catch ( InvalidDatatypeValueException ex ) {
if ( ex.getMajorCode() != 1 && ex.getMinorCode() != -1 ) {
reportRecoverableXMLError(ex.getMajorCode(),
ex.getMinorCode(),
fStringPool.toString( attributeDecl.name.rawname), value );
} else {
System.err.println("Error: " + ex.getLocalizedMessage() );//Should not happen
}
}
if (fNormalizeAttributeValues) {
if (attributeDecl.list) {
attValue = normalizeListAttribute(value, attValue, unTrimValue);
} else {
if (value != unTrimValue) {
attValue = fStringPool.addSymbol(value);
}
}
}
}
break;
case XMLAttributeDecl.TYPE_NOTATION:
{
/* WIP
String unTrimValue = fStringPool.toString(attValue);
String value = unTrimValue.trim();
if (fValidationEnabled) {
if (value != unTrimValue) {
if (invalidStandaloneAttDef(element, attributeDecl.name)) {
reportRecoverableXMLError(XMLMessages.MSG_ATTVALUE_CHANGED_DURING_NORMALIZATION_WHEN_STANDALONE,
XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION,
fStringPool.toString(attributeDecl.name.rawname), unTrimValue, value);
}
}
}
try {
//this.fIdDefs = (Hashtable) fValID.validate( value, null );
//System.out.println("this.fIdDefs = " + this.fIdDefs );
this.fStoreIDRef.setDatatypeObject( fValID.validate( value, null ) );
} catch ( InvalidDatatypeValueException ex ) {
reportRecoverableXMLError(ex.getMajorCode(),
ex.getMinorCode(),
fStringPool.toString( attributeDecl.name.rawname), value );
}
}
*/
av = fAttValidatorNOTATION;
}
break;
case XMLAttributeDecl.TYPE_NMTOKEN:
{
String unTrimValue = fStringPool.toString(attValue);
String value = unTrimValue.trim();
boolean isAlistAttribute = attributeDecl.list;//Caveat - Save this information because invalidStandaloneAttDef
//changes fTempAttDef
if (fValidationEnabled) {
if (value != unTrimValue) {
if (invalidStandaloneAttDef(element, attributeDecl.name)) {
reportRecoverableXMLError(XMLMessages.MSG_ATTVALUE_CHANGED_DURING_NORMALIZATION_WHEN_STANDALONE,
XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION,
fStringPool.toString(attributeDecl.name.rawname), unTrimValue, value);
}
}
if (attributeDecl.list && value.length() == 0 ) {
reportRecoverableXMLError(XMLMessages.MSG_NMTOKENS_INVALID, XMLMessages.VC_NAME_TOKEN,
fStringPool.toString(attributeDecl.name.rawname) ) ;
}
}
try {
if ( isAlistAttribute ) {
fValNMTOKENS.validate( value, null );
} else {
fValNMTOKEN.validate( value, null );
}
} catch ( InvalidDatatypeValueException ex ) {
reportRecoverableXMLError(XMLMessages.MSG_NMTOKEN_INVALID,
XMLMessages.VC_NAME_TOKEN,
fStringPool.toString(attributeDecl.name.rawname), value);//TODO NMTOKENS messge
}
if (fNormalizeAttributeValues) {
if (attributeDecl.list) {
attValue = normalizeListAttribute(value, attValue, unTrimValue);
} else {
if (value != unTrimValue) {
attValue = fStringPool.addSymbol(value);
}
}
}
}
break;
}
if ( av != null ) {
int newValue = av.normalize(element, attributeDecl.name, attValue,
attributeDecl.type, attributeDecl.enumeration);
if (fNormalizeAttributeValues)
attValue = newValue;
}
return attValue;
}
/**
* @param value This is already trimmed.
*/
private int normalizeListAttribute(String value, int origIndex, String origValue) {
int length = value.length();
StringBuffer buffer = null;
int state = 0; // 0:non-S, 1: 1st S, 2: non-1st S
int copyStart = 0;
for (int i = 0; i < length; i++) {
int ch = value.charAt(i);
if (ch == ' ') {
if (state == 0) {
state = 1;
} else if (state == 1) {
state = 2;
if (buffer == null)
buffer = new StringBuffer(length);
buffer.append(value.substring(copyStart, i));
}
} else {
if (state == 2)
copyStart = i;
state = 0;
}
}
if (buffer == null)
return value == origValue ? origIndex : fStringPool.addSymbol(value);
buffer.append(value.substring(copyStart));
return fStringPool.addSymbol(new String(buffer));
}
/** Character data in content. */
private void charDataInContent() {
if (DEBUG_ELEMENT_CHILDREN) {
System.out.println("charDataInContent()");
}
if (fElementChildren.length <= fElementChildrenLength) {
QName[] newarray = new QName[fElementChildren.length * 2];
System.arraycopy(fElementChildren, 0, newarray, 0, fElementChildren.length);
fElementChildren = newarray;
}
QName qname = fElementChildren[fElementChildrenLength];
if (qname == null) {
for (int i = fElementChildrenLength; i < fElementChildren.length; i++) {
fElementChildren[i] = new QName();
}
qname = fElementChildren[fElementChildrenLength];
}
qname.clear();
fElementChildrenLength++;
} // charDataInCount()
/**
* Check that the content of an element is valid.
* <p>
* This is the method of primary concern to the validator. This method is called
* upon the scanner reaching the end tag of an element. At that time, the
* element's children must be structurally validated, so it calls this method.
* The index of the element being checked (in the decl pool), is provided as
* well as an array of element name indexes of the children. The validator must
* confirm that this element can have these children in this order.
* <p>
* This can also be called to do 'what if' testing of content models just to see
* if they would be valid.
* <p>
* Note that the element index is an index into the element decl pool, whereas
* the children indexes are name indexes, i.e. into the string pool.
* <p>
* A value of -1 in the children array indicates a PCDATA node. All other
* indexes will be positive and represent child elements. The count can be
* zero, since some elements have the EMPTY content model and that must be
* confirmed.
*
* @param elementIndex The index within the <code>ElementDeclPool</code> of this
* element.
* @param childCount The number of entries in the <code>children</code> array.
* @param children The children of this element. Each integer is an index within
* the <code>StringPool</code> of the child element name. An index
* of -1 is used to indicate an occurrence of non-whitespace character
* data.
*
* @return The value -1 if fully valid, else the 0 based index of the child
* that first failed. If the value returned is equal to the number
* of children, then additional content is required to reach a valid
* ending state.
*
* @exception Exception Thrown on error.
*/
private int checkContent(int elementIndex,
QName[] children,
int childOffset,
int childCount) throws Exception {
// Get the element name index from the element
// REVISIT: Validation
final int elementType = fCurrentElement.rawname;
if (DEBUG_PRINT_CONTENT) {
String strTmp = fStringPool.toString(elementType);
System.out.println("Name: "+strTmp+", "+
"Count: "+childCount+", "+
"ContentSpecType: " +fCurrentContentSpecType); //+getContentSpecAsString(elementIndex));
for (int index = childOffset; index < (childOffset+childCount) && index < 10; index++) {
if (index == 0) {
System.out.print(" (");
}
String childName = (children[index].localpart == -1) ? "#PCDATA" : fStringPool.toString(children[index].localpart);
if (index + 1 == childCount) {
System.out.println(childName + ")");
} else if (index + 1 == 10) {
System.out.println(childName + ",...)");
} else {
System.out.print(childName + ",");
}
}
}
// Get out the content spec for this element
final int contentType = fCurrentContentSpecType;
//
// Deal with the possible types of content. We try to optimized here
// by dealing specially with content models that don't require the
// full DFA treatment.
//
if (contentType == XMLElementDecl.TYPE_EMPTY) {
//
// If the child count is greater than zero, then this is
// an error right off the bat at index 0.
//
if (childCount != 0) {
return 0;
}
} else if (contentType == XMLElementDecl.TYPE_ANY) {
//
// This one is open game so we don't pass any judgement on it
// at all. Its assumed to fine since it can hold anything.
//
} else if (contentType == XMLElementDecl.TYPE_MIXED_SIMPLE ||
contentType == XMLElementDecl.TYPE_MIXED_COMPLEX ||
contentType == XMLElementDecl.TYPE_CHILDREN) {
// XML Schema REC: Validation Rule: Element Locally Valid (Element)
// 3.2.1 The element information item must have no
// character or element information item [children].
//
if (childCount == 0 && fNil) {
fNil = false;
//return success
return -1;
}
// Get the content model for this element, faulting it in if needed
XMLContentModel cmElem = null;
try {
cmElem = getElementContentModel(elementIndex);
int result = cmElem.validateContent(children, childOffset, childCount);
if (result != -1 && fGrammarIsSchemaGrammar) {
result = cmElem.validateContentSpecial(children, childOffset, childCount);
}
return result;
} catch (CMException excToCatch) {
// REVISIT - Translate the caught exception to the protected error API
int majorCode = excToCatch.getErrorCode();
fErrorReporter.reportError(fErrorReporter.getLocator(),
ImplementationMessages.XERCES_IMPLEMENTATION_DOMAIN,
majorCode,
0,
null,
XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
}
} else if (contentType == -1) {
reportRecoverableXMLError(XMLMessages.MSG_ELEMENT_NOT_DECLARED,
XMLMessages.VC_ELEMENT_VALID,
elementType);
} else if (contentType == XMLElementDecl.TYPE_SIMPLE ) {
XMLContentModel cmElem = null;
if (childCount > 0) {
fErrorReporter.reportError(fErrorReporter.getLocator(),
SchemaMessageProvider.SCHEMA_DOMAIN,
SchemaMessageProvider.DatatypeError,
SchemaMessageProvider.MSG_NONE,
new Object [] { "In element '"+fStringPool.toString(elementType)+"' : "+
"Can not have element children within a simple type content"},
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
} else {
try {
if (fCurrentDV == null) { //no character data
fGrammar.getElementDecl(elementIndex, fTempElementDecl);
fCurrentDV = fTempElementDecl.datatypeValidator;
}
// If there is xsi:type validator, substitute it.
if ( fXsiTypeValidator != null ) {
fCurrentDV = fXsiTypeValidator;
fXsiTypeValidator = null;
}
if (fCurrentDV == null) {
System.out.println("Internal Error: this element have a simpletype "+
"but no datatypevalidator was found, element "+fTempElementDecl.name
+",locapart: "+fStringPool.toString(fTempElementDecl.name.localpart));
} else {
String value =fDatatypeBuffer.toString();
// check for fixed/default values of elements here.
if( ((SchemaGrammar)fGrammar).getElementDefaultValue(fCurrentElementIndex) != null &&
((SchemaGrammar)fGrammar).getElementDefaultValue(fCurrentElementIndex).equals("")) {
if (!fNil) {
fCurrentDV.validate(value, null);
}
else if ( fNil && value.length() != 0 ) {
reportRecoverableXMLError(XMLMessages.MSG_GENERIC_SCHEMA_ERROR,
XMLMessages.SCHEMA_GENERIC_ERROR,
"An element <" +fStringPool.toString(elementType)+"> with attribute xsi:nil=\"true\" must be empty");
}
}
else {
String currentElementDefault = ((SchemaGrammar)fGrammar).getElementDefaultValue(fCurrentElementIndex);
if ( (((SchemaGrammar)fGrammar).getElementDeclMiscFlags(fCurrentElementIndex) & SchemaSymbols.FIXED) != 0 ) {
if ( value.length() == 0 ) { // use fixed as default value
// Note: this is inefficient where the DOMParser
// is concerned. However, if we used the characters(int)
// callback instead, this would be just as inefficient for SAX.
fDocumentHandler.characters(currentElementDefault.toCharArray(), 0, currentElementDefault.length());
}
else { // must check in valuespace!
if ( fCurrentDV.compare(value, currentElementDefault) != 0 ) {
fErrorReporter.reportError(fErrorReporter.getLocator(),
SchemaMessageProvider.SCHEMA_DOMAIN,
SchemaMessageProvider.FixedDiffersFromActual,
0, null,
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
}
}
}
else {
if ( value.length() == 0 ) { // use default value
fDocumentHandler.characters(currentElementDefault.toCharArray(), 0, currentElementDefault.length());
}
else if (!fNil){ // must validate value
fCurrentDV.validate(value, null);
}
}
}
}
} catch (InvalidDatatypeValueException idve) {
fErrorReporter.reportError(fErrorReporter.getLocator(),
SchemaMessageProvider.SCHEMA_DOMAIN,
SchemaMessageProvider.DatatypeError,
SchemaMessageProvider.MSG_NONE,
new Object [] { "In element '"+fStringPool.toString(elementType)+"' : "+idve.getMessage()},
XMLErrorReporter.ERRORTYPE_RECOVERABLE_ERROR);
}
fNil = false;
fCurrentDV = null;
fFirstChunk= true;
fTrailing=false;
}
} else {
fErrorReporter.reportError(fErrorReporter.getLocator(),
ImplementationMessages.XERCES_IMPLEMENTATION_DOMAIN,
ImplementationMessages.VAL_CST,
0,
null,
XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
}
// We succeeded
return -1;
} // checkContent(int,int,int[]):int
/**
* Checks that all declared elements refer to declared elements
* in their content models. This method calls out to the error
* handler to indicate warnings.
*/
/*private void checkDeclaredElements() throws Exception {
//****DEBUG****
if (DEBUG) print("(???) XMLValidator.checkDeclaredElements\n");
//****DEBUG****
for (int i = 0; i < fElementCount; i++) {
int type = fGrammar.getContentSpecType(i);
if (type == XMLElementDecl.TYPE_MIXED_SIMPLE ||
type == XMLElementDecl.TYPE_MIXED_COMPLEX ||
type == XMLElementDecl.TYPE_CHILDREN) {
int chunk = i >> CHUNK_SHIFT;
int index = i & CHUNK_MASK;
int contentSpecIndex = fContentSpec[chunk][index];
checkDeclaredElements(i, contentSpecIndex);
}
}
}
*/
private void printChildren() {
if (DEBUG_ELEMENT_CHILDREN) {
System.out.print('[');
for (int i = 0; i < fElementChildrenLength; i++) {
System.out.print(' ');
QName qname = fElementChildren[i];
if (qname != null) {
System.out.print(fStringPool.toString(qname.rawname));
} else {
System.out.print("null");
}
if (i < fElementChildrenLength - 1) {
System.out.print(", ");
}
System.out.flush();
}
System.out.print(" ]");
System.out.println();
}
}
private void printStack() {
if (DEBUG_ELEMENT_CHILDREN) {
System.out.print('{');
for (int i = 0; i <= fElementDepth; i++) {
System.out.print(' ');
System.out.print(fElementChildrenOffsetStack[i]);
if (i < fElementDepth) {
System.out.print(", ");
}
System.out.flush();
}
System.out.print(" }");
System.out.println();
}
}
//
// Interfaces
//
/**
* AttributeValidator.
*/
public interface AttributeValidator {
//
// AttributeValidator methods
//
/** Normalize. */
public int normalize(QName element, QName attribute,
int attValue, int attType, int enumHandle)
throws Exception;
} // interface AttributeValidator
/** Returns true if invalid standalone attribute definition. */
boolean invalidStandaloneAttDef(QName element, QName attribute) {
if (fStandaloneReader == -1) {
return false;
}
// we are normalizing a default att value... this ok?
if (element.rawname == -1) {
return false;
}
return getAttDefIsExternal(element, attribute);
}
//
// Classes
//
/**
* AttValidatorNOTATION.
*/
final class AttValidatorNOTATION
implements AttributeValidator {
//
// AttributeValidator methods
//
/** Normalize. */
public int normalize(QName element, QName attribute,
int attValueHandle, int attType,
int enumHandle) throws Exception {
//
// Normalize attribute based upon attribute type...
//
String attValue = fStringPool.toString(attValueHandle);
String newAttValue = attValue.trim();
if (fValidating) {
// REVISIT - can we release the old string?
if (newAttValue != attValue) {
if (invalidStandaloneAttDef(element, attribute)) {
reportRecoverableXMLError(XMLMessages.MSG_ATTVALUE_CHANGED_DURING_NORMALIZATION_WHEN_STANDALONE,
XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION,
fStringPool.toString(attribute.rawname), attValue, newAttValue);
}
attValueHandle = fStringPool.addSymbol(newAttValue);
} else {
attValueHandle = fStringPool.addSymbol(attValueHandle);
}
//
// NOTATION - check that the value is in the AttDef enumeration (V_TAGo)
//
if (!fStringPool.stringInList(enumHandle, attValueHandle)) {
reportRecoverableXMLError(XMLMessages.MSG_ATTRIBUTE_VALUE_NOT_IN_LIST,
XMLMessages.VC_NOTATION_ATTRIBUTES,
fStringPool.toString(attribute.rawname),
newAttValue, fStringPool.stringListAsString(enumHandle));
}
} else if (newAttValue != attValue) {
// REVISIT - can we release the old string?
attValueHandle = fStringPool.addSymbol(newAttValue);
}
return attValueHandle;
} // normalize(QName,QName,int,int,int):int
//
// Package methods
//
/** Returns true if invalid standalone attribute definition. */
boolean invalidStandaloneAttDef(QName element, QName attribute) {
if (fStandaloneReader == -1) {
return false;
}
// we are normalizing a default att value... this ok?
if (element.rawname == -1) {
return false;
}
return getAttDefIsExternal(element, attribute);
}
} // class AttValidatorNOTATION
/**
* AttValidatorENUMERATION.
*/
final class AttValidatorENUMERATION
implements AttributeValidator {
//
// AttributeValidator methods
//
/** Normalize. */
public int normalize(QName element, QName attribute,
int attValueHandle, int attType,
int enumHandle) throws Exception {
//
// Normalize attribute based upon attribute type...
//
String attValue = fStringPool.toString(attValueHandle);
String newAttValue = attValue.trim();
if (fValidating) {
// REVISIT - can we release the old string?
if (newAttValue != attValue) {
if (invalidStandaloneAttDef(element, attribute)) {
reportRecoverableXMLError(XMLMessages.MSG_ATTVALUE_CHANGED_DURING_NORMALIZATION_WHEN_STANDALONE,
XMLMessages.VC_STANDALONE_DOCUMENT_DECLARATION,
fStringPool.toString(attribute.rawname), attValue, newAttValue);
}
attValueHandle = fStringPool.addSymbol(newAttValue);
} else {
attValueHandle = fStringPool.addSymbol(attValueHandle);
}
//
// ENUMERATION - check that value is in the AttDef enumeration (V_TAG9)
//
if (!fStringPool.stringInList(enumHandle, attValueHandle)) {
reportRecoverableXMLError(XMLMessages.MSG_ATTRIBUTE_VALUE_NOT_IN_LIST,
XMLMessages.VC_ENUMERATION,
fStringPool.toString(attribute.rawname),
newAttValue, fStringPool.stringListAsString(enumHandle));
}
} else if (newAttValue != attValue) {
// REVISIT - can we release the old string?
attValueHandle = fStringPool.addSymbol(newAttValue);
}
return attValueHandle;
} // normalize(QName,QName,int,int,int):int
//
// Package methods
//
/** Returns true if invalid standalone attribute definition. */
boolean invalidStandaloneAttDef(QName element, QName attribute) {
if (fStandaloneReader == -1) {
return false;
}
// we are normalizing a default att value... this ok?
if (element.rawname == -1) {
return false;
}
return getAttDefIsExternal(element, attribute);
}
} // class AttValidatorENUMERATION
// xpath matcher information
/**
* Stack of XPath matchers for identity constraints.
*
* @author Andy Clark, IBM
*/
protected static class XPathMatcherStack {
//
// Data
//
/** Active matchers. */
protected XPathMatcher[] fMatchers = new XPathMatcher[4];
/** Count of active matchers. */
protected int fMatchersCount;
/** Offset stack for contexts. */
protected IntStack fContextStack = new IntStack();
//
// Constructors
//
public XPathMatcherStack() {
} // <init>()
//
// Public methods
//
/** Resets the XPath matcher stack. */
public void clear() {
for (int i = 0; i < fMatchersCount; i++) {
fMatchers[i] = null;
}
fMatchersCount = 0;
fContextStack.clear();
} // clear()
/** Returns the size of the stack. */
public int size() {
return fContextStack.size();
} // size():int
/** Returns the count of XPath matchers. */
public int getMatcherCount() {
return fMatchersCount;
} // getMatcherCount():int
/** Adds a matcher. */
public void addMatcher(XPathMatcher matcher) {
ensureMatcherCapacity();
fMatchers[fMatchersCount++] = matcher;
} // addMatcher(XPathMatcher)
/** Returns the XPath matcher at the specified index. */
public XPathMatcher getMatcherAt(int index) {
return fMatchers[index];
} // getMatcherAt(index):XPathMatcher
/** Pushes a new context onto the stack. */
public void pushContext() {
fContextStack.push(fMatchersCount);
} // pushContext()
/** Pops a context off of the stack. */
public void popContext() {
fMatchersCount = fContextStack.pop();
} // popContext()
//
// Private methods
//
/** Ensures the size of the matchers array. */
private void ensureMatcherCapacity() {
if (fMatchersCount == fMatchers.length) {
XPathMatcher[] array = new XPathMatcher[fMatchers.length * 2];
System.arraycopy(fMatchers, 0, array, 0, fMatchers.length);
fMatchers = array;
}
} // ensureMatcherCapacity()
} // class XPathMatcherStack
// value store implementations
/**
* Value store implementation base class. There are specific subclasses
* for handling unique, key, and keyref.
*
* @author Andy Clark, IBM
*/
protected abstract class ValueStoreBase
implements ValueStore {
//
// Constants
//
/** Not a value (Unicode: #FFFF). */
protected IDValue NOT_AN_IDVALUE = new IDValue("\uFFFF", null);
//
// Data
//
/** Identity constraint. */
protected IdentityConstraint fIdentityConstraint;
/** Current data values. */
protected final OrderedHashtable fValues = new OrderedHashtable();
/** Current data value count. */
protected int fValuesCount;
/** Data value tuples. */
protected final Vector fValueTuples = new Vector();
//
// Constructors
//
/** Constructs a value store for the specified identity constraint. */
protected ValueStoreBase(IdentityConstraint identityConstraint) {
fIdentityConstraint = identityConstraint;
} // <init>(IdentityConstraint)
//
// Public methods
//
// destroys this ValueStore; useful when, for instanc,e a
// locally-scoped ID constraint is involved.
public void destroy() {
fValuesCount = 0;
fValues.clear();
fValueTuples.removeAllElements();
} // end destroy():void
// appends the contents of one ValueStore to those of us.
public void append(ValueStoreBase newVal) {
for (int i = 0; i < newVal.fValueTuples.size(); i++) {
OrderedHashtable o = (OrderedHashtable)newVal.fValueTuples.elementAt(i);
if (!contains(o))
fValueTuples.addElement(o);
}
} // append(ValueStoreBase)
/** Start scope for value store. */
public void startValueScope() throws Exception {
if (DEBUG_VALUE_STORES) {
System.out.println("<VS>: "+toString()+"#startValueScope()");
}
fValuesCount = 0;
int count = fIdentityConstraint.getFieldCount();
for (int i = 0; i < count; i++) {
fValues.put(fIdentityConstraint.getFieldAt(i), NOT_AN_IDVALUE);
}
} // startValueScope()
/** Ends scope for value store. */
public void endValueScope() throws Exception {
if (DEBUG_VALUE_STORES) {
System.out.println("<VS>: "+toString()+"#endValueScope()");
}
// is there anything to do?
// REVISIT: This check solves the problem with field matchers
// that get activated because they are at the same
// level as the declaring element (e.g. selector xpath
// is ".") but never match.
// However, this doesn't help us catch the problem
// when we expect a field value but never see it. A
// better solution has to be found. -Ac
// REVISIT: Is this a problem? -Ac
// Yes - NG
if (fValuesCount == 0) {
if(fIdentityConstraint.getType() == IdentityConstraint.KEY) {
int code = SchemaMessageProvider.AbsentKeyValue;
String eName = fIdentityConstraint.getElementName();
reportSchemaError(code, new Object[]{eName});
}
return;
}
// do we have enough values?
if (fValuesCount != fIdentityConstraint.getFieldCount()) {
switch (fIdentityConstraint.getType()) {
case IdentityConstraint.UNIQUE: {
int code = SchemaMessageProvider.UniqueNotEnoughValues;
String ename = fIdentityConstraint.getElementName();
reportSchemaError(code, new Object[]{ename});
break;
}
case IdentityConstraint.KEY: {
int code = SchemaMessageProvider.KeyNotEnoughValues;
Key key = (Key)fIdentityConstraint;
String ename = fIdentityConstraint.getElementName();
String kname = key.getIdentityConstraintName();
reportSchemaError(code, new Object[]{ename,kname});
break;
}
case IdentityConstraint.KEYREF: {
int code = SchemaMessageProvider.KeyRefNotEnoughValues;
KeyRef keyref = (KeyRef)fIdentityConstraint;
String ename = fIdentityConstraint.getElementName();
String kname = (keyref.getKey()).getIdentityConstraintName();
reportSchemaError(code, new Object[]{ename,kname});
break;
}
}
return;
}
} // endValueScope()
// This is needed to allow keyref's to look for matched keys
// in the correct scope. Unique and Key may also need to
// override this method for purposes of their own.
// This method is called whenever the DocumentFragment
// of an ID Constraint goes out of scope.
public void endDocumentFragment() throws Exception {
} // endDocumentFragment():void
/**
* Signals the end of the document. This is where the specific
* instances of value stores can verify the integrity of the
* identity constraints.
*/
public void endDocument() throws Exception {
if (DEBUG_VALUE_STORES) {
System.out.println("<VS>: "+toString()+"#endDocument()");
}
} // endDocument()
//
// ValueStore methods
//
/* reports an error if an element is matched
* has nillable true and is matched by a key.
*/
public void reportNilError(IdentityConstraint id) throws Exception {
if(id.getType() == IdentityConstraint.KEY) {
int code = SchemaMessageProvider.KeyMatchesNillable;
reportSchemaError(code, new Object[]{id.getElementName()});
}
} // reportNilError
/**
* Adds the specified value to the value store.
*
* @param value The value to add.
* @param field The field associated to the value. This reference
* is used to ensure that each field only adds a value
* once within a selection scope.
*/
public void addValue(Field field, IDValue value) throws Exception {
if(!field.mayMatch()) {
int code = SchemaMessageProvider.FieldMultipleMatch;
reportSchemaError(code, new Object[]{field.toString()});
}
if (DEBUG_VALUE_STORES) {
System.out.println("<VS>: "+toString()+"#addValue("+
"field="+field+','+
"value="+value+
")");
}
// do we even know this field?
int index = fValues.indexOf(field);
if (index == -1) {
int code = SchemaMessageProvider.UnknownField;
reportSchemaError(code, new Object[]{field.toString()});
return;
}
// store value
IDValue storedValue = fValues.valueAt(index);
if (storedValue.isDuplicateOf(NOT_AN_IDVALUE)) {
fValuesCount++;
}
fValues.put(field, value);
if (fValuesCount == fValues.size()) {
// is this value as a group duplicated?
if (contains(fValues)) {
duplicateValue(fValues);
}
// store values
OrderedHashtable values = (OrderedHashtable)fValues.clone();
fValueTuples.addElement(values);
}
} // addValue(String,Field)
/**
* Returns true if this value store contains the specified
* values tuple.
*/
public boolean contains(OrderedHashtable tuple) {
if (DEBUG_VALUE_STORES) {
System.out.println("<VS>: "+this.toString()+"#contains("+toString(tuple)+")");
}
// do sizes match?
int tcount = tuple.size();
// iterate over tuples to find it
int count = fValueTuples.size();
LOOP: for (int i = 0; i < count; i++) {
OrderedHashtable vtuple = (OrderedHashtable)fValueTuples.elementAt(i);
// compare values
for (int j = 0; j < tcount; j++) {
IDValue value1 = vtuple.valueAt(j);
IDValue value2 = tuple.valueAt(j);
if(!(value1.isDuplicateOf(value2))) {
continue LOOP;
}
}
// found it
return true;
}
// didn't find it
return false;
} // contains(Hashtable):boolean
//
// Protected methods
//
/**
* Called when a duplicate value is added. Subclasses should override
* this method to perform error checking.
*
* @param tuple The duplicate value tuple.
*/
protected void duplicateValue(OrderedHashtable tuple)
throws Exception {
// no-op
} // duplicateValue(Hashtable)
/** Returns a string of the specified values. */
protected String toString(OrderedHashtable tuple) {
// no values
int size = tuple.size();
if (size == 0) {
return "";
}
// construct value string
StringBuffer str = new StringBuffer();
for (int i = 0; i < size; i++) {
if (i > 0) {
str.append(',');
}
str.append(tuple.valueAt(i));
}
return str.toString();
} // toString(OrderedHashtable):String
//
// Object methods
//
/** Returns a string representation of this object. */
public String toString() {
String s = super.toString();
int index1 = s.lastIndexOf('$');
if (index1 != -1) {
s = s.substring(index1 + 1);
}
int index2 = s.lastIndexOf('.');
if (index2 != -1) {
s = s.substring(index2 + 1);
}
return s + '[' + fIdentityConstraint + ']';
} // toString():String
} // class ValueStoreBase
/**
* Unique value store.
*
* @author Andy Clark, IBM
*/
protected class UniqueValueStore
extends ValueStoreBase {
//
// Constructors
//
/** Constructs a unique value store. */
public UniqueValueStore(Unique unique) {
super(unique);
} // <init>(Unique)
//
// ValueStoreBase protected methods
//
/**
* Called when a duplicate value is added.
*
* @param tuple The duplicate value tuple.
*/
protected void duplicateValue(OrderedHashtable tuple)
throws Exception {
int code = SchemaMessageProvider.DuplicateUnique;
String value = toString(tuple);
String ename = fIdentityConstraint.getElementName();
reportSchemaError(code, new Object[]{value,ename});
} // duplicateValue(Hashtable)
} // class UniqueValueStore
/**
* Key value store.
*
* @author Andy Clark, IBM
*/
protected class KeyValueStore
extends ValueStoreBase {
// REVISIT: Implement a more efficient storage mechanism. -Ac
//
// Constructors
//
/** Constructs a key value store. */
public KeyValueStore(Key key) {
super(key);
} // <init>(Key)
//
// ValueStoreBase protected methods
//
/**
* Called when a duplicate value is added.
*
* @param tuple The duplicate value tuple.
*/
protected void duplicateValue(OrderedHashtable tuple)
throws Exception {
int code = SchemaMessageProvider.DuplicateKey;
String value = toString(tuple);
String ename = fIdentityConstraint.getElementName();
reportSchemaError(code, new Object[]{value,ename});
} // duplicateValue(Hashtable)
} // class KeyValueStore
/**
* Key reference value store.
*
* @author Andy Clark, IBM
*/
protected class KeyRefValueStore
extends ValueStoreBase {
//
// Data
//
/** Key value store. */
protected ValueStoreBase fKeyValueStore;
//
// Constructors
//
/** Constructs a key value store. */
public KeyRefValueStore(KeyRef keyRef, KeyValueStore keyValueStore) {
super(keyRef);
fKeyValueStore = keyValueStore;
} // <init>(KeyRef)
//
// ValueStoreBase methods
//
// end the value Scope; here's where we have to tie
// up keyRef loose ends.
public void endDocumentFragment () throws Exception {
// do all the necessary management...
super.endDocumentFragment ();
// verify references
// get the key store corresponding (if it exists):
fKeyValueStore = (ValueStoreBase)fValueStoreCache.fGlobalIDConstraintMap.get(((KeyRef)fIdentityConstraint).getKey());
if(fKeyValueStore == null) {
// report error
int code = SchemaMessageProvider.KeyRefOutOfScope;
String value = fIdentityConstraint.toString();
reportSchemaError(code, new Object[]{value});
return;
}
int count = fValueTuples.size();
for (int i = 0; i < count; i++) {
OrderedHashtable values = (OrderedHashtable)fValueTuples.elementAt(i);
if (!fKeyValueStore.contains(values)) {
int code = SchemaMessageProvider.KeyNotFound;
String value = toString(values);
String element = fIdentityConstraint.getElementName();
reportSchemaError(code, new Object[]{value,element});
}
}
} // endDocumentFragment()
/** End document. */
public void endDocument() throws Exception {
super.endDocument();
} // endDocument()
} // class KeyRefValueStore
// value store management
/**
* Value store cache. This class is used to store the values for
* identity constraints.
*
* @author Andy Clark, IBM
*/
protected class ValueStoreCache {
//
// Data
//
// values stores
/** stores all global Values stores. */
protected final Vector fValueStores = new Vector();
/** Values stores associated to specific identity constraints. */
protected final Hashtable fIdentityConstraint2ValueStoreMap = new Hashtable();
// sketch of algorithm:
// - when a constraint is first encountered, its
// values are stored in the (local) fIdentityConstraint2ValueStoreMap;
// - Once it is validated (i.e., wen it goes out of scope),
// its values are merged into the fGlobalIDConstraintMap;
// - as we encounter keyref's, we look at the global table to
// validate them.
// the fGlobalIDMapStack has the following structure:
// - validation always occurs against the fGlobalIDConstraintMap
// (which comprises all the "eligible" id constraints);
// When an endelement is found, this Hashtable is merged with the one
// below in the stack.
// When a start tag is encountered, we create a new
// fGlobalIDConstraintMap.
// i.e., the top of the fGlobalIDMapStack always contains
// the preceding siblings' eligible id constraints;
// the fGlobalIDConstraintMap contains descendants+self.
// keyrefs can only match descendants+self.
protected final Stack fGlobalMapStack = new Stack();
protected final Hashtable fGlobalIDConstraintMap = new Hashtable();
//
// Constructors
//
/** Default constructor. */
public ValueStoreCache() {
} // <init>()
//
// Public methods
//
/** Resets the identity constraint cache. */
public void startDocument() throws Exception {
if (DEBUG_VALUE_STORES) {
System.out.println("<VS>: "+toString()+"#startDocument()");
}
fValueStores.removeAllElements();
fIdentityConstraint2ValueStoreMap.clear();
fGlobalIDConstraintMap.clear();
fGlobalMapStack.removeAllElements();
} // startDocument()
// startElement: pushes the current fGlobalIDConstraintMap
// onto fGlobalMapStack and clears fGlobalIDConstraint map.
public void startElement() {
fGlobalMapStack.push(fGlobalIDConstraintMap.clone());
fGlobalIDConstraintMap.clear();
} // startElement(void)
// endElement(): merges contents of fGlobalIDConstraintMap with the
// top of fGlobalMapStack into fGlobalIDConstraintMap.
public void endElement() {
if (fGlobalMapStack.isEmpty()) return; // must be an invalid doc!
Hashtable oldMap = (Hashtable)fGlobalMapStack.pop();
Enumeration keys = oldMap.keys();
while(keys.hasMoreElements()) {
IdentityConstraint id = (IdentityConstraint)keys.nextElement();
ValueStoreBase oldVal = (ValueStoreBase)oldMap.get(id);
if(oldVal != null) {
ValueStoreBase currVal = (ValueStoreBase)fGlobalIDConstraintMap.get(id);
if (currVal == null)
fGlobalIDConstraintMap.put(id, oldVal);
else {
currVal.append(oldVal);
fGlobalIDConstraintMap.put(id, currVal);
}
}
}
} // endElement()
/**
* Initializes the value stores for the specified element
* declaration.
*/
public void initValueStoresFor(XMLElementDecl eDecl)
throws Exception {
if (DEBUG_VALUE_STORES) {
System.out.println("<VS>: "+toString()+"#initValueStoresFor("+
fStringPool.toString(eDecl.name.rawname)+
")");
}
// initialize value stores for unique fields
Vector uVector = eDecl.unique;
int uCount = uVector.size();
for (int i = 0; i < uCount; i++) {
Unique unique = (Unique)uVector.elementAt(i);
UniqueValueStore valueStore = (UniqueValueStore)fIdentityConstraint2ValueStoreMap.get(unique);
if (valueStore != null) {
// NOTE: If already initialized, don't need to
// do it again. -Ac
continue;
}
valueStore = new UniqueValueStore(unique);
fValueStores.addElement(valueStore);
if (DEBUG_VALUE_STORES) {
System.out.println("<VS>: "+unique+" -> "+valueStore);
}
fIdentityConstraint2ValueStoreMap.put(unique, valueStore);
}
// initialize value stores for key fields
Vector kVector = eDecl.key;
int kCount = kVector.size();
for (int i = 0; i < kCount; i++) {
Key key = (Key)kVector.elementAt(i);
KeyValueStore valueStore = (KeyValueStore)fIdentityConstraint2ValueStoreMap.get(key);
if (valueStore != null) {
// NOTE: If already initialized, don't need to
// do it again. -Ac
continue;
}
valueStore = new KeyValueStore(key);
fValueStores.addElement(valueStore);
if (DEBUG_VALUE_STORES) {
System.out.println("<VS>: "+key+" -> "+valueStore);
}
fIdentityConstraint2ValueStoreMap.put(key, valueStore);
}
// initialize value stores for key reference fields
Vector krVector = eDecl.keyRef;
int krCount = krVector.size();
for (int i = 0; i < krCount; i++) {
KeyRef keyRef = (KeyRef)krVector.elementAt(i);
KeyRefValueStore keyRefValueStore = new KeyRefValueStore(keyRef, null);
fValueStores.addElement(keyRefValueStore);
if (DEBUG_VALUE_STORES) {
System.out.println("<VS>: "+keyRef+" -> "+keyRefValueStore);
}
fIdentityConstraint2ValueStoreMap.put(keyRef, keyRefValueStore);
}
} // initValueStoresFor(XMLElementDecl)
/** Returns the value store associated to the specified field. */
public ValueStoreBase getValueStoreFor(Field field) {
if (DEBUG_VALUE_STORES) {
System.out.println("<VS>: "+toString()+"#getValueStoreFor("+field+")");
}
IdentityConstraint identityConstraint = field.getIdentityConstraint();
return (ValueStoreBase)fIdentityConstraint2ValueStoreMap.get(identityConstraint);
} // getValueStoreFor(Field):ValueStoreBase
/** Returns the value store associated to the specified IdentityConstraint. */
public ValueStoreBase getValueStoreFor(IdentityConstraint id) {
if (DEBUG_VALUE_STORES) {
System.out.println("<VS>: "+toString()+"#getValueStoreFor("+id+")");
}
return (ValueStoreBase)fIdentityConstraint2ValueStoreMap.get(id);
} // getValueStoreFor(IdentityConstraint):ValueStoreBase
/** Returns the global value store associated to the specified IdentityConstraint. */
public ValueStoreBase getGlobalValueStoreFor(IdentityConstraint id) {
if (DEBUG_VALUE_STORES) {
System.out.println("<VS>: "+toString()+"#getGlobalValueStoreFor("+id+")");
}
return (ValueStoreBase)fGlobalIDConstraintMap.get(id);
} // getValueStoreFor(IdentityConstraint):ValueStoreBase
// This method takes the contents of the (local) ValueStore
// associated with id and moves them into the global
// hashtable, if id is a <unique> or a <key>.
// If it's a <keyRef>, then we leave it for later.
public void transplant(IdentityConstraint id) throws Exception {
if (id.getType() == IdentityConstraint.KEYREF ) return;
ValueStoreBase newVals = (ValueStoreBase)fIdentityConstraint2ValueStoreMap.get(id);
fIdentityConstraint2ValueStoreMap.remove(id);
ValueStoreBase currVals = (ValueStoreBase)fGlobalIDConstraintMap.get(id);
if (currVals != null) {
currVals.append(newVals);
fGlobalIDConstraintMap.put(id, currVals);
} else
fGlobalIDConstraintMap.put(id, newVals);
} // transplant(id)
/** Check identity constraints. */
public void endDocument() throws Exception {
if (DEBUG_VALUE_STORES) {
System.out.println("<VS>: "+toString()+"#endDocument()");
}
int count = fValueStores.size();
for (int i = 0; i < count; i++) {
ValueStoreBase valueStore = (ValueStoreBase)fValueStores.elementAt(i);
valueStore.endDocument();
}
} // endDocument()
//
// Object methods
//
/** Returns a string representation of this object. */
public String toString() {
String s = super.toString();
int index1 = s.lastIndexOf('$');
if (index1 != -1) {
return s.substring(index1 + 1);
}
int index2 = s.lastIndexOf('.');
if (index2 != -1) {
return s.substring(index2 + 1);
}
return s;
} // toString():String
} // class ValueStoreCache
// utility classes
/**
* Ordered hashtable. This class acts as a hashtable with
* <code>put()</code> and <code>get()</code> operations but also
* allows values to be queried via the order that they were
* added to the hashtable.
* <p>
* <strong>Note:</strong> This class does not perform any error
* checking.
* <p>
* <strong>Note:</strong> This class is <em>not</em> efficient but
* is assumed to be used for a very small set of values.
*
* @author Andy Clark, IBM
*/
static final class OrderedHashtable
implements Cloneable {
//
// Data
//
/** Size. */
private int fSize;
/** Hashtable entries. */
private Entry[] fEntries = null;
//
// Public methods
//
/** Returns the number of entries in the hashtable. */
public int size() {
return fSize;
} // size():int
/** Puts an entry into the hashtable. */
public void put(Field key, IDValue value) {
int index = indexOf(key);
if (index == -1) {
ensureCapacity(fSize);
index = fSize++;
fEntries[index].key = key;
}
fEntries[index].value = value;
} // put(Field,String)
/** Returns the value associated to the specified key. */
public IDValue get(Field key) {
return fEntries[indexOf(key)].value;
} // get(Field):String
/** Returns the index of the entry with the specified key. */
public int indexOf(Field key) {
for (int i = 0; i < fSize; i++) {
// NOTE: Only way to be sure that the keys are the
// same is by using a reference comparison. In
// order to rely on the equals method, each
// field would have to take into account its
// position in the identity constraint, the
// identity constraint, the declaring element,
// and the grammar that it is defined in.
// Otherwise, you have the possibility that
// the equals method would return true for two
// fields that look *very* similar.
// The reference compare isn't bad, actually,
// because the field objects are cacheable. -Ac
if (fEntries[i].key == key) {
return i;
}
}
return -1;
} // indexOf(Field):int
/** Returns the key at the specified index. */
public Field keyAt(int index) {
return fEntries[index].key;
} // keyAt(int):Field
/** Returns the value at the specified index. */
public IDValue valueAt(int index) {
return fEntries[index].value;
} // valueAt(int):String
/** Removes all of the entries from the hashtable. */
public void clear() {
fSize = 0;
} // clear()
//
// Private methods
//
/** Ensures the capacity of the entries array. */
private void ensureCapacity(int size) {
// sizes
int osize = -1;
int nsize = -1;
// create array
if (fEntries == null) {
osize = 0;
nsize = 2;
fEntries = new Entry[nsize];
}
// resize array
else if (fEntries.length <= size) {
osize = fEntries.length;
nsize = 2 * osize;
Entry[] array = new Entry[nsize];
System.arraycopy(fEntries, 0, array, 0, osize);
fEntries = array;
}
// create new entries
for (int i = osize; i < nsize; i++) {
fEntries[i] = new Entry();
}
} // ensureCapacity(int)
//
// Cloneable methods
//
/** Clones this object. */
public Object clone() {
OrderedHashtable hashtable = new OrderedHashtable();
for (int i = 0; i < fSize; i++) {
hashtable.put(fEntries[i].key, fEntries[i].value);
}
return hashtable;
} // clone():Object
//
// Object methods
//
/** Returns a string representation of this object. */
public String toString() {
if (fSize == 0) {
return "[]";
}
StringBuffer str = new StringBuffer();
str.append('[');
for (int i = 0; i < fSize; i++) {
if (i > 0) {
str.append(',');
}
str.append('{');
str.append(fEntries[i].key);
str.append(',');
str.append(fEntries[i].value);
str.append('}');
}
str.append(']');
return str.toString();
} // toString():String
//
// Classes
//
/**
* Hashtable entry.
*/
public static final class Entry {
//
// Data
//
/** Key. */
public Field key;
/** Value. */
public IDValue value;
} // class Entry
} // class OrderedHashtable
} // class XMLValidator