blob: 27e7cda12ad5457cd04faad359691cc56617d489 [file] [log] [blame]
/*
* reserved comment block
* DO NOT REMOVE OR ALTER!
*/
/*
* Copyright 1999-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.sun.org.apache.xerces.internal.impl.dtd;
import com.sun.org.apache.xerces.internal.impl.Constants;
import com.sun.org.apache.xerces.internal.impl.RevalidationHandler;
import com.sun.org.apache.xerces.internal.impl.XMLEntityManager;
import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
import com.sun.org.apache.xerces.internal.impl.dtd.models.ContentModelValidator;
import com.sun.org.apache.xerces.internal.impl.dv.DTDDVFactory;
import com.sun.org.apache.xerces.internal.impl.dv.DatatypeValidator;
import com.sun.org.apache.xerces.internal.impl.dv.InvalidDatatypeValueException;
import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
import com.sun.org.apache.xerces.internal.impl.validation.ValidationManager;
import com.sun.org.apache.xerces.internal.impl.validation.ValidationState;
import com.sun.org.apache.xerces.internal.util.SymbolTable;
import com.sun.org.apache.xerces.internal.util.XMLChar;
import com.sun.org.apache.xerces.internal.util.XMLSymbols;
import com.sun.org.apache.xerces.internal.xni.Augmentations;
import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
import com.sun.org.apache.xerces.internal.xni.QName;
import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler;
import com.sun.org.apache.xerces.internal.xni.XMLLocator;
import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
import com.sun.org.apache.xerces.internal.xni.XMLString;
import com.sun.org.apache.xerces.internal.xni.XNIException;
import com.sun.org.apache.xerces.internal.xni.grammars.Grammar;
import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarDescription;
import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarPool;
import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentFilter;
import com.sun.org.apache.xerces.internal.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.
* <p> 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.
* <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>
*
* @xerces.internal
*
* @author Eric Ye, IBM
* @author Andy Clark, IBM
* @author Jeffrey Rodriguez IBM
* @author Neil Graham, IBM
*
* @version $Id: XMLDTDValidator.java,v 1.8 2010-11-01 04:39:42 joehw Exp $
*/
public class XMLDTDValidator
implements XMLComponent, XMLDocumentFilter, XMLDTDValidatorFilter, RevalidationHandler {
//
// Constants
//
/** Symbol: "&lt;&lt;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 <code>true</code>.
*/
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.
* <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
/** 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 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();
} // <init>()
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 = componentManager.getFeature(PARSER_SETTINGS, true);
if (!parser_settings){
// parser settings have not been changed
fValidationManager.addValidationState(fValidationState);
return;
}
// sax features
fNamespaces = componentManager.getFeature(NAMESPACES, true);
fValidation = componentManager.getFeature(VALIDATION, false);
fDTDValidation = !(componentManager.getFeature(Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_VALIDATION_FEATURE, false));
// Xerces features
fDynamicValidation = componentManager.getFeature(DYNAMIC_VALIDATION, false);
fBalanceSyntaxTrees = componentManager.getFeature(BALANCE_SYNTAX_TREES, false);
fWarnDuplicateAttdef = componentManager.getFeature(WARN_ON_DUPLICATE_ATTDEF, false);
fSchemaType = (String)componentManager.getProperty (Constants.JAXP_PROPERTY_PREFIX
+ Constants.SCHEMA_LANGUAGE, 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);
fGrammarPool= (XMLGrammarPool)componentManager.getProperty(GRAMMAR_POOL, 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.
* <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 (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.
* <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)
/**
* 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.
* <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 {
// 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.
* <p>
* <strong>Note:</strong> 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.
* <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 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.
* <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.
* @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.
* <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 ? XMLSymbols.fENTITIESSymbol : XMLSymbols.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 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) {
String value = fValidationState.checkIDRefID();
if (value != null) {
fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,
"MSG_ELEMENT_WITH_ID_REQUIRED",
new Object[]{value},
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