blob: 052044fcf01a3d5249ddd0c72e1798c426f5bdfa [file] [log] [blame]
/*
* 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.impl.dtd;
import org.apache.xerces.impl.Constants;
import org.apache.xerces.impl.XMLErrorReporter;
import org.apache.xerces.impl.validation.ValidationManager;
import org.apache.xerces.impl.msg.XMLMessageFormatter;
import org.apache.xerces.impl.validation.EntityState;
import org.apache.xerces.impl.dtd.models.ContentModelValidator;
import org.apache.xerces.impl.dv.dtd.DatatypeValidator;
import org.apache.xerces.impl.dv.dtd.DatatypeValidatorFactory;
import org.apache.xerces.impl.validation.GrammarPool;
import org.apache.xerces.impl.dv.dtd.DatatypeValidatorFactoryImpl;
import org.apache.xerces.impl.dv.dtd.ENTITYDatatypeValidator;
import org.apache.xerces.impl.dv.dtd.IDDatatypeValidator;
import org.apache.xerces.impl.dv.dtd.IDREFDatatypeValidator;
import org.apache.xerces.impl.dv.dtd.ListDatatypeValidator;
import org.apache.xerces.impl.dv.dtd.NOTATIONDatatypeValidator;
import org.apache.xerces.impl.dv.dtd.InvalidDatatypeFacetException;
import org.apache.xerces.impl.dv.dtd.InvalidDatatypeValueException;
import org.apache.xerces.util.SymbolTable;
import org.apache.xerces.util.XMLChar;
import org.apache.xerces.xni.Augmentations;
import org.apache.xerces.xni.QName;
import org.apache.xerces.xni.XMLString;
import org.apache.xerces.xni.XMLAttributes;
import org.apache.xerces.xni.XMLDocumentHandler;
import org.apache.xerces.xni.XMLDTDHandler;
import org.apache.xerces.xni.XMLDTDContentModelHandler;
import org.apache.xerces.xni.XMLLocator;
import org.apache.xerces.xni.XNIException;
import org.apache.xerces.xni.parser.XMLComponent;
import org.apache.xerces.xni.parser.XMLComponentManager;
import org.apache.xerces.xni.parser.XMLConfigurationException;
import org.apache.xerces.xni.parser.XMLDocumentFilter;
import org.apache.xerces.xni.parser.XMLDTDFilter;
import org.apache.xerces.xni.parser.XMLDTDContentModelFilter;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import java.util.StringTokenizer;
/**
* The DTD validator. The validator implements a document
* filter: receiving document events from the scanner; validating
* the content and structure; augmenting the InfoSet, if applicable;
* and notifying the parser of the information resulting from the
* validation process.
* <p>
* This component requires the following features and properties from the
* component manager that uses it:
* <ul>
* <li>http://xml.org/sax/features/namespaces</li>
* <li>http://xml.org/sax/features/validation</li>
* <li>http://apache.org/xml/features/validation/dynamic</li>
* <li>http://apache.org/xml/properties/internal/symbol-table</li>
* <li>http://apache.org/xml/properties/internal/error-reporter</li>
* <li>http://apache.org/xml/properties/internal/grammar-pool</li>
* <li>http://apache.org/xml/properties/internal/datatype-validator-factory</li>
* </ul>
*
* @author Eric Ye, IBM
* @author Stubs generated by DesignDoc on Mon Sep 11 11:10:57 PDT 2000
* @author Andy Clark, IBM
* @author Jeffrey Rodriguez IBM
*
* @version $Id$
*/
public class XMLDTDValidator
implements XMLComponent,
XMLDocumentFilter, XMLDTDFilter, XMLDTDContentModelFilter {
//
// Constants
//
/** Top level scope (-1). */
private static final int TOP_LEVEL_SCOPE = -1;
// feature identifiers
/** Feature identifier: namespaces. */
protected static final String NAMESPACES =
Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACES_FEATURE;
/** Feature identifier: validation. */
protected static final String VALIDATION =
Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE;
/** Feature identifier: dynamic validation. */
protected static final String DYNAMIC_VALIDATION =
Constants.XERCES_FEATURE_PREFIX + Constants.DYNAMIC_VALIDATION_FEATURE;
/** Feature identifier: xml schema validation */
protected static final String SCHEMA_VALIDATION =
Constants.XERCES_FEATURE_PREFIX +Constants.SCHEMA_VALIDATION_FEATURE;
// property identifiers
/** Property identifier: symbol table. */
protected static final String SYMBOL_TABLE =
Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;
/** Property identifier: error reporter. */
protected static final String ERROR_REPORTER =
Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;
/** Property identifier: grammar pool. */
protected static final String GRAMMAR_POOL =
Constants.XERCES_PROPERTY_PREFIX + Constants.GRAMMAR_POOL_PROPERTY;
/** Property identifier: datatype validator factory. */
protected static final String DATATYPE_VALIDATOR_FACTORY =
Constants.XERCES_PROPERTY_PREFIX + Constants.DATATYPE_VALIDATOR_FACTORY_PROPERTY;
protected static final String VALIDATION_MANAGER =
Constants.XERCES_PROPERTY_PREFIX + Constants.VALIDATION_MANAGER_PROPERTY;
// recognized features and properties
/** Recognized features. */
protected static final String[] RECOGNIZED_FEATURES = {
NAMESPACES,
VALIDATION,
DYNAMIC_VALIDATION,
SCHEMA_VALIDATION
};
/** Recognized properties. */
protected static final String[] RECOGNIZED_PROPERTIES = {
SYMBOL_TABLE,
ERROR_REPORTER,
GRAMMAR_POOL,
DATATYPE_VALIDATOR_FACTORY,
VALIDATION_MANAGER
};
// debugging
/** Compile to true to debug attributes. */
private static final boolean DEBUG_ATTRIBUTES = false;
/** Compile to true to debug element children. */
private static final boolean DEBUG_ELEMENT_CHILDREN = false;
//
// Data
//
// updated during reset
protected ValidationManager fValidationManager = null;
// features
/** Namespaces. */
protected boolean fNamespaces;
/** Validation. */
protected boolean fValidation;
/** Validation against only DTD */
protected boolean fDTDValidation;
/**
* Dynamic validation. This state of this feature is only useful when
* the validation feature is set to <code>true</code>.
*/
protected boolean fDynamicValidation;
// properties
/** Symbol table. */
protected SymbolTable fSymbolTable;
/** Error reporter. */
protected XMLErrorReporter fErrorReporter;
/** Grammar pool. */
protected GrammarPool fGrammarPool;
/** Datatype validator factory. */
protected DatatypeValidatorFactory fDatatypeValidatorFactory;
// handlers
/** Document handler. */
protected XMLDocumentHandler fDocumentHandler;
/** DTD handler. */
protected XMLDTDHandler fDTDHandler;
/** DTD content model handler. */
protected XMLDTDContentModelHandler fDTDContentModelHandler;
// grammars
/** DTD Grammar. */
protected DTDGrammar fDTDGrammar;
// state
/** Perform validation. */
private boolean fPerformValidation;
/** Skip validation. */
private boolean fSkipValidation;
/** True if in an ignore conditional section of the DTD. */
protected boolean fInDTDIgnore;
// information regarding the current element
/** Current element name. */
private final QName fCurrentElement = new QName();
/** Current element index. */
private int fCurrentElementIndex = -1;
/** Current content spec type. */
private int fCurrentContentSpecType = -1;
/** The root element name. */
private final QName fRootElement = new QName();
/** True if seen DOCTYPE declaration. */
private boolean fSeenDoctypeDecl = false;
private boolean fInCDATASection = false;
// element stack
/** Element index stack. */
private int[] fElementIndexStack = new int[8];
/** Content spec type stack. */
private int[] fContentSpecTypeStack = new int[8];
/** Element name stack. */
private QName[] fElementQNamePartsStack = new QName[8];
// children list and offset stack
/**
* Element children. This data structure is a growing stack that
* holds the children of elements from the root to the current
* element depth. This structure never gets "deeper" than the
* deepest element. Space is re-used once each element is closed.
* <p>
* <strong>Note:</strong> This is much more efficient use of memory
* than creating new arrays for each element depth.
* <p>
* <strong>Note:</strong> The use of this data structure is for
* validation "on the way out". If the validation model changes to
* "on the way in", then this data structure is not needed.
*/
private QName[] fElementChildren = new QName[32];
/** Element children count. */
private int fElementChildrenLength = 0;
/**
* Element children offset stack. This stack refers to offsets
* into the <code>fElementChildren</code> array.
* @see #fElementChildren
*/
private int[] fElementChildrenOffsetStack = new int[32];
/** Element depth. */
private int fElementDepth = -1;
// validation states
/** Validation of a standalone document. */
private boolean fStandaloneIsYes = false;
/** True if seen the root element. */
private boolean fSeenRootElement = false;
/** True if inside of element content. */
private boolean fInElementContent = false;
/** Mixed. */
private boolean fMixed;
// temporary variables
/** Temporary element declaration. */
private XMLElementDecl fTempElementDecl = new XMLElementDecl();
/** Temporary atribute declaration. */
private XMLAttributeDecl fTempAttDecl = new XMLAttributeDecl();
/** Temporary entity declaration. */
private XMLEntityDecl fEntityDecl = new XMLEntityDecl();
/** Temporary qualified name. */
private QName fTempQName = new QName();
/** Temporary string buffer for buffering datatype value. */
//private StringBuffer fDatatypeBuffer = new StringBuffer();
/** Notation declaration hash. */
private Hashtable fNDataDeclNotations = new Hashtable();
/** DTD element declaration name. */
private String fDTDElementDeclName = null;
/** Mixed element type "hash". */
private Vector fMixedElementTypes = new Vector();
/** Element declarations in DTD. */
private Vector fDTDElementDecls = new Vector();
/** Temporary string buffers. */
private StringBuffer fBuffer = new StringBuffer();
// symbols: general
/** Symbol: "EMPTY". */
private String fEMPTYSymbol;
/** Symbol: "ANY". */
private String fANYSymbol;
/** Symbol: "MIXED". */
private String fMIXEDSymbol;
/** Symbol: "CHILDREN". */
private String fCHILDRENSymbol;
// symbols: DTD datatype
/** Symbol: "CDATA". */
private String fCDATASymbol;
/** Symbol: "ID". */
private String fIDSymbol;
/** Symbol: "IDREF". */
private String fIDREFSymbol;
/** Symbol: "IDREFS". */
private String fIDREFSSymbol;
/** Symbol: "ENTITY". */
private String fENTITYSymbol;
/** Symbol: "ENTITIES". */
private String fENTITIESSymbol;
/** Symbol: "NMTOKEN". */
private String fNMTOKENSymbol;
/** Symbol: "NMTOKENS". */
private String fNMTOKENSSymbol;
/** Symbol: "NOTATION". */
private String fNOTATIONSymbol;
/** Symbol: "ENUMERATION". */
private String fENUMERATIONSymbol;
/** Symbol: "#IMPLIED. */
private String fIMPLIEDSymbol;
/** Symbol: "#REQUIRED". */
private String fREQUIREDSymbol;
/** Symbol: "#FIXED". */
private String fFIXEDSymbol;
/** Symbol: "&lt;&lt;datatypes>>". */
private String fDATATYPESymbol;
// attribute validators
/** Datatype validator: ID. */
private IDDatatypeValidator fValID;
/** Datatype validator: IDREF. */
private IDREFDatatypeValidator fValIDRef;
/** Datatype validator: IDREFS. */
private ListDatatypeValidator fValIDRefs;
/** Datatype validator: ENTITY. */
private ENTITYDatatypeValidator fValENTITY;
/** Datatype validator: ENTITIES. */
private ListDatatypeValidator fValENTITIES;
/** Datatype validator: NMTOKEN. */
private DatatypeValidator fValNMTOKEN;
/** Datatype validator: NMTOKENS. */
private DatatypeValidator fValNMTOKENS;
/** Datatype validator: NOTATION. */
private NOTATIONDatatypeValidator fValNOTATION;
/**
* This table has to be own by instance of XMLValidator and shared
* among ID, IDREF and IDREFS.
* <p>
* <strong>Note:</strong> Only ID has read/write access.
* <p>
* <strong>Note:</strong> Should revisit and replace with a ligther
* structure.
*/
private Hashtable fTableOfIDs;
// to check for duplicate ID or ANNOTATION attribute declare in
// ATTLIST, and misc VCs
/** ID attribute names. */
private Hashtable fTableOfIDAttributeNames;
/** NOTATION attribute names. */
private Hashtable fTableOfNOTATIONAttributeNames;
/** NOTATION enumeration values. */
private Hashtable fNotationEnumVals;
//
// Constructors
//
/** Default constructor. */
public XMLDTDValidator() {
// initialize data
for (int i = 0; i < fElementQNamePartsStack.length; i++) {
fElementQNamePartsStack[i] = new QName();
}
} // <init>()
//
// XMLComponent methods
//
/*
* Resets the component. The component can query the component manager
* about any features and properties that affect the operation of the
* component.
*
* @param componentManager The component manager.
*
* @throws SAXException Thrown by component on finitialization error.
* For example, if a feature or property is
* required for the operation of the component, the
* component manager may throw a
* SAXNotRecognizedException or a
* SAXNotSupportedException.
*/
public void reset(XMLComponentManager componentManager)
throws XMLConfigurationException {
// clear grammars
fDTDGrammar = null;
fSeenDoctypeDecl = false;
fInCDATASection = false;
// initialize state
fInDTDIgnore = false;
fStandaloneIsYes = false;
fSeenRootElement = false;
fInElementContent = false;
fCurrentElementIndex = -1;
fCurrentContentSpecType = -1;
fSkipValidation=false;
fRootElement.clear();
fNDataDeclNotations.clear();
// sax features
try {
fNamespaces = componentManager.getFeature(NAMESPACES);
}
catch (XMLConfigurationException e) {
fNamespaces = true;
}
try {
fValidation = componentManager.getFeature(VALIDATION);
}
catch (XMLConfigurationException e) {
fValidation = false;
}
try {
fDTDValidation = !(componentManager.getFeature(SCHEMA_VALIDATION));
}
catch (XMLConfigurationException e) {
fValidation = false;
}
// Xerces features
try {
fDynamicValidation = componentManager.getFeature(DYNAMIC_VALIDATION);
}
catch (XMLConfigurationException e) {
fDynamicValidation = false;
}
fValidationManager= (ValidationManager)componentManager.getProperty(VALIDATION_MANAGER);
fValidationManager.reset();
// get needed components
fErrorReporter = (XMLErrorReporter)componentManager.getProperty(Constants.XERCES_PROPERTY_PREFIX+Constants.ERROR_REPORTER_PROPERTY);
fSymbolTable = (SymbolTable)componentManager.getProperty(Constants.XERCES_PROPERTY_PREFIX+Constants.SYMBOL_TABLE_PROPERTY);
fGrammarPool = (GrammarPool)componentManager.getProperty(Constants.XERCES_PROPERTY_PREFIX+Constants.GRAMMAR_POOL_PROPERTY);
fDatatypeValidatorFactory = (DatatypeValidatorFactory)componentManager.getProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.DATATYPE_VALIDATOR_FACTORY_PROPERTY);
fElementDepth = -1;
init();
} // reset(XMLComponentManager)
/**
* Returns a list of feature identifiers that are recognized by
* this component. This method may return null if no features
* are recognized by this component.
*/
public String[] getRecognizedFeatures() {
return RECOGNIZED_FEATURES;
} // getRecognizedFeatures():String[]
/**
* Sets the state of a feature. This method is called by the component
* manager any time after reset when a feature changes state.
* <p>
* <strong>Note:</strong> Components should silently ignore features
* that do not affect the operation of the component.
*
* @param featureId The feature identifier.
* @param state The state of the feature.
*
* @throws SAXNotRecognizedException The component should not throw
* this exception.
* @throws SAXNotSupportedException The component should not throw
* this exception.
*/
public void setFeature(String featureId, boolean state)
throws XMLConfigurationException {
} // setFeature(String,boolean)
/**
* Returns a list of property identifiers that are recognized by
* this component. This method may return null if no properties
* are recognized by this component.
*/
public String[] getRecognizedProperties() {
return RECOGNIZED_PROPERTIES;
} // getRecognizedProperties():String[]
/**
* Sets the value of a property. This method is called by the component
* manager any time after reset when a property changes value.
* <p>
* <strong>Note:</strong> Components should silently ignore properties
* that do not affect the operation of the component.
*
* @param propertyId The property identifier.
* @param value The value of the property.
*
* @throws SAXNotRecognizedException The component should not throw
* this exception.
* @throws SAXNotSupportedException The component should not throw
* this exception.
*/
public void setProperty(String propertyId, Object value)
throws XMLConfigurationException {
} // setProperty(String,Object)
//
// XMLDocumentSource methods
//
/**
* Sets the document handler to receive information about the document.
*
* @param documentHandler The document handler.
*/
public void setDocumentHandler(XMLDocumentHandler documentHandler) {
fDocumentHandler = documentHandler;
} // setDocumentHandler(XMLDocumentHandler)
//
// XMLDTDSource methods
//
/**
* Sets the DTD handler.
*
* @param dtdHandler The DTD handler.
*/
public void setDTDHandler(XMLDTDHandler dtdHandler) {
fDTDHandler = dtdHandler;
} // setDTDHandler(XMLDTDHandler)
//
// XMLDTDContentModelSource methods
//
/**
* Sets the DTD content model handler.
*
* @param dtdContentModelHandler The DTD content model handler.
*/
public void setDTDContentModelHandler(XMLDTDContentModelHandler dtdContentModelHandler) {
fDTDContentModelHandler = dtdContentModelHandler;
} // setDTDContentModelHandler(XMLDTDContentModelHandler)
//
// XMLDocumentHandler methods
//
/**
* The start of the document.
*
* @param systemId The system identifier of the entity if the entity
* is external, null otherwise.
* @param encoding The auto-detected IANA encoding name of the entity
* stream. This value will be null in those situations
* where the entity encoding is not auto-detected (e.g.
* internal entities or a document entity that is
* parsed from a java.io.Reader).
* @param augs Additional information that may include infoset augmentations
* @throws XNIException Thrown by handler to signal an error.
*/
public void startDocument(XMLLocator locator, String encoding, Augmentations augs)
throws XNIException {
// call handlers
if (fDocumentHandler != null) {
fDocumentHandler.startDocument(locator, encoding, augs);
}
} // startDocument(XMLLocator,String)
/**
* Notifies of the presence of an XMLDecl line in the document. If
* present, this method will be called immediately following the
* startDocument call.
*
* @param version The XML version.
* @param encoding The IANA encoding name of the document, or null if
* not specified.
* @param standalone The standalone value, or null if not specified.
* @param augs Additional information that may include infoset augmentations
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void xmlDecl(String version, String encoding, String standalone, Augmentations augs)
throws XNIException {
// save standalone state
fStandaloneIsYes = standalone != null && standalone.equals("yes");
// call handlers
if (fDocumentHandler != null) {
fDocumentHandler.xmlDecl(version, encoding, standalone, augs);
}
} // xmlDecl(String,String,String)
/**
* Notifies of the presence of the DOCTYPE line in the document.
*
* @param rootElement The name of the root element.
* @param publicId The public identifier if an external DTD or null
* if the external DTD is specified using SYSTEM.
* @param systemId The system identifier if an external DTD, null
* otherwise.
* @param augs Additional information that may include infoset augmentations
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void doctypeDecl(String rootElement, String publicId, String systemId,
Augmentations augs)
throws XNIException {
// save root element state
fSeenDoctypeDecl = true;
fRootElement.setValues(null, rootElement, rootElement, null);
// call handlers
if (fDocumentHandler != null) {
fDocumentHandler.doctypeDecl(rootElement, publicId, systemId, augs);
}
} // doctypeDecl(String,String,String)
/**
* The start of a namespace prefix mapping. This method will only be
* called when namespace processing is enabled.
*
* @param prefix The namespace prefix.
* @param uri The URI bound to the prefix.
* @param augs Additional information that may include infoset augmentations
* @throws XNIException Thrown by handler to signal an error.
*/
public void startPrefixMapping(String prefix, String uri, Augmentations augs)
throws XNIException {
// call handlers
if (fDocumentHandler != null) {
fDocumentHandler.startPrefixMapping(prefix, uri, augs);
}
} // startPrefixMapping(String,String)
/**
* The start of an element.
*
* @param element The name of the element.
* @param attributes The element attributes.
* @param augs Additional information that may include infoset augmentations
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void startElement(QName element, XMLAttributes attributes, Augmentations augs)
throws XNIException {
handleStartElement(element, attributes);
// call handlers
if (fDocumentHandler != null) {
fDocumentHandler.startElement(element, attributes, augs);
}
} // startElement(QName,XMLAttributes)
/**
* An empty element.
*
* @param element The name of the element.
* @param attributes The element attributes.
* @param augs Additional information that may include infoset augmentations
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void emptyElement(QName element, XMLAttributes attributes, Augmentations augs)
throws XNIException {
handleStartElement(element, attributes);
if (fDocumentHandler !=null) {
fDocumentHandler.emptyElement(element, attributes, augs);
}
handleEndElement(element, augs, true);
} // emptyElement(QName,XMLAttributes)
/**
* Character content.
*
* @param text The content.
*
* @param augs Additional information that may include infoset augmentations
* @throws XNIException Thrown by handler to signal an error.
*/
public void characters(XMLString text, Augmentations augs) throws XNIException {
boolean callNextCharacters = true;
// REVISIT: [Q] Is there a more efficient way of doing this?
// Perhaps if the scanner told us so we don't have to
// look at the characters again. -Ac
boolean allWhiteSpace = true;
for (int i=text.offset; i< text.offset+text.length; i++) {
if (!XMLChar.isSpace(text.ch[i])) {
allWhiteSpace = false;
break;
}
}
// call the ignoreableWhiteSpace callback
// never call ignorableWhitespace if we are in cdata section
if (fInElementContent && allWhiteSpace && !fInCDATASection) {
if (fDocumentHandler != null) {
fDocumentHandler.ignorableWhitespace(text, augs);
callNextCharacters = false;
}
}
// validate
if (fPerformValidation) {
if (fInElementContent) {
if (fStandaloneIsYes &&
fDTDGrammar.getElementDeclIsExternal(fCurrentElementIndex)) {
if (allWhiteSpace) {
fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,
"MSG_WHITE_SPACE_IN_ELEMENT_CONTENT_WHEN_STANDALONE",
null, XMLErrorReporter.SEVERITY_ERROR);
}
}
if (!allWhiteSpace) {
charDataInContent();
}
}
if (fCurrentContentSpecType == XMLElementDecl.TYPE_EMPTY) {
charDataInContent();
}
}
// call handlers
if (callNextCharacters && fDocumentHandler != null) {
fDocumentHandler.characters(text, augs);
}
} // characters(XMLString)
/**
* Ignorable whitespace. For this method to be called, the document
* source must have some way of determining that the text containing
* only whitespace characters should be considered ignorable. For
* example, the validator can determine if a length of whitespace
* characters in the document are ignorable based on the element
* content model.
*
* @param text The ignorable whitespace.
*
* @param augs Additional information that may include infoset augmentations
* @throws XNIException Thrown by handler to signal an error.
*/
public void ignorableWhitespace(XMLString text, Augmentations augs) throws XNIException {
// call handlers
if (fDocumentHandler != null) {
fDocumentHandler.ignorableWhitespace(text, augs);
}
} // ignorableWhitespace(XMLString)
/**
* The end of an element.
*
* @param element The name of the element.
* @param augs Additional information that may include infoset augmentations
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void endElement(QName element, Augmentations augs) throws XNIException {
handleEndElement(element, augs, false);
} // endElement(QName)
/**
* The end of a namespace prefix mapping. This method will only be
* called when namespace processing is enabled.
*
* @param prefix The namespace prefix.
* @param augs Additional information that may include infoset augmentations
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void endPrefixMapping(String prefix, Augmentations augs) throws XNIException {
// call handlers
if (fDocumentHandler != null) {
fDocumentHandler.endPrefixMapping(prefix, augs);
}
} // endPrefixMapping(String)
/**
* The start of a CDATA section.
* @param augs Additional information that may include infoset augmentations
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void startCDATA(Augmentations augs) throws XNIException {
if (fPerformValidation && fInElementContent) {
charDataInContent();
}
fInCDATASection = true;
// call handlers
if (fDocumentHandler != null) {
fDocumentHandler.startCDATA(augs);
}
} // startCDATA()
/**
* The end of a CDATA section.
* @param augs Additional information that may include infoset augmentations
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void endCDATA(Augmentations augs) throws XNIException {
fInCDATASection = false;
// call handlers
if (fDocumentHandler != null) {
fDocumentHandler.endCDATA(augs);
}
} // endCDATA()
/**
* The end of the document.
* @param augs Additional information that may include infoset augmentations
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void endDocument(Augmentations augs) throws XNIException {
// call handlers
if (fDocumentHandler != null) {
fDocumentHandler.endDocument(augs);
}
} // endDocument()
//
// XMLDocumentHandler and XMLDTDHandler methods
//
/**
* This method notifies of the start of an entity. The DTD has the
* pseudo-name of "[dtd]" parameter entity names start with '%'; and
* general entity names are just the entity name.
* <p>
* <strong>Note:</strong> This method is not called for entity references
* appearing as part of attribute values.
*
* @param name The name of the entity.
* @param publicId The public identifier of the entity if the entity
* is external, null otherwise.
* @param systemId The system identifier of the entity if the entity
* is external, null otherwise.
* @param baseSystemId The base system identifier of the entity if
* the entity is external, null otherwise.
* @param encoding The auto-detected IANA encoding name of the entity
* stream. This value will be null in those situations
* where the entity encoding is not auto-detected (e.g.
* internal parameter entities).
*
* @param augs Additional information that may include infoset augmentations
* @throws XNIException Thrown by handler to signal an error.
*/
public void startEntity(String name,
String publicId, String systemId,
String baseSystemId,
String encoding,
Augmentations augs) throws XNIException {
// check VC: Standalone Document Declartion, entities references appear in the document.
if (fPerformValidation && fDTDGrammar != null) {
if (fStandaloneIsYes && !name.startsWith("[")) {
int entIndex = fDTDGrammar.getEntityDeclIndex(name);
if (entIndex > -1) {
fDTDGrammar.getEntityDecl(entIndex, fEntityDecl);
if (fEntityDecl.inExternal) {
fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,
"MSG_REFERENCE_TO_EXTERNALLY_DECLARED_ENTITY_WHEN_STANDALONE",
new Object[]{name}, XMLErrorReporter.SEVERITY_ERROR);
}
}
}
}
if (fDocumentHandler != null) {
fDocumentHandler.startEntity(name, publicId, systemId,
baseSystemId, encoding, augs);
}
} // startEntity(String,String,String,String,String, Augmentations)
/**
* Notifies of the presence of a TextDecl line in an entity. If present,
* this method will be called immediately following the startEntity call.
* <p>
* <strong>Note:</strong> This method will never be called for the
* document entity; it is only called for external general entities
* referenced in document content.
* <p>
* <strong>Note:</strong> This method is not called for entity references
* appearing as part of attribute values.
*
* @param version The XML version, or null if not specified.
* @param encoding The IANA encoding name of the entity.
* @param augs Additional information that may include infoset augmentations
*
* @exception XNIException
* Thrown by handler to signal an error.
*/
public void textDecl(String version, String encoding, Augmentations augs) throws XNIException {
if (fDocumentHandler != null) {
fDocumentHandler.textDecl(version, encoding, augs);
}
} // textDecl(String,String)
/**
* A comment.
*
* @param text The text in the comment.
*
* @param augs Additional information that may include infoset augmentations
* @throws XNIException Thrown by application to signal an error.
*/
public void comment(XMLString text, Augmentations augs) throws XNIException {
// call handlers
if (fDocumentHandler != null) {
fDocumentHandler.comment(text, augs);
}
} // comment(XMLString)
/**
* A processing instruction. Processing instructions consist of a
* target name and, optionally, text data. The data is only meaningful
* to the application.
* <p>
* Typically, a processing instruction's data will contain a series
* of pseudo-attributes. These pseudo-attributes follow the form of
* element attributes but are <strong>not</strong> parsed or presented
* to the application as anything other than text. The application is
* responsible for parsing the data.
*
* @param target The target.
* @param data The data or null if none specified.
* @param augs Additional information that may include infoset augmentations
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void processingInstruction(String target, XMLString data, Augmentations augs)
throws XNIException {
// call handlers
if (fDocumentHandler != null) {
fDocumentHandler.processingInstruction(target, data, augs);
}
} // processingInstruction(String,XMLString)
/**
* This method notifies the end of an entity.
* <p>
* <strong>Note:</strong> This method is not called for entity references
* appearing as part of attribute values.
*
* @param name The name of the entity.
* @param augs Additional information that may include infoset augmentations
*
* @exception XNIException
* Thrown by handler to signal an error.
*/
public void endEntity(String name, Augmentations augs) throws XNIException {
// call handlers
if (fDocumentHandler != null) {
fDocumentHandler.endEntity(name, augs);
}
} // endEntity(String)
//
// XMLDTDHandler methods
//
/**
* The start of the DTD.
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void startDTD(XMLLocator locator) throws XNIException {
// initialize state
fNDataDeclNotations.clear();
fDTDElementDecls.removeAllElements();
// create DTD grammar
fDTDGrammar = createDTDGrammar();
//fDTDGrammar.setDatatypeValidatorFactory(fDatatypeValidatorFactory);
// REVISIT: should we use the systemId as the key instead?
fGrammarPool.putGrammar("", fDTDGrammar);
// call handlers
fDTDGrammar.startDTD(locator);
if (fDTDHandler != null) {
fDTDHandler.startDTD(locator);
}
} // startDTD(XMLLocator)
/**
* A comment.
*
* @param text The text in the comment.
*
* @throws XNIException Thrown by application to signal an error.
*/
public void comment(XMLString text) throws XNIException {
// call handlers
fDTDGrammar.comment(text);
if (fDTDHandler != null) {
fDTDHandler.comment(text);
}
}
/**
* Character content.
*
* @param text The content.
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void characters(XMLString text) throws XNIException {
// ignored characters in DTD
if (fDTDHandler != null) {
fDTDHandler.characters(text);
}
}
/**
* Notifies of the presence of a TextDecl line in an entity. If present,
* this method will be called immediately following the startEntity call.
* <p>
* <strong>Note:</strong> This method is only called for external
* parameter entities referenced in the DTD.
*
* @param version The XML version, or null if not specified.
* @param encoding The IANA encoding name of the entity.
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void textDecl(String version, String encoding) throws XNIException {
// call handlers
fDTDGrammar.textDecl(version, encoding);
if (fDTDHandler != null) {
fDTDHandler.textDecl(version, encoding);
}
}
/**
* This method notifies of the start of an entity. The DTD has the
* pseudo-name of "[dtd]" and parameter entity names start with '%'.
*
* @param name The name of the entity.
* @param publicId The public identifier of the entity if the entity
* is external, null otherwise.
* @param systemId The system identifier of the entity if the entity
* is external, null otherwise.
* @param baseSystemId The base system identifier of the entity if
* the entity is external, null otherwise.
* @param encoding The auto-detected IANA encoding name of the entity
* stream. This value will be null in those situations
* where the entity encoding is not auto-detected (e.g.
* internal parameter entities).
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void startEntity(String name,
String publicId, String systemId,
String baseSystemId,
String encoding) throws XNIException {
// call handlers
fDTDGrammar.startEntity(name, publicId, systemId,
baseSystemId, encoding);
if (fDTDHandler != null) {
fDTDHandler.startEntity(name, publicId, systemId,
baseSystemId, encoding);
}
}
/**
* This method notifies the end of an entity. The DTD has the pseudo-name
* of "[dtd]" and parameter entity names start with '%'.
*
* @param name The name of the entity.
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void endEntity(String name) throws XNIException {
// call handlers
fDTDGrammar.endEntity(name);
if (fDTDHandler != null) {
fDTDHandler.endEntity(name);
}
}
/**
* A processing instruction. Processing instructions consist of a
* target name and, optionally, text data. The data is only meaningful
* to the application.
* <p>
* Typically, a processing instruction's data will contain a series
* of pseudo-attributes. These pseudo-attributes follow the form of
* element attributes but are <strong>not</strong> parsed or presented
* to the application as anything other than text. The application is
* responsible for parsing the data.
*
* @param target The target.
* @param data The data or null if none specified.
* @exception XNIException
* Thrown by handler to signal an error.
*/
public void processingInstruction(String target, XMLString data)
throws XNIException {
// call handlers
fDTDGrammar.processingInstruction(target, data);
if (fDTDHandler != null) {
fDTDHandler.processingInstruction(target, data);
}
}
/**
* An element declaration.
*
* @param name The name of the element.
* @param contentModel The element content model.
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void elementDecl(String name, String contentModel)
throws XNIException {
//check VC: Unique Element Declaration
if (fValidation) {
if (fDTDElementDecls.contains(name)) {
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"MSG_ELEMENT_ALREADY_DECLARED",
new Object[]{ name},
XMLErrorReporter.SEVERITY_ERROR);
}
else {
fDTDElementDecls.addElement(name);
}
}
// call handlers
fDTDGrammar.elementDecl(name, contentModel);
if (fDTDHandler != null) {
fDTDHandler.elementDecl(name, contentModel);
}
} // elementDecl(String,String)
/**
* The start of an attribute list.
*
* @param elementName The name of the element that this attribute
* list is associated with.
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void startAttlist(String elementName) throws XNIException {
// call handlers
fDTDGrammar.startAttlist(elementName);
if (fDTDHandler != null) {
fDTDHandler.startAttlist(elementName);
}
} // startAttlist(String)
/**
* An attribute declaration.
*
* @param elementName The name of the element that this attribute
* is associated with.
* @param attributeName The name of the attribute.
* @param type The attribute type. This value will be one of
* the following: "CDATA", "ENTITY", "ENTITIES",
* "ENUMERATION", "ID", "IDREF", "IDREFS",
* "NMTOKEN", "NMTOKENS", or "NOTATION".
* @param enumeration If the type has the value "ENUMERATION", this
* array holds the allowed attribute values;
* otherwise, this array is null.
* @param defaultType The attribute default type. This value will be
* one of the following: "#FIXED", "#IMPLIED",
* "#REQUIRED", or null.
* @param defaultValue The attribute default value, or null if no
* default value is specified.
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void attributeDecl(String elementName, String attributeName,
String type, String[] enumeration,
String defaultType, XMLString defaultValue)
throws XNIException {
if (type != fCDATASymbol && defaultValue != null) {
normalizeDefaultAttrValue(defaultValue);
}
if (fValidation) {
//
// a) VC: One ID per Element Type, If duplicate ID attribute
// b) VC: ID attribute Default. if there is a declareared attribute
// default for ID it should be of type #IMPLIED or #REQUIRED
if (type == fIDSymbol) {
if (defaultValue != null && defaultValue.length != 0) {
if (defaultType == null ||
!(defaultType == fIMPLIEDSymbol ||
defaultType == fREQUIREDSymbol)) {
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"IDDefaultTypeInvalid",
new Object[]{ attributeName},
XMLErrorReporter.SEVERITY_ERROR);
}
}
if (!fTableOfIDAttributeNames.containsKey(elementName)) {
fTableOfIDAttributeNames.put(elementName, attributeName);
}
else {
String previousIDAttributeName = (String)fTableOfIDAttributeNames.get( elementName );//rule a)
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"MSG_MORE_THAN_ONE_ID_ATTRIBUTE",
new Object[]{ elementName, previousIDAttributeName, attributeName},
XMLErrorReporter.SEVERITY_ERROR);
}
}
//
// VC: One Notaion Per Element Type, should check if there is a
// duplicate NOTATION attribute
if (type == fNOTATIONSymbol) {
// VC: Notation Attributes: all notation names in the
// (attribute) declaration must be declared.
for (int i=0; i<enumeration.length; i++) {
fNotationEnumVals.put(enumeration[i], attributeName);
}
if (fTableOfNOTATIONAttributeNames.containsKey( elementName ) == false) {
fTableOfNOTATIONAttributeNames.put( elementName, attributeName);
}
else {
String previousNOTATIONAttributeName = (String) fTableOfNOTATIONAttributeNames.get( elementName );
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"MSG_MORE_THAN_ONE_NOTATION_ATTRIBUTE",
new Object[]{ elementName, previousNOTATIONAttributeName, attributeName},
XMLErrorReporter.SEVERITY_ERROR);
}
}
// VC: Attribute Default Legal
boolean ok = true;
if (defaultValue != null &&
(defaultType == null ||
(defaultType != null && defaultType == fFIXEDSymbol))) {
String value = defaultValue.toString();
if (type == fNMTOKENSSymbol ||
type == fENTITIESSymbol || type == fIDREFSSymbol) {
StringTokenizer tokenizer = new StringTokenizer(value);
if (tokenizer.hasMoreTokens()) {
while (true) {
String nmtoken = tokenizer.nextToken();
if (type == fNMTOKENSSymbol) {
if (!XMLChar.isValidNmtoken(nmtoken)) {
ok = false;
break;
}
}
else if (type == fENTITIESSymbol ||
type == fIDREFSSymbol) {
if (!XMLChar.isValidName(nmtoken)) {
ok = false;
break;
}
}
if (!tokenizer.hasMoreTokens()) {
break;
}
}
}
}
else {
if (type == fENTITYSymbol ||
type == fIDSymbol ||
type == fIDREFSymbol ||
type == fNOTATIONSymbol) {
if (!XMLChar.isValidName(value)) {
ok = false;
}
}
else if (type == fNMTOKENSymbol ||
type == fENUMERATIONSymbol) {
if (!XMLChar.isValidNmtoken(value)) {
ok = false;
}
}
if (type == fNOTATIONSymbol ||
type == fENUMERATIONSymbol) {
ok = false;
for (int i=0; i<enumeration.length; i++) {
if (defaultValue.equals(enumeration[i])) {
ok = true;
}
}
}
}
if (!ok) {
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"MSG_ATT_DEFAULT_INVALID",
new Object[]{attributeName, value},
XMLErrorReporter.SEVERITY_ERROR);
}
}
}
// call handlers
fDTDGrammar.attributeDecl(elementName, attributeName,
type, enumeration,
defaultType, defaultValue);
if (fDTDHandler != null) {
fDTDHandler.attributeDecl(elementName, attributeName,
type, enumeration,
defaultType, defaultValue);
}
} // attributeDecl(String,String,String,String[],String,XMLString)
/**
* The end of an attribute list.
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void endAttlist() throws XNIException {
// call handlers
fDTDGrammar.endAttlist();
if (fDTDHandler != null) {
fDTDHandler.endAttlist();
}
} // endAttlist()
/**
* An internal entity declaration.
*
* @param name The name of the entity. Parameter entity names start with
* '%', whereas the name of a general entity is just the
* entity name.
* @param text The value of the entity.
* @param nonNormalizedText The non-normalized value of the entity. This
* value contains the same sequence of characters that was in
* the internal entity declaration, without any entity
* references expanded.
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void internalEntityDecl(String name, XMLString text,
XMLString nonNormalizedText)
throws XNIException {
// call handlers
fDTDGrammar.internalEntityDecl(name, text, nonNormalizedText);
if (fDTDHandler != null) {
fDTDHandler.internalEntityDecl(name, text, nonNormalizedText);
}
} // internalEntityDecl(String,XMLString,XMLString)
/**
* An external entity declaration.
*
* @param name The name of the entity. Parameter entity names start
* with '%', whereas the name of a general entity is just
* the entity name.
* @param publicId The public identifier of the entity or null if the
* the entity was specified with SYSTEM.
* @param systemId The system identifier of the entity.
* @param baseSystemId The base system identifier of where the entity
* is declared.
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void externalEntityDecl(String name,
String publicId, String systemId,
String baseSystemId) throws XNIException {
// call handlers
fDTDGrammar.externalEntityDecl(name, publicId, systemId, baseSystemId);
if (fDTDHandler != null) {
fDTDHandler.externalEntityDecl(name, publicId, systemId,
baseSystemId);
}
} // externalEntityDecl(String,String,String,String)
/**
* An unparsed entity declaration.
*
* @param name The name of the entity.
* @param publicId The public identifier of the entity, or null if not
* specified.
* @param systemId The system identifier of the entity, or null if not
* specified.
* @param notation The name of the notation.
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void unparsedEntityDecl(String name,
String publicId, String systemId,
String notation) throws XNIException {
// VC: Notation declared, in the production of NDataDecl
if (fValidation) {
fNDataDeclNotations.put(name, notation);
}
// call handlers
fDTDGrammar.unparsedEntityDecl(name, publicId, systemId, notation);
if (fDTDHandler != null) {
fDTDHandler.unparsedEntityDecl(name, publicId, systemId, notation);
}
} // unparsedEntityDecl(String,String,String,String)
/**
* A notation declaration
*
* @param name The name of the notation.
* @param publicId The public identifier of the notation, or null if not
* specified.
* @param systemId The system identifier of the notation, or null if not
* specified.
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void notationDecl(String name, String publicId, String systemId)
throws XNIException {
// call handlers
fDTDGrammar.notationDecl(name, publicId, systemId);
if (fDTDHandler != null) {
fDTDHandler.notationDecl(name, publicId, systemId);
}
} // notationDecl(String,String,String)
/**
* The start of a conditional section.
*
* @param type The type of the conditional section. This value will
* either be CONDITIONAL_INCLUDE or CONDITIONAL_IGNORE.
*
* @throws XNIException Thrown by handler to signal an error.
*
* @see XMLDTDHandler#CONDITIONAL_INCLUDE
* @see XMLDTDHandler#CONDITIONAL_IGNORE
*/
public void startConditional(short type) throws XNIException {
// set state
fInDTDIgnore = type == XMLDTDHandler.CONDITIONAL_IGNORE;
// call handlers
fDTDGrammar.startConditional(type);
if (fDTDHandler != null) {
fDTDHandler.startConditional(type);
}
} // startConditional(short)
/**
* The end of a conditional section.
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void endConditional() throws XNIException {
// set state
fInDTDIgnore = false;
// call handlers
fDTDGrammar.endConditional();
if (fDTDHandler != null) {
fDTDHandler.endConditional();
}
} // endConditional()
/**
* The end of the DTD.
*
* @throws XNIException Thrown by handler to signal an error.
*/
public void endDTD() throws XNIException {
// save grammar
fDTDGrammar.endDTD();
// check VC: Notation declared, in the production of NDataDecl
if (fValidation) {
fValENTITY.initialize(fDTDGrammar);//Initialize ENTITY, ENTITIES validators
fValENTITIES.initialize(fDTDGrammar);
// VC : Notation Declared. for external entity declaration [Production 76].
Enumeration entities = fNDataDeclNotations.keys();
while (entities.hasMoreElements()) {
String entity = (String) entities.nextElement();
String notation = (String) fNDataDeclNotations.get(entity);
if (fDTDGrammar.getNotationDeclIndex(notation) == -1) {
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"MSG_NOTATION_NOT_DECLARED_FOR_UNPARSED_ENTITYDECL",
new Object[]{entity, notation},
XMLErrorReporter.SEVERITY_ERROR);
}
}
// VC: Notation Attributes:
// all notation names in the (attribute) declaration must be declared.
Enumeration notationVals = fNotationEnumVals.keys();
while (notationVals.hasMoreElements()) {
String notation = (String) notationVals.nextElement();
String attributeName = (String) fNotationEnumVals.get(notation);
if (fDTDGrammar.getNotationDeclIndex(notation) == -1) {
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"MSG_NOTATION_NOT_DECLARED_FOR_NOTATIONTYPE_ATTRIBUTE",
new Object[]{attributeName, notation},
XMLErrorReporter.SEVERITY_ERROR);
}
}
fTableOfIDAttributeNames = null;//should be safe to release these references
fTableOfNOTATIONAttributeNames = null;
}
// call handlers
if (fDTDHandler != null) {
fDTDHandler.endDTD();
}
} // endDTD()
//
// XMLDTDContentModelHandler methods
//
/** Start content model. */
public void startContentModel(String elementName) throws XNIException {
if (fValidation) {
fDTDElementDeclName = elementName;
fMixedElementTypes.removeAllElements();
}
// call handlers
fDTDGrammar.startContentModel(elementName);
if (fDTDContentModelHandler != null) {
fDTDContentModelHandler.startContentModel(elementName);
}
} // startContentModel(String)
/** ANY. */
public void any() throws XNIException {
fDTDGrammar.any();
if (fDTDContentModelHandler != null) {
fDTDContentModelHandler.any();
}
} // any()
/** EMPTY. */
public void empty() throws XNIException {
fDTDGrammar.empty();
if (fDTDContentModelHandler != null) {
fDTDContentModelHandler.empty();
}
} // empty()
/** Start group. */
public void startGroup() throws XNIException {
fMixed = false;
// call handlers
fDTDGrammar.startGroup();
if (fDTDContentModelHandler != null) {
fDTDContentModelHandler.startGroup();
}
} // startGroup()
/** #PCDATA. */
public void pcdata() {
fMixed = true;
fDTDGrammar.pcdata();
if (fDTDContentModelHandler != null) {
fDTDContentModelHandler.pcdata();
}
} // pcdata()
/** Element. */
public void element(String elementName) throws XNIException {
// check VC: No duplicate Types, in a single mixed-content declaration
if (fMixed && fValidation) {
if (fMixedElementTypes.contains(elementName)) {
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"DuplicateTypeInMixedContent",
new Object[]{fDTDElementDeclName, elementName},
XMLErrorReporter.SEVERITY_ERROR);
}
else {
fMixedElementTypes.addElement(elementName);
}
}
// call handlers
fDTDGrammar.element(elementName);
if (fDTDContentModelHandler != null) {
fDTDContentModelHandler.element(elementName);
}
} // childrenElement(String)
/** Separator. */
public void separator(short separator) throws XNIException {
// call handlers
fDTDGrammar.separator(separator);
if (fDTDContentModelHandler != null) {
fDTDContentModelHandler.separator(separator);
}
} // separator(short)
/** Occurrence. */
public void occurrence(short occurrence) throws XNIException {
// call handlers
fDTDGrammar.occurrence(occurrence);
if (fDTDContentModelHandler != null) {
fDTDContentModelHandler.occurrence(occurrence);
}
} // occurrence(short)
/** End group. */
public void endGroup() throws XNIException {
// call handlers
fDTDGrammar.endGroup();
if (fDTDContentModelHandler != null) {
fDTDContentModelHandler.endGroup();
}
} // endGroup()
/** End content model. */
public void endContentModel() throws XNIException {
// call handlers
fDTDGrammar.endContentModel();
if (fDTDContentModelHandler != null) {
fDTDContentModelHandler.endContentModel();
}
} // endContentModel()
//
// Private methods
//
/** Add default attributes and validate. */
private void addDTDDefaultAttrsAndValidate(int elementIndex,
XMLAttributes attributes)
throws XNIException {
// is there anything to do?
if (elementIndex == -1 || fDTDGrammar == null) {
return;
}
// get element info
fDTDGrammar.getElementDecl(elementIndex,fTempElementDecl);
QName element = fTempElementDecl.name;
//
// Check after all specified attrs are scanned
// (1) report error for REQUIRED attrs that are missing (V_TAGc)
// (2) add default attrs (FIXED and NOT_FIXED)
//
int attlistIndex = fDTDGrammar.getFirstAttributeDeclIndex(elementIndex);
while (attlistIndex != -1) {
fDTDGrammar.getAttributeDecl(attlistIndex, fTempAttDecl);
if (DEBUG_ATTRIBUTES) {
if (fTempAttDecl != null) {
XMLElementDecl elementDecl = new XMLElementDecl();
fDTDGrammar.getElementDecl(elementIndex, elementDecl);
System.out.println("element: "+(elementDecl.name.localpart));
System.out.println("attlistIndex " + attlistIndex + "\n"+
"attName : '"+(fTempAttDecl.name.localpart) + "'\n"
+ "attType : "+fTempAttDecl.simpleType.type + "\n"
+ "attDefaultType : "+fTempAttDecl.simpleType.defaultType + "\n"
+ "attDefaultValue : '"+fTempAttDecl.simpleType.defaultValue + "'\n"
+ attributes.getLength() +"\n"
);
}
}
String attPrefix = fTempAttDecl.name.prefix;
String attLocalpart = fTempAttDecl.name.localpart;
String attRawName = fTempAttDecl.name.rawname;
String attType = getAttributeTypeName(fTempAttDecl);
int attDefaultType =fTempAttDecl.simpleType.defaultType;
String attValue = null;
if (fTempAttDecl.simpleType.defaultValue != null) {
attValue = fTempAttDecl.simpleType.defaultValue;
}
boolean specified = false;
boolean required = attDefaultType == XMLSimpleType.DEFAULT_TYPE_REQUIRED;
boolean cdata = attType == fCDATASymbol;
if (!cdata || required || attValue != null) {
int attrCount = attributes.getLength();
for (int i = 0; i < attrCount; i++) {
if (attributes.getQName(i) == attRawName) {
specified = true;
break;
}
}
}
if (!specified) {
if (required) {
if (fValidation) {
Object[] args = {element.localpart, attRawName};
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"MSG_REQUIRED_ATTRIBUTE_NOT_SPECIFIED", args,
XMLErrorReporter.SEVERITY_ERROR);
}
}
else if (attValue != null) {
if (fPerformValidation && fStandaloneIsYes) {
if (fDTDGrammar.getAttributeDeclIsExternal(attlistIndex)) {
Object[] args = { element.localpart, attRawName};
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"MSG_DEFAULTED_ATTRIBUTE_NOT_SPECIFIED", args,
XMLErrorReporter.SEVERITY_ERROR);
}
}
// add namespace information
if (fNamespaces) {
int index = attRawName.indexOf(':');
if (index != -1) {
attPrefix = attRawName.substring(0, index);
attPrefix = fSymbolTable.addSymbol(attPrefix);
attLocalpart = attRawName.substring(index + 1);
attLocalpart = fSymbolTable.addSymbol(attLocalpart);
}
}
// add attribute
fTempQName.setValues(attPrefix, attLocalpart, attRawName, fTempAttDecl.name.uri);
int newAttr = attributes.addAttribute(fTempQName, attType, attValue);
}
}
// get next att decl in the Grammar for this element
attlistIndex = fDTDGrammar.getNextAttributeDeclIndex(attlistIndex);
}
// now iterate through the expanded attributes for
// 1. if every attribute seen is declared in the DTD
// 2. check if the VC: default_fixed holds
// 3. validate every attribute.
int attrCount = attributes.getLength();
for (int i = 0; i < attrCount; i++) {
String attrRawName = attributes.getQName(i);
boolean declared = false;
if (fPerformValidation) {
if (fStandaloneIsYes) {
// check VC: Standalone Document Declaration, entities
// references appear in the document.
// REVISIT: this can be combined to a single check in
// startEntity if we add one more argument in
// startEnity, inAttrValue
String nonNormalizedValue = attributes.getNonNormalizedValue(i);
if (nonNormalizedValue != null) {
String entityName = getExternalEntityRefInAttrValue(nonNormalizedValue);
if (entityName != null) {
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"MSG_REFERENCE_TO_EXTERNALLY_DECLARED_ENTITY_WHEN_STANDALONE",
new Object[]{entityName},
XMLErrorReporter.SEVERITY_ERROR);
}
}
}
}
int attDefIndex = -1;
int position =
fDTDGrammar.getFirstAttributeDeclIndex(elementIndex);
while (position != -1) {
fDTDGrammar.getAttributeDecl(position, fTempAttDecl);
if (fTempAttDecl.name.rawname == attrRawName) {
// found the match att decl,
attDefIndex = position;
declared = true;
break;
}
position = fDTDGrammar.getNextAttributeDeclIndex(position);
}
if (!declared) {
if (fPerformValidation) {
// REVISIT - cache the elem/attr tuple so that we only
// give this error once for each unique occurrence
Object[] args = { element.rawname, attrRawName};
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"MSG_ATTRIBUTE_NOT_DECLARED",
args,XMLErrorReporter.SEVERITY_ERROR);
}
continue;
}
// attribute is declared
// fTempAttDecl should have the right value set now, so
// the following is not needed
// fGrammar.getAttributeDecl(attDefIndex,fTempAttDecl);
String type = getAttributeTypeName(fTempAttDecl);
attributes.setType(i, type);
boolean changedByNormalization = false;
String oldValue = attributes.getValue(i);
String attrValue = oldValue;
if (attributes.isSpecified(i) && type != fCDATASymbol) {
changedByNormalization = normalizeAttrValue(attributes, i);
attrValue = attributes.getValue(i);
if (fPerformValidation && fStandaloneIsYes
&& changedByNormalization
&& fDTDGrammar.getAttributeDeclIsExternal(position)
) {
// check VC: Standalone Document Declaration
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"MSG_ATTVALUE_CHANGED_DURING_NORMALIZATION_WHEN_STANDALONE",
new Object[]{attrRawName, oldValue, attrValue},
XMLErrorReporter.SEVERITY_ERROR);
}
}
if (!fPerformValidation) {
continue;
}
if (fTempAttDecl.simpleType.defaultType ==
XMLSimpleType.DEFAULT_TYPE_FIXED) {
String defaultValue = fTempAttDecl.simpleType.defaultValue;
if (!attrValue.equals(defaultValue)) {
Object[] args = {element.localpart,
attrRawName,
attrValue,
defaultValue};
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"MSG_FIXED_ATTVALUE_INVALID",
args, XMLErrorReporter.SEVERITY_ERROR);
}
}
if (fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_ENTITY ||
fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_ENUMERATION ||
fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_ID ||
fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_IDREF ||
fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_NMTOKEN ||
fTempAttDecl.simpleType.type == XMLSimpleType.TYPE_NOTATION
) {
validateDTDattribute(element, attrValue, fTempAttDecl);
}
} // for all attributes
} // addDTDDefaultAttrsAndValidate(int,XMLAttrList)
/** Checks entities in attribute values for standalone VC. */
private String getExternalEntityRefInAttrValue(String nonNormalizedValue) {
int valLength = nonNormalizedValue.length();
int ampIndex = nonNormalizedValue.indexOf('&');
while (ampIndex != -1) {
if (ampIndex + 1 < valLength &&
nonNormalizedValue.charAt(ampIndex+1) != '#') {
int semicolonIndex = nonNormalizedValue.indexOf(';', ampIndex+1);
String entityName = nonNormalizedValue.substring(ampIndex+1, semicolonIndex);
entityName = fSymbolTable.addSymbol(entityName);
int entIndex = fDTDGrammar.getEntityDeclIndex(entityName);
if (entIndex > -1) {
fDTDGrammar.getEntityDecl(entIndex, fEntityDecl);
if (fEntityDecl.inExternal ||
(entityName = getExternalEntityRefInAttrValue(fEntityDecl.value)) != null) {
return entityName;
}
}
}
ampIndex = nonNormalizedValue.indexOf('&', ampIndex+1);
}
return null;
} // isExternalEntityRefInAttrValue(String):String
/**
* Validate attributes in DTD fashion.
*/
private void validateDTDattribute(QName element, String attValue,
XMLAttributeDecl attributeDecl)
throws XNIException {
switch (attributeDecl.simpleType.type) {
case XMLSimpleType.TYPE_ENTITY: {
// NOTE: Save this information because invalidStandaloneAttDef
boolean isAlistAttribute = attributeDecl.simpleType.list;
try {
if (isAlistAttribute) {
fValENTITIES.validate(attValue, null);
}
else {
fValENTITY.validate(attValue, null);
}
}
catch (InvalidDatatypeValueException ex) {
String key = ex.getKeyIntoReporter();
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
key,
new Object[]{ ex.getMessage()},
XMLErrorReporter.SEVERITY_ERROR );
}
break;
}
case XMLSimpleType.TYPE_NOTATION:
case XMLSimpleType.TYPE_ENUMERATION: {
boolean found = false;
String [] enumVals = attributeDecl.simpleType.enumeration;
if (enumVals == null) {
found = false;
}
else
for (int i = 0; i < enumVals.length; i++) {
if (attValue == enumVals[i] || attValue.equals(enumVals[i])) {
found = true;
break;
}
}
if (!found) {
StringBuffer enumValueString = new StringBuffer();
if (enumVals != null)
for (int i = 0; i < enumVals.length; i++) {
enumValueString.append(enumVals[i]+" ");
}
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"MSG_ATTRIBUTE_VALUE_NOT_IN_LIST",
new Object[]{attributeDecl.name.rawname, attValue, enumValueString},
XMLErrorReporter.SEVERITY_ERROR);
}
break;
}
case XMLSimpleType.TYPE_ID: {
try {
fValID.validate(attValue, null);
}
catch (InvalidDatatypeValueException ex) {
String key = ex.getKeyIntoReporter();
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
key,
new Object[] { ex.getMessage()},
XMLErrorReporter.SEVERITY_ERROR );
}
break;
}
case XMLSimpleType.TYPE_IDREF: {
boolean isAlistAttribute = attributeDecl.simpleType.list;//Caveat - Save this information because invalidStandaloneAttDef
try {
if (isAlistAttribute) {
//System.out.println("values = >>" + value + "<<" );
fValIDRefs.validate(attValue, null);
}
else {
fValIDRef.validate(attValue, null);
}
}
catch (InvalidDatatypeValueException ex) {
String key = ex.getKeyIntoReporter();
if (key == null) {
key = "IDREFSInvalid";
}
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
key,
new Object[]{ ex.getMessage()},
XMLErrorReporter.SEVERITY_ERROR );
}
break;
}
case XMLSimpleType.TYPE_NMTOKEN: {
boolean isAlistAttribute = attributeDecl.simpleType.list;//Caveat - Save this information because invalidStandaloneAttDef
//changes fTempAttDef
try {
if (isAlistAttribute) {
fValNMTOKENS.validate(attValue, null);
}
else {
fValNMTOKEN.validate(attValue, null);
}
}
catch (InvalidDatatypeValueException ex) {
if (isAlistAttribute) {
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"NMTOKENSInvalid",
new Object[] { attValue},
XMLErrorReporter.SEVERITY_ERROR);
}
else {
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"NMTOKENInvalid",
new Object[] { attValue},
XMLErrorReporter.SEVERITY_ERROR);
}
}
break;
}
} // switch
} // validateDTDattribute(QName,String,XMLAttributeDecl)
/** Returns true if invalid standalone attribute definition. */
boolean invalidStandaloneAttDef(QName element, QName attribute) {
// REVISIT: This obviously needs to be fixed! -Ac
boolean state = true;
/*
if (fStandaloneReader == -1) {
return false;
}
// we are normalizing a default att value... this ok?
if (element.rawname == -1) {
return false;
}
return getAttDefIsExternal(element, attribute);
*/
return state;
}
/**
* Normalize the attribute value of a non CDATA attributes collapsing
* sequences of space characters (x20)
*
* @param attributes The list of attributes
* @param index The index of the attribute to normalize
*/
private boolean normalizeAttrValue(XMLAttributes attributes, int index) {
// vars
boolean leadingSpace = true;
boolean spaceStart = false;
boolean readingNonSpace = false;
int count = 0;
int eaten = 0;
String attrValue = attributes.getValue(index);
char[] attValue = new char[attrValue.length()];
fBuffer.setLength(0);
attrValue.getChars(0, attrValue.length(), attValue, 0);
for (int i = 0; i < attValue.length; i++) {
if (attValue[i] == ' ') {
// now the tricky part
if (readingNonSpace) {
spaceStart = true;
readingNonSpace = false;
}
if (spaceStart && !leadingSpace) {
spaceStart = false;
fBuffer.append(attValue[i]);
count++;
}
else {
if (leadingSpace || !spaceStart) {
eaten ++;
/*** BUG #3512 ***
int entityCount = attributes.getEntityCount(index);
for (int j = 0; j < entityCount; j++) {
int offset = attributes.getEntityOffset(index, j);
int length = attributes.getEntityLength(index, j);
if (offset <= i-eaten+1) {
if (offset+length >= i-eaten+1) {
if (length > 0)
length--;
}
}
else {
if (offset > 0)
offset--;
}
attributes.setEntityOffset(index, j, offset);
attributes.setEntityLength(index, j, length);
}
/***/
}
}
}
else {
readingNonSpace = true;
spaceStart = false;
leadingSpace = false;
fBuffer.append(attValue[i]);
count++;
}
}
// check if the last appended character is a space.
if (count > 0 && fBuffer.charAt(count-1) == ' ') {
fBuffer.setLength(count-1);
/*** BUG #3512 ***
int entityCount = attributes.getEntityCount(index);
for (int j=0; j < entityCount; j++) {
int offset = attributes.getEntityOffset(index, j);
int length = attributes.getEntityLength(index, j);
if (offset < count-1) {
if (offset+length == count) {
length--;
}
}
else {
offset--;
}
attributes.setEntityOffset(index, j, offset);
attributes.setEntityLength(index, j, length);
}
/***/
}
String newValue = fBuffer.toString();
attributes.setValue(index, newValue);
return ! attrValue.equals(newValue);
}
/**
* Normalize the attribute value of a non CDATA default attribute
* collapsing sequences of space characters (x20)
*
* @param value The value to normalize
* @return Whether the value was changed or not.
*/
private boolean normalizeDefaultAttrValue(XMLString value) {
int oldLength = value.length;
boolean skipSpace = true; // skip leading spaces
int current = value.offset;
int end = value.offset + value.length;
for (int i = value.offset; i < end; i++) {
if (value.ch[i] == ' ') {
if (!skipSpace) {
// take the first whitespace as a space and skip the others
value.ch[current++] = ' ';
skipSpace = true;
}
else {
// just skip it.
}
}
else {
// simply shift non space chars if needed
if (current != i) {
value.ch[current] = value.ch[i];
}
current++;
skipSpace = false;
}
}
if (current != end) {
if (skipSpace) {
// if we finished on a space trim it
current--;
}
// set the new value length
value.length = current - value.offset;
return true;
}
return false;
}
/** Root element specified. */
private void rootElementSpecified(QName rootElement) throws XNIException {
if (fPerformValidation) {
String root1 = fRootElement.rawname;
String root2 = rootElement.rawname;
if (root1 == null || !root1.equals(root2)) {
fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,
"RootElementTypeMustMatchDoctypedecl",
new Object[]{root1, root2},
XMLErrorReporter.SEVERITY_ERROR);
}
}
} // rootElementSpecified(QName)
/**
* 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.
*
* @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 XNIException {
fDTDGrammar.getElementDecl(elementIndex, fTempElementDecl);
// Get the element name index from the element
final String elementType = fCurrentElement.rawname;
// 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 ||
contentType == XMLElementDecl.TYPE_CHILDREN) {
// Get the content model for this element, faulting it in if needed
ContentModelValidator cmElem = null;
cmElem = fTempElementDecl.contentModelValidator;
int result = cmElem.validate(children, childOffset, childCount);
return result;
}
else if (contentType == -1) {
//REVISIT
/****
reportRecoverableXMLError(XMLMessages.MSG_ELEMENT_NOT_DECLARED,
XMLMessages.VC_ELEMENT_VALID,
elementType);
/****/
}
else if (contentType == XMLElementDecl.TYPE_SIMPLE) {
//REVISIT
// this should never be reached in the case of DTD validation.
}
else {
//REVISIT
/****
fErrorReporter.reportError(fErrorReporter.getLocator(),
ImplementationMessages.XERCES_IMPLEMENTATION_DOMAIN,
ImplementationMessages.VAL_CST,
0,
null,
XMLErrorReporter.ERRORTYPE_FATAL_ERROR);
/****/
}
// We succeeded
return -1;
} // checkContent(int,int,QName[]):int
/** Returns the content spec type for an element index. */
private int getContentSpecType(int elementIndex) {
int contentSpecType = -1;
if (elementIndex > -1) {
if (fDTDGrammar.getElementDecl(elementIndex,fTempElementDecl)) {
contentSpecType = fTempElementDecl.type;
}
}
return contentSpecType;
}
/** 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()
/** convert attribute type from ints to strings */
private String getAttributeTypeName(XMLAttributeDecl attrDecl) {
switch (attrDecl.simpleType.type) {
case XMLSimpleType.TYPE_ENTITY: {
return attrDecl.simpleType.list ? fENTITIESSymbol : fENTITYSymbol;
}
case XMLSimpleType.TYPE_ENUMERATION: {
StringBuffer buffer = new StringBuffer();
buffer.append('(');
for (int i=0; i<attrDecl.simpleType.enumeration.length ; i++) {
if (i > 0) {
buffer.append("|");
}
buffer.append(attrDecl.simpleType.enumeration[i]);
}
buffer.append(')');
return fSymbolTable.addSymbol(buffer.toString());
}
case XMLSimpleType.TYPE_ID: {
return fIDSymbol;
}
case XMLSimpleType.TYPE_IDREF: {
return attrDecl.simpleType.list ? fIDREFSSymbol : fIDREFSymbol;
}
case XMLSimpleType.TYPE_NMTOKEN: {
return attrDecl.simpleType.list ? fNMTOKENSSymbol : fNMTOKENSymbol;
}
case XMLSimpleType.TYPE_NOTATION: {
return fNOTATIONSymbol;
}
}
return fCDATASymbol;
} // getAttributeTypeName(XMLAttributeDecl):String
/** intialization */
private void init() {
// symbols
fEMPTYSymbol = fSymbolTable.addSymbol("EMPTY");
fANYSymbol = fSymbolTable.addSymbol("ANY");
fMIXEDSymbol = fSymbolTable.addSymbol("MIXED");
fCHILDRENSymbol = fSymbolTable.addSymbol("CHILDREN");
fCDATASymbol = fSymbolTable.addSymbol("CDATA");
fIDSymbol = fSymbolTable.addSymbol("ID");
fIDREFSymbol = fSymbolTable.addSymbol("IDREF");
fIDREFSSymbol = fSymbolTable.addSymbol("IDREFS");
fENTITYSymbol = fSymbolTable.addSymbol("ENTITY");
fENTITIESSymbol = fSymbolTable.addSymbol("ENTITIES");
fNMTOKENSymbol = fSymbolTable.addSymbol("NMTOKEN");
fNMTOKENSSymbol = fSymbolTable.addSymbol("NMTOKENS");
fNOTATIONSymbol = fSymbolTable.addSymbol("NOTATION");
fENUMERATIONSymbol = fSymbolTable.addSymbol("ENUMERATION");
fIMPLIEDSymbol = fSymbolTable.addSymbol("#IMPLIED");
fREQUIREDSymbol = fSymbolTable.addSymbol("#REQUIRED");
fFIXEDSymbol = fSymbolTable.addSymbol("#FIXED");
fDATATYPESymbol = fSymbolTable.addSymbol("<<datatype>>");
// datatype validators
if (fValidation) {
try {
//REVISIT: datatypeRegistry + initialization of datatype
// why do we cast to ListDatatypeValidator?
((DatatypeValidatorFactoryImpl)fDatatypeValidatorFactory).initializeDTDRegistry();
fValID = (IDDatatypeValidator)((DatatypeValidatorFactoryImpl)fDatatypeValidatorFactory).getDatatypeValidator("ID" );
fValIDRef = (IDREFDatatypeValidator)((DatatypeValidatorFactoryImpl)fDatatypeValidatorFactory).getDatatypeValidator("IDREF" );
fValIDRefs = (ListDatatypeValidator)((DatatypeValidatorFactoryImpl)fDatatypeValidatorFactory).getDatatypeValidator("IDREFS" );
fValENTITY = (ENTITYDatatypeValidator)((DatatypeValidatorFactoryImpl)fDatatypeValidatorFactory).getDatatypeValidator("ENTITY" );
fValENTITIES = (ListDatatypeValidator)((DatatypeValidatorFactoryImpl)fDatatypeValidatorFactory).getDatatypeValidator("ENTITIES" );
fValNMTOKEN = ((DatatypeValidatorFactoryImpl)fDatatypeValidatorFactory).getDatatypeValidator("NMTOKEN");
fValNMTOKENS = ((DatatypeValidatorFactoryImpl)fDatatypeValidatorFactory).getDatatypeValidator("NMTOKENS");
fValNOTATION = (NOTATIONDatatypeValidator)((DatatypeValidatorFactoryImpl)fDatatypeValidatorFactory).getDatatypeValidator("NOTATION" );
}
catch (Exception e) {
// should never happen
e.printStackTrace(System.err);
}
//Initialize ID, IDREF, IDREFS validators
if (fTableOfIDs == null) {
fTableOfIDs = new Hashtable();//Initialize table of IDs
}
fTableOfIDs.clear();
fValID.initialize(fTableOfIDs);
fValIDRef.initialize(fTableOfIDs);
fValIDRefs.initialize(fTableOfIDs);
if (fNotationEnumVals == null) {
fNotationEnumVals = new Hashtable();
}
fNotationEnumVals.clear();
fTableOfIDAttributeNames = new Hashtable();
fTableOfNOTATIONAttributeNames = new Hashtable();
}
} // init()
/** ensure element stack capacity */
private void ensureStackCapacity ( int newElementDepth) {
if (newElementDepth == fElementQNamePartsStack.length) {
int[] newStack = new int[newElementDepth * 2];
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(fElementIndexStack, 0, newStack, 0, newElementDepth);
fElementIndexStack = newStack;
newStack = new int[newElementDepth * 2];
System.arraycopy(fContentSpecTypeStack, 0, newStack, 0, newElementDepth);
fContentSpecTypeStack = newStack;
}
} // ensureStackCapacity
//
// Protected methods
//
/** Handle element. */
protected void handleStartElement(QName element, XMLAttributes attributes) throws XNIException {
// REVISIT: Here are current assumptions about validation features
// given that XMLSchema validator is in the pipeline
//
// http://xml.org/sax/features/validation = true
// http://apache.org/xml/features/validation/schema = true
//
//[1] XML instance document only has reference to a DTD
// Outcome: report validation errors only against dtd.
//
//[2] XML instance document has only XML Schema grammars:
// Outcome: report validation errors only against schemas (no errors produced from DTD validator)
//
// [3] XML instance document has DTD and XML schemas:
// Outcome: validation errors reported against both grammars: DTD and schemas.
//
//
// if dynamic validation is on
// validate only against grammar we've found (depending on settings
// for schema feature)
//
// set wether we're performing validation
fPerformValidation = fValidation && (!fDynamicValidation || fSeenDoctypeDecl)
&& (fDTDValidation || fSeenDoctypeDecl);
// VC: Root Element Type
// see if the root element's name matches the one in DoctypeDecl
if (!fSeenRootElement) {
fSeenRootElement = true;
fValidationManager.getValidationState().setEntityState(fDTDGrammar);
fValidationManager.setGrammarFound(fSeenDoctypeDecl);
rootElementSpecified(element);
}
if (fDTDGrammar == null) {
if (!fPerformValidation) {
fCurrentElementIndex = -1;
fCurrentContentSpecType = -1;
fInElementContent = false;
}
if (fPerformValidation && !fSkipValidation) {
fSkipValidation = true;
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"MSG_GRAMMAR_NOT_FOUND",
new Object[]{ element.rawname},
XMLErrorReporter.SEVERITY_ERROR);
}
}
else {
// resolve the element
fCurrentElementIndex = fDTDGrammar.getElementDeclIndex(element, -1);
fCurrentContentSpecType = getContentSpecType(fCurrentElementIndex);
if (fCurrentElementIndex == -1 && fPerformValidation) {
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"MSG_ELEMENT_NOT_DECLARED",
new Object[]{ element.rawname},
XMLErrorReporter.SEVERITY_ERROR);
}
else {
// 0. insert default attributes
// 1. normalize the attributes
// 2. validate the attrivute list.
// TO DO:
//
addDTDDefaultAttrsAndValidate(fCurrentElementIndex, attributes);
}
}
// set element content state
fInElementContent = fCurrentContentSpecType == XMLElementDecl.TYPE_CHILDREN;
// increment the element depth, add this element's
// QName to its enclosing element 's children list
fElementDepth++;
if (fPerformValidation) {
// 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++;
}
// save current element information
fCurrentElement.setValues(element);
ensureStackCapacity(fElementDepth);
fElementQNamePartsStack[fElementDepth].setValues(fCurrentElement);
fElementIndexStack[fElementDepth] = fCurrentElementIndex;
fContentSpecTypeStack[fElementDepth] = fCurrentContentSpecType;
} // handleStartElement(QName,XMLAttributes,boolean)
/** Handle end element. */
protected void handleEndElement(QName element, Augmentations augs, boolean isEmpty)
throws XNIException {
// decrease element depth
fElementDepth--;
// validate
if (fPerformValidation) {
int elementIndex = fCurrentElementIndex;
if (elementIndex != -1 && fCurrentContentSpecType != -1) {
QName children[] = fElementChildren;
int childrenOffset = fElementChildrenOffsetStack[fElementDepth + 1] + 1;
int childrenLength = fElementChildrenLength - childrenOffset;
int result = checkContent(elementIndex,
children, childrenOffset, childrenLength);
if (result != -1) {
fDTDGrammar.getElementDecl(elementIndex, fTempElementDecl);
if (fTempElementDecl.type == XMLElementDecl.TYPE_EMPTY) {
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"MSG_CONTENT_INVALID",
new Object[]{ element.rawname, "EMPTY"},
XMLErrorReporter.SEVERITY_ERROR);
}
else {
String messageKey = result != childrenLength ?
"MSG_CONTENT_INVALID" : "MSG_CONTENT_INCOMPLETE";
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
messageKey,
new Object[]{ element.rawname,
fDTDGrammar.getContentSpecAsString(elementIndex)},
XMLErrorReporter.SEVERITY_ERROR);
}
}
}
fElementChildrenLength = fElementChildrenOffsetStack[fElementDepth + 1] + 1;
}
// call handlers
if (fDocumentHandler != null && !isEmpty) {
// NOTE: The binding of the element doesn't actually happen
// yet because the namespace binder does that. However,
// if it does it before this point, then the endPrefix-
// Mapping calls get made too soon! As long as the
// rawnames match, we know it'll have a good binding,
// so we can just use the current element. -Ac
fDocumentHandler.endElement(fCurrentElement, augs);
}
// now pop this element off the top of the element stack
if (fElementDepth < -1) {
throw new RuntimeException("FWK008 Element stack underflow");
}
if (fElementDepth < 0) {
fCurrentElement.clear();
fCurrentElementIndex = -1;
fCurrentContentSpecType = -1;
fInElementContent = false;
// TO DO : fix this
//
// 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 (fPerformValidation) {
try {
fValIDRef.validate();//Do final validation of IDREFS against IDs
fValIDRefs.validate();
}
catch (InvalidDatatypeValueException ex) {
String key = ex.getKeyIntoReporter();
fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,
key,
new Object[]{ ex.getMessage()},
XMLErrorReporter.SEVERITY_ERROR );
}
fTableOfIDs.clear();//Clear table of IDs
}
return;
}
// If Namespace enable then localName != rawName
fCurrentElement.setValues(fElementQNamePartsStack[fElementDepth]);
fCurrentElementIndex = fElementIndexStack[fElementDepth];
fCurrentContentSpecType = fContentSpecTypeStack[fElementDepth];
fInElementContent = (fCurrentContentSpecType == XMLElementDecl.TYPE_CHILDREN);
} // handleEndElement(QName,boolean)
/** Factory method for creating a DTD grammar. */
protected DTDGrammar createDTDGrammar() {
return new DTDGrammar(fSymbolTable);
} // createDTDGrammar():DTDGrammar
} // class XMLDTDValidator