--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jaxp/src/com/sun/org/apache/xerces/internal/impl/XMLDTDScannerImpl.java Thu Apr 12 08:38:26 2012 -0700
@@ -0,0 +1,2133 @@
+/*
+ * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*
+ * Copyright 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;
+import com.sun.xml.internal.stream.dtd.nonvalidating.DTDGrammar;
+import java.io.EOFException;
+import java.io.IOException;
+
+import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
+
+import com.sun.org.apache.xerces.internal.util.SymbolTable;
+import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl;
+import com.sun.org.apache.xerces.internal.util.XMLChar;
+import com.sun.org.apache.xerces.internal.util.XMLStringBuffer;
+
+import com.sun.org.apache.xerces.internal.xni.XMLDTDContentModelHandler;
+import com.sun.org.apache.xerces.internal.xni.XMLDTDHandler;
+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.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.XMLDTDScanner;
+import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
+import com.sun.org.apache.xerces.internal.xni.Augmentations;
+import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
+import com.sun.org.apache.xerces.internal.impl.XMLEntityHandler;
+import com.sun.org.apache.xerces.internal.impl.Constants;
+
+/**
+ * This class is responsible for scanning the declarations found
+ * in the internal and external subsets of a DTD in an XML document.
+ * The scanner acts as the sources for the DTD information which is
+ * communicated to the DTD handlers.
+ * <p>
+ * This component requires the following features and properties from the
+ * component manager that uses it:
+ * <ul>
+ * <li>http://xml.org/sax/features/validation</li>
+ * <li>http://apache.org/xml/features/scanner/notify-char-refs</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/entity-manager</li>
+ * </ul>
+ *
+ * @author Arnaud Le Hors, IBM
+ * @author Andy Clark, IBM
+ * @author Glenn Marcy, IBM
+ * @author Eric Ye, IBM
+ *
+ * @version $Id: XMLDTDScannerImpl.java,v 1.7 2007/09/26 12:52:40 ndw Exp $
+ */
+public class XMLDTDScannerImpl
+extends XMLScanner
+implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
+
+ //
+ // Constants
+ //
+
+ // scanner states
+
+ /** Scanner state: end of input. */
+ protected static final int SCANNER_STATE_END_OF_INPUT = 0;
+
+ /** Scanner state: text declaration. */
+ protected static final int SCANNER_STATE_TEXT_DECL = 1;
+
+ /** Scanner state: markup declaration. */
+ protected static final int SCANNER_STATE_MARKUP_DECL = 2;
+
+ // recognized features and properties
+
+ /** Recognized features. */
+ private static final String[] RECOGNIZED_FEATURES = {
+ VALIDATION,
+ NOTIFY_CHAR_REFS,
+ };
+
+ /** Feature defaults. */
+ private static final Boolean[] FEATURE_DEFAULTS = {
+ null,
+ Boolean.FALSE,
+ };
+
+ /** Recognized properties. */
+ private static final String[] RECOGNIZED_PROPERTIES = {
+ SYMBOL_TABLE,
+ ERROR_REPORTER,
+ ENTITY_MANAGER,
+ };
+
+ /** Property defaults. */
+ private static final Object[] PROPERTY_DEFAULTS = {
+ null,
+ null,
+ null,
+ };
+
+ // debugging
+
+ /** Debug scanner state. */
+ private static final boolean DEBUG_SCANNER_STATE = false;
+
+ //
+ // Data
+ //
+
+ // handlers
+
+ /** DTD handler. */
+ public XMLDTDHandler fDTDHandler = null;
+
+ /** DTD content model handler. */
+ protected XMLDTDContentModelHandler fDTDContentModelHandler;
+
+ // state
+
+ /** Scanner state. */
+ protected int fScannerState;
+
+ /** Standalone. */
+ protected boolean fStandalone;
+
+ /** Seen external DTD. */
+ protected boolean fSeenExternalDTD;
+
+ /** Seen external parameter entity. */
+ protected boolean fSeenExternalPE;
+
+ // private data
+
+ /** Start DTD called. */
+ private boolean fStartDTDCalled;
+
+ /** Default attribute */
+ private XMLAttributesImpl fAttributes = new XMLAttributesImpl();
+
+ /**
+ * Stack of content operators (either '|' or ',') in children
+ * content.
+ */
+ private int[] fContentStack = new int[5];
+
+ /** Size of content stack. */
+ private int fContentDepth;
+
+ /** Parameter entity stack to check well-formedness. */
+ private int[] fPEStack = new int[5];
+
+
+ /** Parameter entity stack to report start/end entity calls. */
+ private boolean[] fPEReport = new boolean[5];
+
+ /** Number of opened parameter entities. */
+ private int fPEDepth;
+
+ /** Markup depth. */
+ private int fMarkUpDepth;
+
+ /** Number of opened external entities. */
+ private int fExtEntityDepth;
+
+ /** Number of opened include sections. */
+ private int fIncludeSectDepth;
+
+ // temporary variables
+
+ /** Array of 3 strings. */
+ private String[] fStrings = new String[3];
+
+ /** String. */
+ private XMLString fString = new XMLString();
+
+ /** String buffer. */
+ private XMLStringBuffer fStringBuffer = new XMLStringBuffer();
+
+ /** String buffer. */
+ private XMLStringBuffer fStringBuffer2 = new XMLStringBuffer();
+
+ /** Literal text. */
+ private XMLString fLiteral = new XMLString();
+
+ /** Literal text. */
+ private XMLString fLiteral2 = new XMLString();
+
+ /** Enumeration values. */
+ private String[] fEnumeration = new String[5];
+
+ /** Enumeration values count. */
+ private int fEnumerationCount;
+
+ /** Ignore conditional section buffer. */
+ private XMLStringBuffer fIgnoreConditionalBuffer = new XMLStringBuffer(128);
+
+ /** Object contains grammar information for a non-validaing parser. */
+ DTDGrammar nvGrammarInfo = null;
+
+ boolean nonValidatingMode = false;
+ //
+ // Constructors
+ //
+
+ /** Default constructor. */
+ public XMLDTDScannerImpl() {
+ } // <init>()
+
+ /** Constructor for he use of non-XMLComponentManagers. */
+ public XMLDTDScannerImpl(SymbolTable symbolTable,
+ XMLErrorReporter errorReporter, XMLEntityManager entityManager) {
+ fSymbolTable = symbolTable;
+ fErrorReporter = errorReporter;
+ fEntityManager = entityManager;
+ entityManager.setProperty(SYMBOL_TABLE, fSymbolTable);
+ }
+
+ //
+ // XMLDTDScanner methods
+ //
+
+ /**
+ * Sets the input source.
+ *
+ * @param inputSource The input source or null.
+ *
+ * @throws IOException Thrown on i/o error.
+ */
+ public void setInputSource(XMLInputSource inputSource) throws IOException {
+ if (inputSource == null) {
+ // no system id was available
+ if (fDTDHandler != null) {
+ fDTDHandler.startDTD(null, null);
+ fDTDHandler.endDTD(null);
+ }
+ if (nonValidatingMode){
+ nvGrammarInfo.startDTD(null,null);
+ nvGrammarInfo.endDTD(null);
+ }
+ return;
+ }
+ fEntityManager.setEntityHandler(this);
+ fEntityManager.startDTDEntity(inputSource);
+ } // setInputSource(XMLInputSource)
+
+ /**
+ * Scans the external subset of the document.
+ *
+ * @param complete True if the scanner should scan the document
+ * completely, pushing all events to the registered
+ * document handler. A value of false indicates that
+ * that the scanner should only scan the next portion
+ * of the document and return. A scanner instance is
+ * permitted to completely scan a document if it does
+ * not support this "pull" scanning model.
+ *
+ * @return True if there is more to scan, false otherwise.
+ */
+ public boolean scanDTDExternalSubset(boolean complete)
+ throws IOException, XNIException {
+
+ fEntityManager.setEntityHandler(this);
+ if (fScannerState == SCANNER_STATE_TEXT_DECL) {
+ fSeenExternalDTD = true;
+ boolean textDecl = scanTextDecl();
+ if (fScannerState == SCANNER_STATE_END_OF_INPUT) {
+ return false;
+ }
+ else {
+ // next state is markup decls regardless of whether there
+ // is a TextDecl or not
+ setScannerState(SCANNER_STATE_MARKUP_DECL);
+ if (textDecl && !complete) {
+ return true;
+ }
+ }
+ }
+ // keep dispatching "events"
+ do {
+ if (!scanDecls(complete)) {
+ return false;
+ }
+ } while (complete);
+
+ // return that there is more to scan
+ return true;
+
+ } // scanDTDExternalSubset(boolean):boolean
+
+ /**
+ * Scans the internal subset of the document.
+ *
+ * @param complete True if the scanner should scan the document
+ * completely, pushing all events to the registered
+ * document handler. A value of false indicates that
+ * that the scanner should only scan the next portion
+ * of the document and return. A scanner instance is
+ * permitted to completely scan a document if it does
+ * not support this "pull" scanning model.
+ * @param standalone True if the document was specified as standalone.
+ * This value is important for verifying certain
+ * well-formedness constraints.
+ * @param hasExternalDTD True if the document has an external DTD.
+ * This allows the scanner to properly notify
+ * the handler of the end of the DTD in the
+ * absence of an external subset.
+ *
+ * @return True if there is more to scan, false otherwise.
+ */
+ public boolean scanDTDInternalSubset(boolean complete, boolean standalone,
+ boolean hasExternalSubset)
+ throws IOException, XNIException {
+ // reset entity scanner
+ //xxx:stax getText() is supposed to return only DTD internal subset
+ //shouldn't we record position here before we go ahead ??
+
+ fEntityScanner = (XMLEntityScanner)fEntityManager.getEntityScanner();
+ fEntityManager.setEntityHandler(this);
+ fStandalone = standalone;
+ //System.out.println("state"+fScannerState);
+ if (fScannerState == SCANNER_STATE_TEXT_DECL) {
+ // call handler
+ if (fDTDHandler != null) {
+ fDTDHandler.startDTD(fEntityScanner, null);
+ fStartDTDCalled = true;
+ }
+
+ if (nonValidatingMode){
+ fStartDTDCalled = true;
+ nvGrammarInfo.startDTD(fEntityScanner,null);
+ }
+ // set starting state for internal subset
+ setScannerState(SCANNER_STATE_MARKUP_DECL);
+ }
+ // keep dispatching "events"
+ do {
+ if (!scanDecls(complete)) {
+ // call handler
+ if (fDTDHandler != null && hasExternalSubset == false) {
+ fDTDHandler.endDTD(null);
+ }
+ if (nonValidatingMode && hasExternalSubset == false ){
+ nvGrammarInfo.endDTD(null);
+ }
+ // we're done, set starting state for external subset
+ setScannerState(SCANNER_STATE_TEXT_DECL);
+ return false;
+ }
+ } while (complete);
+
+ // return that there is more to scan
+ return true;
+
+ } // scanDTDInternalSubset(boolean,boolean,boolean):boolean
+
+ //
+ // XMLComponent methods
+ //
+
+ /**
+ * reset
+ *
+ * @param componentManager
+ */
+ public void reset(XMLComponentManager componentManager)
+ throws XMLConfigurationException {
+
+ super.reset(componentManager);
+ init();
+
+ } // reset(XMLComponentManager)
+
+ // this is made for something like XMLDTDLoader--XMLComponentManager-free operation...
+ public void reset() {
+ super.reset();
+ init();
+
+ }
+
+ public void reset(PropertyManager props) {
+ setPropertyManager(props);
+ super.reset(props);
+ init() ;
+ nonValidatingMode = true;
+ //Revisit : Create new grammar until we implement GrammarPool.
+ nvGrammarInfo = new DTDGrammar(fSymbolTable);
+ }
+ /**
+ * 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[]
+
+ /**
+ * 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[]
+
+ /**
+ * 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
+
+ //
+ // XMLDTDSource methods
+ //
+
+ /**
+ * setDTDHandler
+ *
+ * @param dtdHandler
+ */
+ public void setDTDHandler(XMLDTDHandler dtdHandler) {
+ fDTDHandler = dtdHandler;
+ } // setDTDHandler(XMLDTDHandler)
+
+ /**
+ * getDTDHandler
+ *
+ * @return the XMLDTDHandler
+ */
+ public XMLDTDHandler getDTDHandler() {
+ return fDTDHandler;
+ } // getDTDHandler(): XMLDTDHandler
+
+ //
+ // XMLDTDContentModelSource methods
+ //
+
+ /**
+ * setDTDContentModelHandler
+ *
+ * @param dtdContentModelHandler
+ */
+ public void setDTDContentModelHandler(XMLDTDContentModelHandler
+ dtdContentModelHandler) {
+ fDTDContentModelHandler = dtdContentModelHandler;
+ } // setDTDContentModelHandler
+
+ /**
+ * getDTDContentModelHandler
+ *
+ * @return XMLDTDContentModelHandler
+ */
+ public XMLDTDContentModelHandler getDTDContentModelHandler() {
+ return fDTDContentModelHandler ;
+ } // setDTDContentModelHandler
+
+ //
+ // XMLEntityHandler methods
+ //
+
+ /**
+ * This method notifies of the start of an entity. The DTD has the
+ * pseudo-name of "[dtd]" parameter entity names start with '%'; and
+ * general entities are just specified by their name.
+ *
+ * @param name The name of the 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
+ *
+ * @throws XNIException Thrown by handler to signal an error.
+ */
+ public void startEntity(String name,
+ XMLResourceIdentifier identifier,
+ String encoding, Augmentations augs) throws XNIException {
+
+ super.startEntity(name, identifier, encoding, augs);
+
+ boolean dtdEntity = name.equals("[dtd]");
+ if (dtdEntity) {
+ // call handler
+ if (fDTDHandler != null && !fStartDTDCalled ) {
+ fDTDHandler.startDTD(fEntityScanner, null);
+ }
+ if (fDTDHandler != null) {
+ fDTDHandler.startExternalSubset(identifier,null);
+ }
+ fEntityManager.startExternalSubset();
+ fEntityStore.startExternalSubset();
+ fExtEntityDepth++;
+ }
+ else if (name.charAt(0) == '%') {
+ pushPEStack(fMarkUpDepth, fReportEntity);
+ if (fEntityScanner.isExternal()) {
+ fExtEntityDepth++;
+ }
+ }
+
+ // call handler
+ if (fDTDHandler != null && !dtdEntity && fReportEntity) {
+ fDTDHandler.startParameterEntity(name, identifier, encoding, null);
+ }
+
+ } // startEntity(String,XMLResourceIdentifier,String)
+
+ /**
+ * This method notifies the end of an entity. The DTD has the pseudo-name
+ * of "[dtd]" parameter entity names start with '%'; and general entities
+ * are just specified by their name.
+ *
+ * @param name The name of the entity.
+ *
+ * @throws XNIException Thrown by handler to signal an error.
+ */
+ public void endEntity(String name, Augmentations augs)
+ throws XNIException, IOException {
+
+ super.endEntity(name, augs);
+
+ // if there is no data after the doctype
+ //
+ if (fScannerState == SCANNER_STATE_END_OF_INPUT)
+ return;
+
+ // Handle end of PE
+ boolean reportEntity = fReportEntity;
+ if (name.startsWith("%")) {
+ reportEntity = peekReportEntity();
+ // check well-formedness of the enity
+ int startMarkUpDepth = popPEStack();
+ // throw fatalError if this entity was incomplete and
+ // was a freestanding decl
+ if(startMarkUpDepth == 0 &&
+ startMarkUpDepth < fMarkUpDepth) {
+ fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
+ "ILL_FORMED_PARAMETER_ENTITY_WHEN_USED_IN_DECL",
+ new Object[]{ fEntityManager.fCurrentEntity.name},
+ XMLErrorReporter.SEVERITY_FATAL_ERROR);
+ }
+ if (startMarkUpDepth != fMarkUpDepth) {
+ reportEntity = false;
+ if (fValidation) {
+ // Proper nesting of parameter entities is a Validity Constraint
+ // and must not be enforced when validation is off
+ fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
+ "ImproperDeclarationNesting",
+ new Object[]{ name },
+ XMLErrorReporter.SEVERITY_ERROR);
+ }
+ }
+ if (fEntityScanner.isExternal()) {
+ fExtEntityDepth--;
+ }
+ }
+
+ // call handler
+ boolean dtdEntity = name.equals("[dtd]");
+ if (fDTDHandler != null && !dtdEntity && reportEntity) {
+ fDTDHandler.endParameterEntity(name, null);
+ }
+
+ // end DTD
+ if (dtdEntity) {
+ if (fIncludeSectDepth != 0) {
+ reportFatalError("IncludeSectUnterminated", null);
+ }
+ fScannerState = SCANNER_STATE_END_OF_INPUT;
+ // call handler
+ fEntityManager.endExternalSubset();
+ fEntityStore.endExternalSubset();
+
+ if (fDTDHandler != null) {
+ fDTDHandler.endExternalSubset(null);
+ fDTDHandler.endDTD(null);
+ }
+ fExtEntityDepth--;
+ }
+
+ //XML (Document Entity) is the last opened entity, however
+ //if for some reason DTD Scanner receives this callback
+ //there is something wrong (probably invalid XML), throw exception.
+ //or
+ //For standalone DTD loader, it might be the last opened entity
+ //and if this is the last opened entity and fMarkUpDepth != 0 or
+ //fIncludeSectDepth != 0 or fExtEntityDepth != 0 throw Exception
+ if (augs != null && Boolean.TRUE.equals(augs.getItem(Constants.LAST_ENTITY))
+ && ( fMarkUpDepth != 0 || fExtEntityDepth !=0 || fIncludeSectDepth != 0)){
+ throw new EOFException();
+ }
+
+ } // endEntity(String)
+
+ // helper methods
+
+ /**
+ * Sets the scanner state.
+ *
+ * @param state The new scanner state.
+ */
+ protected final void setScannerState(int state) {
+
+ fScannerState = state;
+ if (DEBUG_SCANNER_STATE) {
+ System.out.print("### setScannerState: ");
+ System.out.print(getScannerStateName(state));
+ //System.out.println();
+ }
+
+ } // setScannerState(int)
+
+ //
+ // Private methods
+ //
+
+ /** Returns the scanner state name. */
+ private static String getScannerStateName(int state) {
+
+ if (DEBUG_SCANNER_STATE) {
+ switch (state) {
+ case SCANNER_STATE_END_OF_INPUT: return "SCANNER_STATE_END_OF_INPUT";
+ case SCANNER_STATE_TEXT_DECL: return "SCANNER_STATE_TEXT_DECL";
+ case SCANNER_STATE_MARKUP_DECL: return "SCANNER_STATE_MARKUP_DECL";
+ }
+ }
+
+ return "??? ("+state+')';
+
+ } // getScannerStateName(int):String
+
+ protected final boolean scanningInternalSubset() {
+ return fExtEntityDepth == 0;
+ }
+
+ /**
+ * start a parameter entity dealing with the textdecl if there is any
+ *
+ * @param name The name of the parameter entity to start (without the '%')
+ * @param literal Whether this is happening within a literal
+ */
+ protected void startPE(String name, boolean literal)
+ throws IOException, XNIException {
+ int depth = fPEDepth;
+ String pName = "%"+name;
+ if (fValidation && !fEntityStore.isDeclaredEntity(pName)) {
+ fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,"EntityNotDeclared",
+ new Object[]{name}, XMLErrorReporter.SEVERITY_ERROR);
+ }
+ fEntityManager.startEntity(fSymbolTable.addSymbol(pName),
+ literal);
+ // if we actually got a new entity and it's external
+ // parse text decl if there is any
+ if (depth != fPEDepth && fEntityScanner.isExternal()) {
+ scanTextDecl();
+ }
+ }
+
+ /**
+ * Dispatch an XML "event".
+ *
+ * @param complete True if this method is intended to scan
+ * and dispatch as much as possible.
+ *
+ * @return True if a TextDecl was scanned.
+ *
+ * @throws IOException Thrown on i/o error.
+ * @throws XNIException Thrown on parse error.
+ *
+ */
+ protected final boolean scanTextDecl()
+ throws IOException, XNIException {
+
+ // scan XMLDecl
+ boolean textDecl = false;
+ if (fEntityScanner.skipString("<?xml")) {
+ fMarkUpDepth++;
+ // NOTE: special case where document starts with a PI
+ // whose name starts with "xml" (e.g. "xmlfoo")
+ if (isValidNameChar(fEntityScanner.peekChar())) {
+ fStringBuffer.clear();
+ fStringBuffer.append("xml");
+ while (isValidNameChar(fEntityScanner.peekChar())) {
+ fStringBuffer.append((char)fEntityScanner.scanChar());
+ }
+ String target =
+ fSymbolTable.addSymbol(fStringBuffer.ch,
+ fStringBuffer.offset,
+ fStringBuffer.length);
+ scanPIData(target, fString);
+ }
+
+ // standard Text declaration
+ else {
+ // pseudo-attribute values
+ String version = null;
+ String encoding = null;
+
+ scanXMLDeclOrTextDecl(true, fStrings);
+ textDecl = true;
+ fMarkUpDepth--;
+
+ version = fStrings[0];
+ encoding = fStrings[1];
+
+ fEntityScanner.setEncoding(encoding);
+
+ // call handler
+ if (fDTDHandler != null) {
+ fDTDHandler.textDecl(version, encoding, null);
+ }
+ }
+ }
+ fEntityManager.fCurrentEntity.mayReadChunks = true;
+
+ return textDecl;
+
+ } // scanTextDecl(boolean):boolean
+
+ /**
+ * Scans a processing data. This is needed to handle the situation
+ * where a document starts with a processing instruction whose
+ * target name <em>starts with</em> "xml". (e.g. xmlfoo)
+ *
+ * @param target The PI target
+ * @param data The string to fill in with the data
+ */
+ protected final void scanPIData(String target, XMLString data)
+ throws IOException, XNIException {
+ //Venu REVISIT
+ // super.scanPIData(target, data);
+ fMarkUpDepth--;
+
+ // call handler
+ if (fDTDHandler != null) {
+ fDTDHandler.processingInstruction(target, data, null);
+ }
+
+ } // scanPIData(String)
+
+ /**
+ * Scans a comment.
+ * <p>
+ * <pre>
+ * [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
+ * </pre>
+ * <p>
+ * <strong>Note:</strong> Called after scanning past '<!--'
+ */
+ protected final void scanComment() throws IOException, XNIException {
+
+ fReportEntity = false;
+ scanComment(fStringBuffer);
+ fMarkUpDepth--;
+
+ // call handler
+ if (fDTDHandler != null) {
+ fDTDHandler.comment(fStringBuffer, null);
+ }
+ fReportEntity = true;
+
+ } // scanComment()
+
+ /**
+ * Scans an element declaration
+ * <p>
+ * <pre>
+ * [45] elementdecl ::= '<!ELEMENT' S Name S contentspec S? '>'
+ * [46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | children
+ * </pre>
+ * <p>
+ * <strong>Note:</strong> Called after scanning past '<!ELEMENT'
+ */
+ protected final void scanElementDecl() throws IOException, XNIException {
+
+ // spaces
+ fReportEntity = false;
+ if (!skipSeparator(true, !scanningInternalSubset())) {
+ reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ELEMENTDECL",
+ null);
+ }
+
+ // element name
+ String name = fEntityScanner.scanName();
+ if (name == null) {
+ reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_ELEMENTDECL",
+ null);
+ }
+
+ // spaces
+ if (!skipSeparator(true, !scanningInternalSubset())) {
+ reportFatalError("MSG_SPACE_REQUIRED_BEFORE_CONTENTSPEC_IN_ELEMENTDECL",
+ new Object[]{name});
+ }
+
+ // content model
+ if (fDTDContentModelHandler != null) {
+ fDTDContentModelHandler.startContentModel(name, null);
+ }
+ String contentModel = null;
+ fReportEntity = true;
+ if (fEntityScanner.skipString("EMPTY")) {
+ contentModel = "EMPTY";
+ // call handler
+ if (fDTDContentModelHandler != null) {
+ fDTDContentModelHandler.empty(null);
+ }
+ }
+ else if (fEntityScanner.skipString("ANY")) {
+ contentModel = "ANY";
+ // call handler
+ if (fDTDContentModelHandler != null) {
+ fDTDContentModelHandler.any(null);
+ }
+ }
+ else {
+ if (!fEntityScanner.skipChar('(')) {
+ reportFatalError("MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN",
+ new Object[]{name});
+ }
+ if (fDTDContentModelHandler != null) {
+ fDTDContentModelHandler.startGroup(null);
+ }
+ fStringBuffer.clear();
+ fStringBuffer.append('(');
+ fMarkUpDepth++;
+ skipSeparator(false, !scanningInternalSubset());
+
+ // Mixed content model
+ if (fEntityScanner.skipString("#PCDATA")) {
+ scanMixed(name);
+ }
+ else { // children content
+ scanChildren(name);
+ }
+ contentModel = fStringBuffer.toString();
+ }
+
+ // call handler
+ if (fDTDContentModelHandler != null) {
+ fDTDContentModelHandler.endContentModel(null);
+ }
+
+ fReportEntity = false;
+ skipSeparator(false, !scanningInternalSubset());
+ // end
+ if (!fEntityScanner.skipChar('>')) {
+ reportFatalError("ElementDeclUnterminated", new Object[]{name});
+ }
+ fReportEntity = true;
+ fMarkUpDepth--;
+
+ // call handler
+ if (fDTDHandler != null) {
+ fDTDHandler.elementDecl(name, contentModel, null);
+ }
+ if (nonValidatingMode) nvGrammarInfo.elementDecl(name, contentModel, null);
+ } // scanElementDecl()
+
+ /**
+ * scan Mixed content model
+ * This assumes the content model has been parsed up to #PCDATA and
+ * can simply append to fStringBuffer.
+ * <pre>
+ * [51] Mixed ::= '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*'
+ * | '(' S? '#PCDATA' S? ')'
+ * </pre>
+ *
+ * @param elName The element type name this declaration is about.
+ *
+ * <strong>Note:</strong> Called after scanning past '(#PCDATA'.
+ */
+ private final void scanMixed(String elName)
+ throws IOException, XNIException {
+
+ String childName = null;
+
+ fStringBuffer.append("#PCDATA");
+ // call handler
+ if (fDTDContentModelHandler != null) {
+ fDTDContentModelHandler.pcdata(null);
+ }
+ skipSeparator(false, !scanningInternalSubset());
+ while (fEntityScanner.skipChar('|')) {
+ fStringBuffer.append('|');
+ // call handler
+ if (fDTDContentModelHandler != null) {
+ fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_CHOICE,
+ null);
+ }
+ skipSeparator(false, !scanningInternalSubset());
+
+ childName = fEntityScanner.scanName();
+ if (childName == null) {
+ reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_MIXED_CONTENT",
+ new Object[]{elName});
+ }
+ fStringBuffer.append(childName);
+ // call handler
+ if (fDTDContentModelHandler != null) {
+ fDTDContentModelHandler.element(childName, null);
+ }
+ skipSeparator(false, !scanningInternalSubset());
+ }
+ // The following check must be done in a single call (as opposed to one
+ // for ')' and then one for '*') to guarantee that callbacks are
+ // properly nested. We do not want to trigger endEntity too early in
+ // case we cross the boundary of an entity between the two characters.
+ if (fEntityScanner.skipString(")*")) {
+ fStringBuffer.append(")*");
+ // call handler
+ if (fDTDContentModelHandler != null) {
+ fDTDContentModelHandler.endGroup(null);
+ fDTDContentModelHandler.occurrence(XMLDTDContentModelHandler.OCCURS_ZERO_OR_MORE,
+ null);
+ }
+ }
+ else if (childName != null) {
+ reportFatalError("MixedContentUnterminated",
+ new Object[]{elName});
+ }
+ else if (fEntityScanner.skipChar(')')){
+ fStringBuffer.append(')');
+ // call handler
+ if (fDTDContentModelHandler != null) {
+ fDTDContentModelHandler.endGroup(null);
+ }
+ }
+ else {
+ reportFatalError("MSG_CLOSE_PAREN_REQUIRED_IN_CHILDREN",
+ new Object[]{elName});
+ }
+ fMarkUpDepth--;
+ // we are done
+ }
+
+ /**
+ * scan children content model
+ * This assumes it can simply append to fStringBuffer.
+ * <pre>
+ * [47] children ::= (choice | seq) ('?' | '*' | '+')?
+ * [48] cp ::= (Name | choice | seq) ('?' | '*' | '+')?
+ * [49] choice ::= '(' S? cp ( S? '|' S? cp )+ S? ')'
+ * [50] seq ::= '(' S? cp ( S? ',' S? cp )* S? ')'
+ * </pre>
+ *
+ * @param elName The element type name this declaration is about.
+ *
+ * <strong>Note:</strong> Called after scanning past the first open
+ * paranthesis.
+ */
+ private final void scanChildren(String elName)
+ throws IOException, XNIException {
+
+ fContentDepth = 0;
+ pushContentStack(0);
+ int currentOp = 0;
+ int c;
+ while (true) {
+ if (fEntityScanner.skipChar('(')) {
+ fMarkUpDepth++;
+ fStringBuffer.append('(');
+ // call handler
+ if (fDTDContentModelHandler != null) {
+ fDTDContentModelHandler.startGroup(null);
+ }
+ // push current op on stack and reset it
+ pushContentStack(currentOp);
+ currentOp = 0;
+ skipSeparator(false, !scanningInternalSubset());
+ continue;
+ }
+ skipSeparator(false, !scanningInternalSubset());
+ String childName = fEntityScanner.scanName();
+ if (childName == null) {
+ reportFatalError("MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN",
+ new Object[]{elName});
+ return;
+ }
+ // call handler
+ if (fDTDContentModelHandler != null) {
+ fDTDContentModelHandler.element(childName, null);
+ }
+ fStringBuffer.append(childName);
+ c = fEntityScanner.peekChar();
+ if (c == '?' || c == '*' || c == '+') {
+ // call handler
+ if (fDTDContentModelHandler != null) {
+ short oc;
+ if (c == '?') {
+ oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_ONE;
+ }
+ else if (c == '*') {
+ oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_MORE;
+ }
+ else {
+ oc = XMLDTDContentModelHandler.OCCURS_ONE_OR_MORE;
+ }
+ fDTDContentModelHandler.occurrence(oc, null);
+ }
+ fEntityScanner.scanChar();
+ fStringBuffer.append((char)c);
+ }
+ while (true) {
+ skipSeparator(false, !scanningInternalSubset());
+ c = fEntityScanner.peekChar();
+ if (c == ',' && currentOp != '|') {
+ currentOp = c;
+ // call handler
+ if (fDTDContentModelHandler != null) {
+ fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_SEQUENCE,
+ null);
+ }
+ fEntityScanner.scanChar();
+ fStringBuffer.append(',');
+ break;
+ }
+ else if (c == '|' && currentOp != ',') {
+ currentOp = c;
+ // call handler
+ if (fDTDContentModelHandler != null) {
+ fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_CHOICE,
+ null);
+ }
+ fEntityScanner.scanChar();
+ fStringBuffer.append('|');
+ break;
+ }
+ else if (c != ')') {
+ reportFatalError("MSG_CLOSE_PAREN_REQUIRED_IN_CHILDREN",
+ new Object[]{elName});
+ }
+ // call handler
+ if (fDTDContentModelHandler != null) {
+ fDTDContentModelHandler.endGroup(null);
+ }
+ // restore previous op
+ currentOp = popContentStack();
+ short oc;
+ // The following checks must be done in a single call (as
+ // opposed to one for ')' and then one for '?', '*', and '+')
+ // to guarantee that callbacks are properly nested. We do not
+ // want to trigger endEntity too early in case we cross the
+ // boundary of an entity between the two characters.
+ if (fEntityScanner.skipString(")?")) {
+ fStringBuffer.append(")?");
+ // call handler
+ if (fDTDContentModelHandler != null) {
+ oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_ONE;
+ fDTDContentModelHandler.occurrence(oc, null);
+ }
+ }
+ else if (fEntityScanner.skipString(")+")) {
+ fStringBuffer.append(")+");
+ // call handler
+ if (fDTDContentModelHandler != null) {
+ oc = XMLDTDContentModelHandler.OCCURS_ONE_OR_MORE;
+ fDTDContentModelHandler.occurrence(oc, null);
+ }
+ }
+ else if (fEntityScanner.skipString(")*")) {
+ fStringBuffer.append(")*");
+ // call handler
+ if (fDTDContentModelHandler != null) {
+ oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_MORE;
+ fDTDContentModelHandler.occurrence(oc, null);
+ }
+ }
+ else {
+ // no occurrence specified
+ fEntityScanner.scanChar();
+ fStringBuffer.append(')');
+ }
+ fMarkUpDepth--;
+ if (fContentDepth == 0) {
+ return;
+ }
+ }
+ skipSeparator(false, !scanningInternalSubset());
+ }
+ }
+
+ /**
+ * Scans an attlist declaration
+ * <p>
+ * <pre>
+ * [52] AttlistDecl ::= '<!ATTLIST' S Name AttDef* S? '>'
+ * [53] AttDef ::= S Name S AttType S DefaultDecl
+ * </pre>
+ * <p>
+ * <strong>Note:</strong> Called after scanning past '<!ATTLIST'
+ */
+ protected final void scanAttlistDecl() throws IOException, XNIException {
+
+ // spaces
+ fReportEntity = false;
+ if (!skipSeparator(true, !scanningInternalSubset())) {
+ reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ATTLISTDECL",
+ null);
+ }
+
+ // element name
+ String elName = fEntityScanner.scanName();
+ if (elName == null) {
+ reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_ATTLISTDECL",
+ null);
+ }
+
+ // call handler
+ if (fDTDHandler != null) {
+ fDTDHandler.startAttlist(elName, null);
+ }
+
+ // spaces
+ if (!skipSeparator(true, !scanningInternalSubset())) {
+ // no space, is it the end yet?
+ if (fEntityScanner.skipChar('>')) {
+ // yes, stop here
+ // call handler
+ if (fDTDHandler != null) {
+ fDTDHandler.endAttlist(null);
+ }
+ fMarkUpDepth--;
+ return;
+ }
+ else {
+ reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ATTRIBUTE_NAME_IN_ATTDEF",
+ new Object[]{elName});
+ }
+ }
+
+ // definitions
+ while (!fEntityScanner.skipChar('>')) {
+ String name = fEntityScanner.scanName();
+ if (name == null) {
+ reportFatalError("AttNameRequiredInAttDef",
+ new Object[]{elName});
+ }
+ // spaces
+ if (!skipSeparator(true, !scanningInternalSubset())) {
+ reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ATTTYPE_IN_ATTDEF",
+ new Object[]{elName, name});
+ }
+ // type
+ String type = scanAttType(elName, name);
+
+ // spaces
+ if (!skipSeparator(true, !scanningInternalSubset())) {
+ reportFatalError("MSG_SPACE_REQUIRED_BEFORE_DEFAULTDECL_IN_ATTDEF",
+ new Object[]{elName, name});
+ }
+
+ // default decl
+ String defaultType = scanAttDefaultDecl(elName, name,
+ type,
+ fLiteral, fLiteral2);
+ // REVISIT: Should we do anything with the non-normalized
+ // default attribute value? -Ac
+ // yes--according to bug 5073. - neilg
+ String[] enumr = null;
+ if( fDTDHandler != null || nonValidatingMode){
+ if (fEnumerationCount != 0) {
+ enumr = new String[fEnumerationCount];
+ System.arraycopy(fEnumeration, 0, enumr,
+ 0, fEnumerationCount);
+ }
+ }
+ // call handler
+ // Determine whether the default value to be passed should be null.
+ // REVISIT: should probably check whether fLiteral.ch is null instead. LM.
+ if (defaultType!=null && (defaultType.equals("#REQUIRED") ||
+ defaultType.equals("#IMPLIED"))) {
+ if (fDTDHandler != null){
+ fDTDHandler.attributeDecl(elName, name, type, enumr,
+ defaultType, null, null, null);
+ }
+ if(nonValidatingMode){
+ nvGrammarInfo.attributeDecl(elName, name, type, enumr,
+ defaultType, null, null, null);
+
+ }
+ }
+ else {
+ if (fDTDHandler != null){
+ fDTDHandler.attributeDecl(elName, name, type, enumr,
+ defaultType, fLiteral, fLiteral2, null);
+ }
+ if(nonValidatingMode){
+ nvGrammarInfo.attributeDecl(elName, name, type, enumr,
+ defaultType, fLiteral, fLiteral2, null);
+ }
+ }
+ skipSeparator(false, !scanningInternalSubset());
+ }
+
+ // call handler
+ if (fDTDHandler != null) {
+ fDTDHandler.endAttlist(null);
+ }
+ fMarkUpDepth--;
+ fReportEntity = true;
+
+ } // scanAttlistDecl()
+
+ /**
+ * Scans an attribute type definition
+ * <p>
+ * <pre>
+ * [54] AttType ::= StringType | TokenizedType | EnumeratedType
+ * [55] StringType ::= 'CDATA'
+ * [56] TokenizedType ::= 'ID'
+ * | 'IDREF'
+ * | 'IDREFS'
+ * | 'ENTITY'
+ * | 'ENTITIES'
+ * | 'NMTOKEN'
+ * | 'NMTOKENS'
+ * [57] EnumeratedType ::= NotationType | Enumeration
+ * [58] NotationType ::= 'NOTATION' S '(' S? Name (S? '|' S? Name)* S? ')'
+ * [59] Enumeration ::= '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')'
+ * </pre>
+ * <p>
+ * <strong>Note:</strong> Called after scanning past '<!ATTLIST'
+ *
+ * @param elName The element type name this declaration is about.
+ * @param atName The attribute name this declaration is about.
+ */
+ private final String scanAttType(String elName, String atName)
+ throws IOException, XNIException {
+
+ String type = null;
+ fEnumerationCount = 0;
+ /*
+ * Watchout: the order here is important: when a string happens to
+ * be a substring of another string, the longer one needs to be
+ * looked for first!!
+ */
+ if (fEntityScanner.skipString("CDATA")) {
+ type = "CDATA";
+ }
+ else if (fEntityScanner.skipString("IDREFS")) {
+ type = "IDREFS";
+ }
+ else if (fEntityScanner.skipString("IDREF")) {
+ type = "IDREF";
+ }
+ else if (fEntityScanner.skipString("ID")) {
+ type = "ID";
+ }
+ else if (fEntityScanner.skipString("ENTITY")) {
+ type = "ENTITY";
+ }
+ else if (fEntityScanner.skipString("ENTITIES")) {
+ type = "ENTITIES";
+ }
+ else if (fEntityScanner.skipString("NMTOKENS")) {
+ type = "NMTOKENS";
+ }
+ else if (fEntityScanner.skipString("NMTOKEN")) {
+ type = "NMTOKEN";
+ }
+ else if (fEntityScanner.skipString("NOTATION")) {
+ type = "NOTATION";
+ // spaces
+ if (!skipSeparator(true, !scanningInternalSubset())) {
+ reportFatalError("MSG_SPACE_REQUIRED_AFTER_NOTATION_IN_NOTATIONTYPE",
+ new Object[]{elName, atName});
+ }
+ // open paren
+ int c = fEntityScanner.scanChar();
+ if (c != '(') {
+ reportFatalError("MSG_OPEN_PAREN_REQUIRED_IN_NOTATIONTYPE",
+ new Object[]{elName, atName});
+ }
+ fMarkUpDepth++;
+ do {
+ skipSeparator(false, !scanningInternalSubset());
+ String aName = fEntityScanner.scanName();
+ if (aName == null) {
+ reportFatalError("MSG_NAME_REQUIRED_IN_NOTATIONTYPE",
+ new Object[]{elName, atName});
+ }
+ ensureEnumerationSize(fEnumerationCount + 1);
+ fEnumeration[fEnumerationCount++] = aName;
+ skipSeparator(false, !scanningInternalSubset());
+ c = fEntityScanner.scanChar();
+ } while (c == '|');
+ if (c != ')') {
+ reportFatalError("NotationTypeUnterminated",
+ new Object[]{elName, atName});
+ }
+ fMarkUpDepth--;
+ }
+ else { // Enumeration
+ type = "ENUMERATION";
+ // open paren
+ int c = fEntityScanner.scanChar();
+ if (c != '(') {
+ // "OPEN_PAREN_REQUIRED_BEFORE_ENUMERATION_IN_ATTRDECL",
+ reportFatalError("AttTypeRequiredInAttDef",
+ new Object[]{elName, atName});
+ }
+ fMarkUpDepth++;
+ do {
+ skipSeparator(false, !scanningInternalSubset());
+ String token = fEntityScanner.scanNmtoken();
+ if (token == null) {
+ reportFatalError("MSG_NMTOKEN_REQUIRED_IN_ENUMERATION",
+ new Object[]{elName, atName});
+ }
+ ensureEnumerationSize(fEnumerationCount + 1);
+ fEnumeration[fEnumerationCount++] = token;
+ skipSeparator(false, !scanningInternalSubset());
+ c = fEntityScanner.scanChar();
+ } while (c == '|');
+ if (c != ')') {
+ reportFatalError("EnumerationUnterminated",
+ new Object[]{elName, atName});
+ }
+ fMarkUpDepth--;
+ }
+ return type;
+
+ } // scanAttType():String
+
+
+ /**
+ * Scans an attribute default declaration
+ * <p>
+ * <pre>
+ * [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue)
+ * </pre>
+ *
+ * @param name The name of the attribute being scanned.
+ * @param defaultVal The string to fill in with the default value.
+ */
+ protected final String scanAttDefaultDecl(String elName, String atName,
+ String type,
+ XMLString defaultVal,
+ XMLString nonNormalizedDefaultVal)
+ throws IOException, XNIException {
+
+ String defaultType = null;
+ fString.clear();
+ defaultVal.clear();
+ if (fEntityScanner.skipString("#REQUIRED")) {
+ defaultType = "#REQUIRED";
+ }
+ else if (fEntityScanner.skipString("#IMPLIED")) {
+ defaultType = "#IMPLIED";
+ }
+ else {
+ if (fEntityScanner.skipString("#FIXED")) {
+ defaultType = "#FIXED";
+ // spaces
+ if (!skipSeparator(true, !scanningInternalSubset())) {
+ reportFatalError("MSG_SPACE_REQUIRED_AFTER_FIXED_IN_DEFAULTDECL",
+ new Object[]{elName, atName});
+ }
+ }
+ // AttValue
+ boolean isVC = !fStandalone && (fSeenExternalDTD || fSeenExternalPE) ;
+ scanAttributeValue(defaultVal, nonNormalizedDefaultVal, atName,
+ fAttributes, 0, isVC);
+ }
+ return defaultType;
+
+ } // ScanAttDefaultDecl
+
+ /**
+ * Scans an entity declaration
+ * <p>
+ * <pre>
+ * [70] EntityDecl ::= GEDecl | PEDecl
+ * [71] GEDecl ::= '<!ENTITY' S Name S EntityDef S? '>'
+ * [72] PEDecl ::= '<!ENTITY' S '%' S Name S PEDef S? '>'
+ * [73] EntityDef ::= EntityValue | (ExternalID NDataDecl?)
+ * [74] PEDef ::= EntityValue | ExternalID
+ * [75] ExternalID ::= 'SYSTEM' S SystemLiteral
+ * | 'PUBLIC' S PubidLiteral S SystemLiteral
+ * [76] NDataDecl ::= S 'NDATA' S Name
+ * </pre>
+ * <p>
+ * <strong>Note:</strong> Called after scanning past '<!ENTITY'
+ */
+ private final void scanEntityDecl() throws IOException, XNIException {
+
+ boolean isPEDecl = false;
+ boolean sawPERef = false;
+ fReportEntity = false;
+ if (fEntityScanner.skipSpaces()) {
+ if (!fEntityScanner.skipChar('%')) {
+ isPEDecl = false; // <!ENTITY x "x">
+ }
+ else if (skipSeparator(true, !scanningInternalSubset())) {
+ // <!ENTITY % x "x">
+ isPEDecl = true;
+ }
+ else if (scanningInternalSubset()) {
+ reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL",
+ null);
+ isPEDecl = true;
+ }
+ else if (fEntityScanner.peekChar() == '%') {
+ // <!ENTITY %%x; "x"> is legal
+ skipSeparator(false, !scanningInternalSubset());
+ isPEDecl = true;
+ }
+ else {
+ sawPERef = true;
+ }
+ }
+ else if (scanningInternalSubset() || !fEntityScanner.skipChar('%')) {
+ // <!ENTITY[^ ]...> or <!ENTITY[^ %]...>
+ reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL",
+ null);
+ isPEDecl = false;
+ }
+ else if (fEntityScanner.skipSpaces()) {
+ // <!ENTITY% ...>
+ reportFatalError("MSG_SPACE_REQUIRED_BEFORE_PERCENT_IN_PEDECL",
+ null);
+ isPEDecl = false;
+ }
+ else {
+ sawPERef = true;
+ }
+ if (sawPERef) {
+ while (true) {
+ String peName = fEntityScanner.scanName();
+ if (peName == null) {
+ reportFatalError("NameRequiredInPEReference", null);
+ }
+ else if (!fEntityScanner.skipChar(';')) {
+ reportFatalError("SemicolonRequiredInPEReference",
+ new Object[]{peName});
+ }
+ else {
+ startPE(peName, false);
+ }
+ fEntityScanner.skipSpaces();
+ if (!fEntityScanner.skipChar('%'))
+ break;
+ if (!isPEDecl) {
+ if (skipSeparator(true, !scanningInternalSubset())) {
+ isPEDecl = true;
+ break;
+ }
+ isPEDecl = fEntityScanner.skipChar('%');
+ }
+ }
+ }
+
+ // name
+ String name = fEntityScanner.scanName();
+ if (name == null) {
+ reportFatalError("MSG_ENTITY_NAME_REQUIRED_IN_ENTITYDECL", null);
+ }
+
+ // spaces
+ if (!skipSeparator(true, !scanningInternalSubset())) {
+ reportFatalError("MSG_SPACE_REQUIRED_AFTER_ENTITY_NAME_IN_ENTITYDECL",
+ new Object[]{name});
+ }
+
+ // external id
+ scanExternalID(fStrings, false);
+ String systemId = fStrings[0];
+ String publicId = fStrings[1];
+
+ if (isPEDecl && systemId != null) {
+ fSeenExternalPE = true;
+ }
+
+ String notation = null;
+ // NDATA
+ boolean sawSpace = skipSeparator(true, !scanningInternalSubset());
+ if (!isPEDecl && fEntityScanner.skipString("NDATA")) {
+ // check whether there was space before NDATA
+ if (!sawSpace) {
+ reportFatalError("MSG_SPACE_REQUIRED_BEFORE_NDATA_IN_UNPARSED_ENTITYDECL",
+ new Object[]{name});
+ }
+
+ // spaces
+ if (!skipSeparator(true, !scanningInternalSubset())) {
+ reportFatalError("MSG_SPACE_REQUIRED_BEFORE_NOTATION_NAME_IN_UNPARSED_ENTITYDECL",
+ new Object[]{name});
+ }
+ notation = fEntityScanner.scanName();
+ if (notation == null) {
+ reportFatalError("MSG_NOTATION_NAME_REQUIRED_FOR_UNPARSED_ENTITYDECL",
+ new Object[]{name});
+ }
+ }
+
+ // internal entity
+ if (systemId == null) {
+ scanEntityValue(fLiteral, fLiteral2);
+ // since we need it's value anyway, let's snag it so it doesn't get corrupted
+ // if a new load takes place before we store the entity values
+ fStringBuffer.clear();
+ fStringBuffer2.clear();
+ fStringBuffer.append(fLiteral.ch, fLiteral.offset, fLiteral.length);
+ fStringBuffer2.append(fLiteral2.ch, fLiteral2.offset, fLiteral2.length);
+ }
+
+ // skip possible trailing space
+ skipSeparator(false, !scanningInternalSubset());
+
+ // end
+ if (!fEntityScanner.skipChar('>')) {
+ reportFatalError("EntityDeclUnterminated", new Object[]{name});
+ }
+ fMarkUpDepth--;
+
+ // register entity and make callback
+ if (isPEDecl) {
+ name = "%" + name;
+ }
+ if (systemId != null) {
+ String baseSystemId = fEntityScanner.getBaseSystemId();
+ if (notation != null) {
+ fEntityStore.addUnparsedEntity(name, publicId, systemId, baseSystemId, notation);
+ }
+ else {
+ fEntityStore.addExternalEntity(name, publicId, systemId,
+ baseSystemId);
+ }
+ if (fDTDHandler != null) {
+ //Venu Revisit : why false has been removed in expandSYstem
+ fResourceIdentifier.setValues(publicId, systemId, baseSystemId, XMLEntityManager.expandSystemId(systemId, baseSystemId ));
+
+ if (notation != null) {
+ fDTDHandler.unparsedEntityDecl(name, fResourceIdentifier,
+ notation, null);
+ }
+ else {
+ fDTDHandler.externalEntityDecl(name, fResourceIdentifier, null);
+ }
+ }
+ }
+ else {
+ fEntityStore.addInternalEntity(name, fStringBuffer.toString());
+ if (fDTDHandler != null) {
+ fDTDHandler.internalEntityDecl(name, fStringBuffer, fStringBuffer2, null);
+ }
+ }
+ fReportEntity = true;
+
+ } // scanEntityDecl()
+
+ /**
+ * Scans an entity value.
+ *
+ * @param value The string to fill in with the value.
+ * @param nonNormalizedValue The string to fill in with the
+ * non-normalized value.
+ *
+ * <strong>Note:</strong> This method uses fString, fStringBuffer (through
+ * the use of scanCharReferenceValue), and fStringBuffer2, anything in them
+ * at the time of calling is lost.
+ */
+ protected final void scanEntityValue(XMLString value,
+ XMLString nonNormalizedValue)
+ throws IOException, XNIException {
+ int quote = fEntityScanner.scanChar();
+ if (quote != '\'' && quote != '"') {
+ reportFatalError("OpenQuoteMissingInDecl", null);
+ }
+ // store at which depth of entities we start
+ int entityDepth = fEntityDepth;
+
+ XMLString literal = fString;
+ XMLString literal2 = fString;
+ if (fEntityScanner.scanLiteral(quote, fString) != quote) {
+ fStringBuffer.clear();
+ fStringBuffer2.clear();
+ do {
+ fStringBuffer.append(fString);
+ fStringBuffer2.append(fString);
+ if (fEntityScanner.skipChar('&')) {
+ if (fEntityScanner.skipChar('#')) {
+ fStringBuffer2.append("&#");
+ scanCharReferenceValue(fStringBuffer, fStringBuffer2);
+ }
+ else {
+ fStringBuffer.append('&');
+ fStringBuffer2.append('&');
+ String eName = fEntityScanner.scanName();
+ if (eName == null) {
+ reportFatalError("NameRequiredInReference",
+ null);
+ }
+ else {
+ fStringBuffer.append(eName);
+ fStringBuffer2.append(eName);
+ }
+ if (!fEntityScanner.skipChar(';')) {
+ reportFatalError("SemicolonRequiredInReference",
+ new Object[]{eName});
+ }
+ else {
+ fStringBuffer.append(';');
+ fStringBuffer2.append(';');
+ }
+ }
+ }
+ else if (fEntityScanner.skipChar('%')) {
+ while (true) {
+ fStringBuffer2.append('%');
+ String peName = fEntityScanner.scanName();
+ if (peName == null) {
+ reportFatalError("NameRequiredInPEReference",
+ null);
+ }
+ else if (!fEntityScanner.skipChar(';')) {
+ reportFatalError("SemicolonRequiredInPEReference",
+ new Object[]{peName});
+ }
+ else {
+ if (scanningInternalSubset()) {
+ reportFatalError("PEReferenceWithinMarkup",
+ new Object[]{peName});
+ }
+ fStringBuffer2.append(peName);
+ fStringBuffer2.append(';');
+ }
+ startPE(peName, true);
+ // REVISIT: [Q] Why do we skip spaces here? -Ac
+ // REVISIT: This will make returning the non-
+ // normalized value harder. -Ac
+ fEntityScanner.skipSpaces();
+ if (!fEntityScanner.skipChar('%'))
+ break;
+ }
+ }
+ else {
+ int c = fEntityScanner.peekChar();
+ if (XMLChar.isHighSurrogate(c)) {
+ scanSurrogates(fStringBuffer2);
+ }
+ else if (isInvalidLiteral(c)) {
+ reportFatalError("InvalidCharInLiteral",
+ new Object[]{Integer.toHexString(c)});
+ fEntityScanner.scanChar();
+ }
+ // if it's not the delimiting quote or if it is but from a
+ // different entity than the one this literal started from,
+ // simply append the character to our buffer
+ else if (c != quote || entityDepth != fEntityDepth) {
+ fStringBuffer.append((char)c);
+ fStringBuffer2.append((char)c);
+ fEntityScanner.scanChar();
+ }
+ }
+ } while (fEntityScanner.scanLiteral(quote, fString) != quote);
+ fStringBuffer.append(fString);
+ fStringBuffer2.append(fString);
+ literal = fStringBuffer;
+ literal2 = fStringBuffer2;
+ }
+ value.setValues(literal);
+ nonNormalizedValue.setValues(literal2);
+ if (!fEntityScanner.skipChar(quote)) {
+ reportFatalError("CloseQuoteMissingInDecl", null);
+ }
+ } // scanEntityValue(XMLString,XMLString):void
+
+ /**
+ * Scans a notation declaration
+ * <p>
+ * <pre>
+ * [82] NotationDecl ::= '<!NOTATION' S Name S (ExternalID|PublicID) S? '>'
+ * [83] PublicID ::= 'PUBLIC' S PubidLiteral
+ * </pre>
+ * <p>
+ * <strong>Note:</strong> Called after scanning past '<!NOTATION'
+ */
+ private final void scanNotationDecl() throws IOException, XNIException {
+
+ // spaces
+ fReportEntity = false;
+ if (!skipSeparator(true, !scanningInternalSubset())) {
+ reportFatalError("MSG_SPACE_REQUIRED_BEFORE_NOTATION_NAME_IN_NOTATIONDECL",
+ null);
+ }
+
+ // notation name
+ String name = fEntityScanner.scanName();
+ if (name == null) {
+ reportFatalError("MSG_NOTATION_NAME_REQUIRED_IN_NOTATIONDECL",
+ null);
+ }
+
+ // spaces
+ if (!skipSeparator(true, !scanningInternalSubset())) {
+ reportFatalError("MSG_SPACE_REQUIRED_AFTER_NOTATION_NAME_IN_NOTATIONDECL",
+ new Object[]{name});
+ }
+
+ // external id
+ scanExternalID(fStrings, true);
+ String systemId = fStrings[0];
+ String publicId = fStrings[1];
+ String baseSystemId = fEntityScanner.getBaseSystemId();
+
+ if (systemId == null && publicId == null) {
+ reportFatalError("ExternalIDorPublicIDRequired",
+ new Object[]{name});
+ }
+
+ // skip possible trailing space
+ skipSeparator(false, !scanningInternalSubset());
+
+ // end
+ if (!fEntityScanner.skipChar('>')) {
+ reportFatalError("NotationDeclUnterminated", new Object[]{name});
+ }
+ fMarkUpDepth--;
+
+ fResourceIdentifier.setValues(publicId, systemId, baseSystemId, XMLEntityManager.expandSystemId(systemId, baseSystemId ));
+ if (nonValidatingMode) nvGrammarInfo.notationDecl(name, fResourceIdentifier, null);
+ // call handler
+ if (fDTDHandler != null) {
+ //Venu Revisit wby false has been removed.
+ //fResourceIdentifier.setValues(publicId, systemId, baseSystemId, XMLEntityManager.expandSystemId(systemId, baseSystemId, false));
+ fDTDHandler.notationDecl(name, fResourceIdentifier, null);
+ }
+ fReportEntity = true;
+
+ } // scanNotationDecl()
+
+ /**
+ * Scans a conditional section. If it's a section to ignore the whole
+ * section gets scanned through and this method only returns after the
+ * closing bracket has been found. When it's an include section though, it
+ * returns to let the main loop take care of scanning it. In that case the
+ * end of the section if handled by the main loop (scanDecls).
+ * <p>
+ * <pre>
+ * [61] conditionalSect ::= includeSect | ignoreSect
+ * [62] includeSect ::= '<![' S? 'INCLUDE' S? '[' extSubsetDecl ']]>'
+ * [63] ignoreSect ::= '<![' S? 'IGNORE' S? '[' ignoreSectContents* ']]>'
+ * [64] ignoreSectContents ::= Ignore ('<![' ignoreSectContents ']]>' Ignore)*
+ * [65] Ignore ::= Char* - (Char* ('<![' | ']]>') Char*)
+ * </pre>
+ * <p>
+ * <strong>Note:</strong> Called after scanning past '<![' */
+ private final void scanConditionalSect(int currPEDepth)
+ throws IOException, XNIException {
+
+ fReportEntity = false;
+ skipSeparator(false, !scanningInternalSubset());
+
+ if (fEntityScanner.skipString("INCLUDE")) {
+ skipSeparator(false, !scanningInternalSubset());
+ if(currPEDepth != fPEDepth && fValidation) {
+ fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
+ "INVALID_PE_IN_CONDITIONAL",
+ new Object[]{ fEntityManager.fCurrentEntity.name},
+ XMLErrorReporter.SEVERITY_ERROR);
+ }
+ // call handler
+ if (!fEntityScanner.skipChar('[')) {
+ reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
+ }
+
+ if (fDTDHandler != null) {
+ fDTDHandler.startConditional(XMLDTDHandler.CONDITIONAL_INCLUDE,
+ null);
+ }
+ fIncludeSectDepth++;
+ // just stop there and go back to the main loop
+ fReportEntity = true;
+ }
+ else if (fEntityScanner.skipString("IGNORE")) {
+ skipSeparator(false, !scanningInternalSubset());
+ if(currPEDepth != fPEDepth && fValidation) {
+ fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
+ "INVALID_PE_IN_CONDITIONAL",
+ new Object[]{ fEntityManager.fCurrentEntity.name},
+ XMLErrorReporter.SEVERITY_ERROR);
+ }
+ // call handler
+ if (fDTDHandler != null) {
+ fDTDHandler.startConditional(XMLDTDHandler.CONDITIONAL_IGNORE,
+ null);
+ }
+ if (!fEntityScanner.skipChar('[')) {
+ reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
+ }
+ fReportEntity = true;
+ int initialDepth = ++fIncludeSectDepth;
+ if (fDTDHandler != null) {
+ fIgnoreConditionalBuffer.clear();
+ }
+ while (true) {
+ if (fEntityScanner.skipChar('<')) {
+ if (fDTDHandler != null) {
+ fIgnoreConditionalBuffer.append('<');
+ }
+ //
+ // These tests are split so that we handle cases like
+ // '<<![' and '<!<![' which we might otherwise miss.
+ //
+ if (fEntityScanner.skipChar('!')) {
+ if(fEntityScanner.skipChar('[')) {
+ if (fDTDHandler != null) {
+ fIgnoreConditionalBuffer.append("![");
+ }
+ fIncludeSectDepth++;
+ } else {
+ if (fDTDHandler != null) {
+ fIgnoreConditionalBuffer.append("!");
+ }
+ }
+ }
+ }
+ else if (fEntityScanner.skipChar(']')) {
+ if (fDTDHandler != null) {
+ fIgnoreConditionalBuffer.append(']');
+ }
+ //
+ // The same thing goes for ']<![' and '<]]>', etc.
+ //
+ if (fEntityScanner.skipChar(']')) {
+ if (fDTDHandler != null) {
+ fIgnoreConditionalBuffer.append(']');
+ }
+ while (fEntityScanner.skipChar(']')) {
+ /* empty loop body */
+ if (fDTDHandler != null) {
+ fIgnoreConditionalBuffer.append(']');
+ }
+ }
+ if (fEntityScanner.skipChar('>')) {
+ if (fIncludeSectDepth-- == initialDepth) {
+ fMarkUpDepth--;
+ // call handler
+ if (fDTDHandler != null) {
+ fLiteral.setValues(fIgnoreConditionalBuffer.ch, 0,
+ fIgnoreConditionalBuffer.length - 2);
+ fDTDHandler.ignoredCharacters(fLiteral, null);
+ fDTDHandler.endConditional(null);
+ }
+ return;
+ } else if(fDTDHandler != null) {
+ fIgnoreConditionalBuffer.append('>');
+ }
+ }
+ }
+ }
+ else {
+ int c = fEntityScanner.scanChar();
+ if (fScannerState == SCANNER_STATE_END_OF_INPUT) {
+ reportFatalError("IgnoreSectUnterminated", null);
+ return;
+ }
+ if (fDTDHandler != null) {
+ fIgnoreConditionalBuffer.append((char)c);
+ }
+ }
+ }
+ }
+ else {
+ reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
+ }
+
+ } // scanConditionalSect()
+
+ /**
+ * Dispatch an XML "event".
+ *
+ * @param complete True if this method is intended to scan
+ * and dispatch as much as possible.
+ *
+ * @return True if there is more to scan.
+ *
+ * @throws IOException Thrown on i/o error.
+ * @throws XNIException Thrown on parse error.
+ *
+ */
+ protected final boolean scanDecls(boolean complete)
+ throws IOException, XNIException {
+
+ skipSeparator(false, true);
+ boolean again = true;
+ //System.out.println("scanDecls"+fScannerState);
+ while (again && fScannerState == SCANNER_STATE_MARKUP_DECL) {
+ again = complete;
+ if (fEntityScanner.skipChar('<')) {
+ fMarkUpDepth++;
+ if (fEntityScanner.skipChar('?')) {
+ fStringBuffer.clear();
+ scanPI(fStringBuffer);
+ fMarkUpDepth--; // we're done with this decl
+ }
+ else if (fEntityScanner.skipChar('!')) {
+ if (fEntityScanner.skipChar('-')) {
+ if (!fEntityScanner.skipChar('-')) {
+ reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD",
+ null);
+ } else {
+ scanComment();
+ }
+ }
+ else if (fEntityScanner.skipString("ELEMENT")) {
+ scanElementDecl();
+ }
+ else if (fEntityScanner.skipString("ATTLIST")) {
+ scanAttlistDecl();
+ }
+ else if (fEntityScanner.skipString("ENTITY")) {
+ scanEntityDecl();
+ }
+ else if (fEntityScanner.skipString("NOTATION")) {
+ scanNotationDecl();
+ }
+ else if (fEntityScanner.skipChar('[') &&
+ !scanningInternalSubset()) {
+ scanConditionalSect(fPEDepth);
+ }
+ else {
+ fMarkUpDepth--;
+ reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD",
+ null);
+ }
+ }
+ else {
+ fMarkUpDepth--;
+ reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
+ }
+ }
+ else if (fIncludeSectDepth > 0 && fEntityScanner.skipChar(']')) {
+ // end of conditional section?
+ if (!fEntityScanner.skipChar(']')
+ || !fEntityScanner.skipChar('>')) {
+ reportFatalError("IncludeSectUnterminated", null);
+ }
+ // call handler
+ if (fDTDHandler != null) {
+ fDTDHandler.endConditional(null);
+ }
+ // decreaseMarkupDepth();
+ fIncludeSectDepth--;
+ fMarkUpDepth--;
+ }
+ else if (scanningInternalSubset() &&
+ fEntityScanner.peekChar() == ']') {
+ // this is the end of the internal subset, let's stop here
+ return false;
+ }
+ else if (fEntityScanner.skipSpaces()) {
+ // simply skip
+ }
+ else {
+ reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
+ }
+ skipSeparator(false, true);
+ }
+ return fScannerState != SCANNER_STATE_END_OF_INPUT;
+ }
+
+ /**
+ * Skip separator. This is typically just whitespace but it can also be one
+ * or more parameter entity references.
+ * <p>
+ * If there are some it "expands them" by calling the corresponding entity
+ * from the entity manager.
+ * <p>
+ * This is recursive and will process has many refs as possible.
+ *
+ * @param spaceRequired Specify whether some leading whitespace should be
+ * found
+ * @param lookForPERefs Specify whether parameter entity references should
+ * be looked for
+ * @return True if any leading whitespace was found or the end of a
+ * parameter entity was crossed.
+ */
+ private boolean skipSeparator(boolean spaceRequired, boolean lookForPERefs)
+ throws IOException, XNIException {
+ int depth = fPEDepth;
+ boolean sawSpace = fEntityScanner.skipSpaces();
+ if (!lookForPERefs || !fEntityScanner.skipChar('%')) {
+ return !spaceRequired || sawSpace || (depth != fPEDepth);
+ }
+ while (true) {
+ String name = fEntityScanner.scanName();
+ if (name == null) {
+ reportFatalError("NameRequiredInPEReference", null);
+ }
+ else if (!fEntityScanner.skipChar(';')) {
+ reportFatalError("SemicolonRequiredInPEReference",
+ new Object[]{name});
+ }
+ startPE(name, false);
+ fEntityScanner.skipSpaces();
+ if (!fEntityScanner.skipChar('%'))
+ return true;
+ }
+ }
+
+
+ /*
+ * Element Children Content Stack
+ */
+ private final void pushContentStack(int c) {
+ if (fContentStack.length == fContentDepth) {
+ int[] newStack = new int[fContentDepth * 2];
+ System.arraycopy(fContentStack, 0, newStack, 0, fContentDepth);
+ fContentStack = newStack;
+ }
+ fContentStack[fContentDepth++] = c;
+ }
+
+ private final int popContentStack() {
+ return fContentStack[--fContentDepth];
+ }
+
+
+ /*
+ * Parameter Entity Stack
+ */
+ private final void pushPEStack(int depth, boolean report) {
+ if (fPEStack.length == fPEDepth) {
+ int[] newIntStack = new int[fPEDepth * 2];
+ System.arraycopy(fPEStack, 0, newIntStack, 0, fPEDepth);
+ fPEStack = newIntStack;
+ // report end/start calls
+ boolean[] newBooleanStack = new boolean[fPEDepth * 2];
+ System.arraycopy(fPEReport, 0, newBooleanStack, 0, fPEDepth);
+ fPEReport = newBooleanStack;
+
+ }
+ fPEReport[fPEDepth] = report;
+ fPEStack[fPEDepth++] = depth;
+ }
+
+ /** pop the stack */
+ private final int popPEStack() {
+ return fPEStack[--fPEDepth];
+ }
+
+ /** look at the top of the stack */
+ private final boolean peekReportEntity() {
+ return fPEReport[fPEDepth-1];
+ }
+
+
+ /*
+ * Utility method
+ */
+ private final void ensureEnumerationSize(int size) {
+ if (fEnumeration.length == size) {
+ String[] newEnum = new String[size * 2];
+ System.arraycopy(fEnumeration, 0, newEnum, 0, size);
+ fEnumeration = newEnum;
+ }
+ }
+
+ // private methods
+ private void init() {
+ // reset state related data
+ fStartDTDCalled = false;
+ fExtEntityDepth = 0;
+ fIncludeSectDepth = 0;
+ fMarkUpDepth = 0;
+ fPEDepth = 0;
+
+ fStandalone = false;
+ fSeenExternalDTD = false;
+ fSeenExternalPE = false;
+
+ // set starting state
+ setScannerState(SCANNER_STATE_TEXT_DECL);
+ //new SymbolTable());
+ }
+
+ public DTDGrammar getGrammar(){
+ return nvGrammarInfo;
+ }
+
+} // class XMLDTDScannerImpl