All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.xerces.impl.dtd.XMLDTDValidator Maven / Gradle / Ivy

There is a newer version: 6.2.20
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.xerces.impl.dtd;

import java.util.Iterator;

import org.apache.xerces.impl.Constants;
import org.apache.xerces.impl.RevalidationHandler;
import org.apache.xerces.impl.XMLEntityManager;
import org.apache.xerces.impl.XMLErrorReporter;
import org.apache.xerces.impl.dtd.models.ContentModelValidator;
import org.apache.xerces.impl.dv.DTDDVFactory;
import org.apache.xerces.impl.dv.DatatypeValidator;
import org.apache.xerces.impl.dv.InvalidDatatypeValueException;
import org.apache.xerces.impl.msg.XMLMessageFormatter;
import org.apache.xerces.impl.validation.ValidationManager;
import org.apache.xerces.impl.validation.ValidationState;
import org.apache.xerces.util.SymbolTable;
import org.apache.xerces.util.XMLChar;
import org.apache.xerces.util.XMLSymbols;
import org.apache.xerces.xni.Augmentations;
import org.apache.xerces.xni.NamespaceContext;
import org.apache.xerces.xni.QName;
import org.apache.xerces.xni.XMLAttributes;
import org.apache.xerces.xni.XMLDocumentHandler;
import org.apache.xerces.xni.XMLLocator;
import org.apache.xerces.xni.XMLResourceIdentifier;
import org.apache.xerces.xni.XMLString;
import org.apache.xerces.xni.XNIException;
import org.apache.xerces.xni.grammars.Grammar;
import org.apache.xerces.xni.grammars.XMLGrammarDescription;
import org.apache.xerces.xni.grammars.XMLGrammarPool;
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.XMLDocumentSource;

/**
 * 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.
 * 

Formerly, this component also handled DTD events and grammar construction. * To facilitate the development of a meaningful DTD grammar caching/preparsing * framework, this functionality has been moved into the XMLDTDLoader * class. Therefore, this class no longer implements the DTDFilter * or DTDContentModelFilter interfaces. *

* This component requires the following features and properties from the * component manager that uses it: *

    *
  • http://xml.org/sax/features/namespaces
  • *
  • http://xml.org/sax/features/validation
  • *
  • http://apache.org/xml/features/validation/dynamic
  • *
  • http://apache.org/xml/properties/internal/symbol-table
  • *
  • http://apache.org/xml/properties/internal/error-reporter
  • *
  • http://apache.org/xml/properties/internal/grammar-pool
  • *
  • http://apache.org/xml/properties/internal/datatype-validator-factory
  • *
* * @xerces.internal * * @author Eric Ye, IBM * @author Andy Clark, IBM * @author Jeffrey Rodriguez IBM * @author Neil Graham, IBM * * @version $Id: XMLDTDValidator.java 1380445 2012-09-04 04:43:34Z mrglavas $ */ public class XMLDTDValidator implements XMLComponent, XMLDocumentFilter, XMLDTDValidatorFilter, RevalidationHandler { // // Constants // /** Symbol: "<<datatypes>>". */ /** 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: balance syntax trees. */ protected static final String BALANCE_SYNTAX_TREES = Constants.XERCES_FEATURE_PREFIX + Constants.BALANCE_SYNTAX_TREES; /** Feature identifier: warn on duplicate attdef */ protected static final String WARN_ON_DUPLICATE_ATTDEF = Constants.XERCES_FEATURE_PREFIX + Constants.WARN_ON_DUPLICATE_ATTDEF_FEATURE; protected static final String PARSER_SETTINGS = Constants.XERCES_FEATURE_PREFIX + Constants.PARSER_SETTINGS; // 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.XMLGRAMMAR_POOL_PROPERTY; /** Property identifier: datatype validator factory. */ protected static final String DATATYPE_VALIDATOR_FACTORY = Constants.XERCES_PROPERTY_PREFIX + Constants.DATATYPE_VALIDATOR_FACTORY_PROPERTY; // property identifier: ValidationManager protected static final String VALIDATION_MANAGER = Constants.XERCES_PROPERTY_PREFIX + Constants.VALIDATION_MANAGER_PROPERTY; // recognized features and properties /** Recognized features. */ private static final String[] RECOGNIZED_FEATURES = { NAMESPACES, VALIDATION, DYNAMIC_VALIDATION, BALANCE_SYNTAX_TREES }; /** Feature defaults. */ private static final Boolean[] FEATURE_DEFAULTS = { null, null, Boolean.FALSE, Boolean.FALSE, }; /** Recognized properties. */ private static final String[] RECOGNIZED_PROPERTIES = { SYMBOL_TABLE, ERROR_REPORTER, GRAMMAR_POOL, DATATYPE_VALIDATOR_FACTORY, VALIDATION_MANAGER }; /** Property defaults. */ private static final Object[] PROPERTY_DEFAULTS = { null, null, null, null, null, }; // 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; // validation state protected final ValidationState fValidationState = new ValidationState(); // 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 true. */ protected boolean fDynamicValidation; /** Controls whether the DTD grammar produces balanced syntax trees. */ protected boolean fBalanceSyntaxTrees; /** warn on duplicate attribute definition, this feature works only when validation is true */ protected boolean fWarnDuplicateAttdef; // properties /** Symbol table. */ protected SymbolTable fSymbolTable; /** Error reporter. */ protected XMLErrorReporter fErrorReporter; // the grammar pool protected XMLGrammarPool fGrammarPool; /** Grammar bucket. */ protected DTDGrammarBucket fGrammarBucket; /* location of the document as passed in from startDocument call */ protected XMLLocator fDocLocation; /** Namespace support. */ protected NamespaceContext fNamespaceContext = null; /** Datatype validator factory. */ protected DTDDVFactory fDatatypeValidatorFactory; // handlers /** Document handler. */ protected XMLDocumentHandler fDocumentHandler; protected XMLDocumentSource fDocumentSource; // grammars /** DTD Grammar. */ protected DTDGrammar fDTDGrammar; // state /** True if seen DOCTYPE declaration. */ protected boolean fSeenDoctypeDecl = false; /** Perform validation. */ private boolean fPerformValidation; /** Schema type: None, DTD, Schema */ private String fSchemaType; // 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(); 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. *

* Note: This is much more efficient use of memory * than creating new arrays for each element depth. *

* Note: 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 fElementChildren array. * @see #fElementChildren */ private int[] fElementChildrenOffsetStack = new int[32]; /** Element depth. */ private int fElementDepth = -1; // validation states /** True if seen the root element. */ private boolean fSeenRootElement = false; /** True if inside of element content. */ private boolean fInElementContent = false; // temporary variables /** Temporary element declaration. */ private final XMLElementDecl fTempElementDecl = new XMLElementDecl(); /** Temporary atribute declaration. */ private final XMLAttributeDecl fTempAttDecl = new XMLAttributeDecl(); /** Temporary entity declaration. */ private final XMLEntityDecl fEntityDecl = new XMLEntityDecl(); /** Temporary qualified name. */ private final QName fTempQName = new QName(); /** Temporary string buffers. */ private final StringBuffer fBuffer = new StringBuffer(); // symbols: general // attribute validators /** Datatype validator: ID. */ protected DatatypeValidator fValID; /** Datatype validator: IDREF. */ protected DatatypeValidator fValIDRef; /** Datatype validator: IDREFS. */ protected DatatypeValidator fValIDRefs; /** Datatype validator: ENTITY. */ protected DatatypeValidator fValENTITY; /** Datatype validator: ENTITIES. */ protected DatatypeValidator fValENTITIES; /** Datatype validator: NMTOKEN. */ protected DatatypeValidator fValNMTOKEN; /** Datatype validator: NMTOKENS. */ protected DatatypeValidator fValNMTOKENS; /** Datatype validator: NOTATION. */ protected DatatypeValidator fValNOTATION; // to check for duplicate ID or ANNOTATION attribute declare in // ATTLIST, and misc VCs // // Constructors // /** Default constructor. */ public XMLDTDValidator() { // initialize data for (int i = 0; i < fElementQNamePartsStack.length; i++) { fElementQNamePartsStack[i] = new QName(); } fGrammarBucket = new DTDGrammarBucket(); } // () DTDGrammarBucket getGrammarBucket() { return fGrammarBucket; } // getGrammarBucket(): DTDGrammarBucket // // 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 fSeenRootElement = false; fInElementContent = false; fCurrentElementIndex = -1; fCurrentContentSpecType = -1; fRootElement.clear(); fValidationState.resetIDTables(); fGrammarBucket.clear(); fElementDepth = -1; fElementChildrenLength = 0; boolean parser_settings; try { parser_settings = componentManager.getFeature(PARSER_SETTINGS); } catch (XMLConfigurationException e){ parser_settings = true; } if (!parser_settings){ // parser settings have not been changed fValidationManager.addValidationState(fValidationState); return; } // 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(Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_VALIDATION_FEATURE)); } catch (XMLConfigurationException e) { // must be in a schema-less configuration! fDTDValidation = true; } // Xerces features try { fDynamicValidation = componentManager.getFeature(DYNAMIC_VALIDATION); } catch (XMLConfigurationException e) { fDynamicValidation = false; } try { fBalanceSyntaxTrees = componentManager.getFeature(BALANCE_SYNTAX_TREES); } catch (XMLConfigurationException e) { fBalanceSyntaxTrees = false; } try { fWarnDuplicateAttdef = componentManager.getFeature(WARN_ON_DUPLICATE_ATTDEF); } catch (XMLConfigurationException e) { fWarnDuplicateAttdef = false; } try { fSchemaType = (String)componentManager.getProperty (Constants.JAXP_PROPERTY_PREFIX + Constants.SCHEMA_LANGUAGE); } catch (XMLConfigurationException e){ fSchemaType = null; } fValidationManager= (ValidationManager)componentManager.getProperty(VALIDATION_MANAGER); fValidationManager.addValidationState(fValidationState); fValidationState.setUsingNamespaces(fNamespaces); // 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); try { fGrammarPool= (XMLGrammarPool)componentManager.getProperty(GRAMMAR_POOL); } catch (XMLConfigurationException e) { fGrammarPool = null; } fDatatypeValidatorFactory = (DTDDVFactory)componentManager.getProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.DATATYPE_VALIDATOR_FACTORY_PROPERTY); 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 (String[])(RECOGNIZED_FEATURES.clone()); } // 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. *

* Note: 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 (String[])(RECOGNIZED_PROPERTIES.clone()); } // 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. *

* Note: 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) /** * Returns the default state for a feature, or null if this * component does not want to report a default value for this * feature. * * @param featureId The feature identifier. * * @since Xerces 2.2.0 */ public Boolean getFeatureDefault(String featureId) { for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) { if (RECOGNIZED_FEATURES[i].equals(featureId)) { return FEATURE_DEFAULTS[i]; } } return null; } // getFeatureDefault(String):Boolean /** * Returns the default state for a property, or null if this * component does not want to report a default value for this * property. * * @param propertyId The property identifier. * * @since Xerces 2.2.0 */ public Object getPropertyDefault(String propertyId) { for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) { if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) { return PROPERTY_DEFAULTS[i]; } } return null; } // getPropertyDefault(String):Object // // XMLDocumentSource methods // /** Sets the document handler to receive information about the document. */ public void setDocumentHandler(XMLDocumentHandler documentHandler) { fDocumentHandler = documentHandler; } // setDocumentHandler(XMLDocumentHandler) /** Returns the document handler */ public XMLDocumentHandler getDocumentHandler() { return fDocumentHandler; } // getDocumentHandler(): XMLDocumentHandler // // XMLDocumentHandler methods // /** Sets the document source */ public void setDocumentSource(XMLDocumentSource source){ fDocumentSource = source; } // setDocumentSource /** Returns the document source */ public XMLDocumentSource getDocumentSource (){ return fDocumentSource; } // getDocumentSource /** * The start of the document. * * @param locator 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 namespaceContext * The namespace context in effect at the * start of this document. * This object represents the current context. * Implementors of this class are responsible * for copying the namespace bindings from the * the current context (and its parent contexts) * if that information is important. * @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, NamespaceContext namespaceContext, Augmentations augs) throws XNIException { // call handlers // get initial grammars if (fGrammarPool != null) { Grammar [] grammars = fGrammarPool.retrieveInitialGrammarSet(XMLGrammarDescription.XML_DTD); final int length = (grammars != null) ? grammars.length : 0; for (int i = 0; i < length; ++i) { fGrammarBucket.putGrammar((DTDGrammar)grammars[i]); } } fDocLocation = locator; fNamespaceContext = namespaceContext; if (fDocumentHandler != null) { fDocumentHandler.startDocument(locator, encoding, namespaceContext, 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 fGrammarBucket.setStandalone(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); // find or create grammar: String eid = null; try { eid = XMLEntityManager.expandSystemId(systemId, fDocLocation.getExpandedSystemId(), false); } catch (java.io.IOException e) { } XMLDTDDescription grammarDesc = new XMLDTDDescription(publicId, systemId, fDocLocation.getExpandedSystemId(), eid, rootElement); fDTDGrammar = fGrammarBucket.getGrammar(grammarDesc); if(fDTDGrammar == null) { // give grammar pool a chance... // // Do not bother checking the pool if no public or system identifier was provided. // Since so many different DTDs have roots in common, using only a root name as the // key may cause an unexpected grammar to be retrieved from the grammar pool. This scenario // would occur when an ExternalSubsetResolver has been queried and the // XMLInputSource returned contains an input stream but no external identifier. // This can never happen when the instance document specified a DOCTYPE. -- mrglavas if (fGrammarPool != null && (systemId != null || publicId != null)) { fDTDGrammar = (DTDGrammar)fGrammarPool.retrieveGrammar(grammarDesc); } } if(fDTDGrammar == null) { // we'll have to create it... if (!fBalanceSyntaxTrees) { fDTDGrammar = new DTDGrammar(fSymbolTable, grammarDesc); } else { fDTDGrammar = new BalancedDTDGrammar(fSymbolTable, grammarDesc); } } else { // we've found a cached one;so let's make sure not to read // any external subset! fValidationManager.setCachedDTD(true); } fGrammarBucket.setActiveGrammar(fDTDGrammar); // call handlers if (fDocumentHandler != null) { fDocumentHandler.doctypeDecl(rootElement, publicId, systemId, augs); } } // doctypeDecl(String,String,String, Augmentations) /** * 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, augs); // 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 { boolean removed = handleStartElement(element, attributes, augs); if (fDocumentHandler !=null) { fDocumentHandler.emptyElement(element, attributes, augs); } if (!removed) { 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 (!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 (fGrammarBucket.getStandalone() && 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(); } // For E15.2 if (augs != null && augs.getItem(Constants.CHAR_REF_PROBABLE_WS) == Boolean.TRUE) { fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, "MSG_CONTENT_INVALID_SPECIFIED", new Object[]{ fCurrentElement.rawname, fDTDGrammar.getContentSpecAsString(fElementDepth), "character reference"}, XMLErrorReporter.SEVERITY_ERROR); } } 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 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() /** * 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 { // fixes E15.1 if (fPerformValidation && fElementDepth >= 0 && fDTDGrammar != null) { fDTDGrammar.getElementDecl(fCurrentElementIndex, fTempElementDecl); if (fTempElementDecl.type == XMLElementDecl.TYPE_EMPTY) { fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, "MSG_CONTENT_INVALID_SPECIFIED", new Object[]{ fCurrentElement.rawname, "EMPTY", "comment"}, XMLErrorReporter.SEVERITY_ERROR); } } // 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. *

* Typically, a processing instruction's data will contain a series * of pseudo-attributes. These pseudo-attributes follow the form of * element attributes but are not 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 { // fixes E15.1 if (fPerformValidation && fElementDepth >= 0 && fDTDGrammar != null) { fDTDGrammar.getElementDecl(fCurrentElementIndex, fTempElementDecl); if (fTempElementDecl.type == XMLElementDecl.TYPE_EMPTY) { fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, "MSG_CONTENT_INVALID_SPECIFIED", new Object[]{ fCurrentElement.rawname, "EMPTY", "processing instruction"}, XMLErrorReporter.SEVERITY_ERROR); } } // call handlers if (fDocumentHandler != null) { fDocumentHandler.processingInstruction(target, data, augs); } } // processingInstruction(String,XMLString) /** * This method notifies the start of a general entity. *

* Note: This method is not called for entity references * appearing as part of attribute values. * * @param name The name of the general entity. * @param identifier The resource identifier. * @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 * * @exception XNIException Thrown by handler to signal an error. */ public void startGeneralEntity(String name, XMLResourceIdentifier identifier, String encoding, Augmentations augs) throws XNIException { if (fPerformValidation && fElementDepth >= 0 && fDTDGrammar != null) { fDTDGrammar.getElementDecl(fCurrentElementIndex, fTempElementDecl); // fixes E15.1 if (fTempElementDecl.type == XMLElementDecl.TYPE_EMPTY) { fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, "MSG_CONTENT_INVALID_SPECIFIED", new Object[]{ fCurrentElement.rawname, "EMPTY", "ENTITY"}, XMLErrorReporter.SEVERITY_ERROR); } if (fGrammarBucket.getStandalone()) { XMLDTDLoader.checkStandaloneEntityRef(name, fDTDGrammar, fEntityDecl, fErrorReporter); } } if (fDocumentHandler != null) { fDocumentHandler.startGeneralEntity(name, identifier, encoding, augs); } } /** * This method notifies the end of a general entity. *

* Note: 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 endGeneralEntity(String name, Augmentations augs) throws XNIException { // call handlers if (fDocumentHandler != null) { fDocumentHandler.endGeneralEntity(name, augs); } } // endEntity(String) /** * Notifies of the presence of a TextDecl line in an entity. If present, * this method will be called immediately following the startParameterEntity call. *

* Note: 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. * @param augs Additional information that may include infoset * augmentations. * * @throws XNIException Thrown by handler to signal an error. */ public void textDecl(String version, String encoding, Augmentations augs) throws XNIException { // call handlers if (fDocumentHandler != null) { fDocumentHandler.textDecl(version, encoding, augs); } } public final boolean hasGrammar(){ return (fDTDGrammar != null); } public final boolean validate(){ // Do validation if all of the following are true: // 1. The JAXP Schema Language property is not XML Schema // REVISIT: since only DTD and Schema are supported at this time, // such checking is sufficient. but if more schema types // are introduced in the future, we'll need to change it // to something like // (fSchemaType == null || fSchemaType == NS_XML_DTD) // 2. One of the following is true (validation features) // 2.1 Dynamic validation is off, and validation is on // 2.2 Dynamic validation is on, and DOCTYPE was seen // 3 Xerces schema validation feature is off, or DOCTYPE was seen. return (fSchemaType != Constants.NS_XMLSCHEMA) && (!fDynamicValidation && fValidation || fDynamicValidation && fSeenDoctypeDecl) && (fDTDValidation || fSeenDoctypeDecl); } //REVISIT:we can convert into functions.. adding default attribute values.. and one validating. /** Add default attributes and validate. */ protected void addDTDDefaultAttrsAndValidate(QName elementName, int elementIndex, XMLAttributes attributes) throws XNIException { // is there anything to do? if (elementIndex == -1 || fDTDGrammar == null) { return; } // // 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 == XMLSymbols.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 (fPerformValidation) { Object[] args = {elementName.localpart, attRawName}; fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, "MSG_REQUIRED_ATTRIBUTE_NOT_SPECIFIED", args, XMLErrorReporter.SEVERITY_ERROR); } } else if (attValue != null) { if (fPerformValidation && fGrammarBucket.getStandalone()) { if (fDTDGrammar.getAttributeDeclIsExternal(attlistIndex)) { Object[] args = { elementName.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 (fGrammarBucket.getStandalone()) { // 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 = { elementName.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); attributes.getAugmentations(i).putItem(Constants.ATTRIBUTE_DECLARED, Boolean.TRUE); boolean changedByNormalization = false; String oldValue = attributes.getValue(i); String attrValue = oldValue; if (attributes.isSpecified(i) && type != XMLSymbols.fCDATASymbol) { changedByNormalization = normalizeAttrValue(attributes, i); attrValue = attributes.getValue(i); if (fPerformValidation && fGrammarBucket.getStandalone() && 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 = {elementName.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(elementName, attrValue, fTempAttDecl); } } // for all attributes } // addDTDDefaultAttrsAndValidate(int,XMLAttrList) /** Checks entities in attribute values for standalone VC. */ protected 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. */ protected 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, fValidationState); } else { fValENTITY.validate(attValue, fValidationState); } } catch (InvalidDatatypeValueException ex) { fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, ex.getKey(), ex.getArgs(), 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, fValidationState); } catch (InvalidDatatypeValueException ex) { fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, ex.getKey(), ex.getArgs(), XMLErrorReporter.SEVERITY_ERROR ); } break; } case XMLSimpleType.TYPE_IDREF: { boolean isAlistAttribute = attributeDecl.simpleType.list;//Caveat - Save this information because invalidStandaloneAttDef try { if (isAlistAttribute) { fValIDRefs.validate(attValue, fValidationState); } else { fValIDRef.validate(attValue, fValidationState); } } catch (InvalidDatatypeValueException ex) { if (isAlistAttribute) { fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, "IDREFSInvalid", new Object[]{attValue}, XMLErrorReporter.SEVERITY_ERROR ); } else { fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, ex.getKey(), ex.getArgs(), 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, fValidationState); } else { fValNMTOKEN.validate(attValue, fValidationState); } } 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. */ protected 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; } // // Private methods // /** * 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); } /** Root element specified. */ private final 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. *

* 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. *

* This can also be called to do 'what if' testing of content models just to see * if they would be valid. *

* 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. *

* 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 ElementDeclPool of this * element. * @param childCount The number of entries in the children 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 ? XMLSymbols.fENTITIESSymbol : XMLSymbols.fENTITYSymbol; } case XMLSimpleType.TYPE_ENUMERATION: { StringBuffer buffer = new StringBuffer(); buffer.append('('); for (int i=0; i 0) { buffer.append('|'); } buffer.append(attrDecl.simpleType.enumeration[i]); } buffer.append(')'); return fSymbolTable.addSymbol(buffer.toString()); } case XMLSimpleType.TYPE_ID: { return XMLSymbols.fIDSymbol; } case XMLSimpleType.TYPE_IDREF: { return attrDecl.simpleType.list ? XMLSymbols.fIDREFSSymbol : XMLSymbols.fIDREFSymbol; } case XMLSimpleType.TYPE_NMTOKEN: { return attrDecl.simpleType.list ? XMLSymbols.fNMTOKENSSymbol : XMLSymbols.fNMTOKENSymbol; } case XMLSimpleType.TYPE_NOTATION: { return XMLSymbols.fNOTATIONSymbol; } } return XMLSymbols.fCDATASymbol; } // getAttributeTypeName(XMLAttributeDecl):String /** initialization */ protected void init() { // datatype validators if (fValidation || fDynamicValidation) { try { //REVISIT: datatypeRegistry + initialization of datatype // why do we cast to ListDatatypeValidator? fValID = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fIDSymbol); fValIDRef = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fIDREFSymbol); fValIDRefs = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fIDREFSSymbol); fValENTITY = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fENTITYSymbol); fValENTITIES = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fENTITIESSymbol); fValNMTOKEN = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fNMTOKENSymbol); fValNMTOKENS = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fNMTOKENSSymbol); fValNOTATION = fDatatypeValidatorFactory.getBuiltInDV(XMLSymbols.fNOTATIONSymbol); } catch (Exception e) { // should never happen e.printStackTrace(System.err); } } } // init() /** ensure element stack capacity */ private void ensureStackCapacity (int newElementDepth) { if (newElementDepth == fElementQNamePartsStack.length) { 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(); } } int[] 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 * @return true if validator is removed from the pipeline */ protected boolean handleStartElement(QName element, XMLAttributes attributes, Augmentations augs) throws XNIException { // VC: Root Element Type // see if the root element's name matches the one in DoctypeDecl if (!fSeenRootElement) { // 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: // [a] if schema language is not set outcome - validation errors reported against both grammars: DTD and schemas. // [b] if schema language is set to XML Schema - do not report validation errors // // if dynamic validation is on // validate only against grammar we've found (depending on settings // for schema feature) // // fPerformValidation = validate(); fSeenRootElement = true; fValidationManager.setEntityState(fDTDGrammar); fValidationManager.setGrammarFound(fSeenDoctypeDecl); rootElementSpecified(element); } if (fDTDGrammar == null) { if (!fPerformValidation) { fCurrentElementIndex = -1; fCurrentContentSpecType = -1; fInElementContent = false; } if (fPerformValidation) { fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, "MSG_GRAMMAR_NOT_FOUND", new Object[]{ element.rawname}, XMLErrorReporter.SEVERITY_ERROR); } // modify pipeline if (fDocumentSource !=null ) { fDocumentSource.setDocumentHandler(fDocumentHandler); if (fDocumentHandler != null) fDocumentHandler.setDocumentSource(fDocumentSource); return true; } } else { // resolve the element fCurrentElementIndex = fDTDGrammar.getElementDeclIndex(element); //changed here.. new function for getContentSpecType fCurrentContentSpecType = fDTDGrammar.getContentSpecType(fCurrentElementIndex); if (fCurrentContentSpecType == -1 && fPerformValidation) { fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, "MSG_ELEMENT_NOT_DECLARED", new Object[]{ element.rawname}, XMLErrorReporter.SEVERITY_ERROR); } // 0. insert default attributes // 1. normalize the attributes // 2. validate the attrivute list. // TO DO: //changed here.. also pass element name, addDTDDefaultAttrsAndValidate(element, 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; startNamespaceScope(element, attributes, augs); return false; } // handleStartElement(QName,XMLAttributes) protected void startNamespaceScope(QName element, XMLAttributes attributes, Augmentations augs){ } /** 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; } endNamespaceScope(fCurrentElement, augs, isEmpty); // 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) { Iterator invIdRefs = fValidationState.checkIDRefID(); if (invIdRefs != null) { while (invIdRefs.hasNext()) { fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN, "MSG_ELEMENT_WITH_ID_REQUIRED", new Object[]{invIdRefs.next()}, XMLErrorReporter.SEVERITY_ERROR ); } } } 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) protected void endNamespaceScope(QName element, Augmentations augs, boolean isEmpty){ // 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); } } // returns whether a character is space according to the // version of XML this validator supports. protected boolean isSpace(int c) { return XMLChar.isSpace(c); } // isSpace(int): boolean public boolean characterData(String data, Augmentations augs) { characters(new XMLString(data.toCharArray(), 0, data.length()), augs); return true; } } // class XMLDTDValidator





© 2015 - 2024 Weber Informatics LLC | Privacy Policy