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

package org.apache.xerces.impl;

import java.io.IOException;

import org.apache.xerces.impl.msg.XMLMessageFormatter;
import org.apache.xerces.util.SymbolTable;
import org.apache.xerces.util.XMLChar;
import org.apache.xerces.util.XMLStringBuffer;
import org.apache.xerces.util.XMLSymbols;
import org.apache.xerces.xni.Augmentations;
import org.apache.xerces.xni.XMLDTDContentModelHandler;
import org.apache.xerces.xni.XMLDTDHandler;
import org.apache.xerces.xni.XMLResourceIdentifier;
import org.apache.xerces.xni.XMLString;
import org.apache.xerces.xni.XNIException;
import org.apache.xerces.xni.parser.XMLComponent;
import org.apache.xerces.xni.parser.XMLComponentManager;
import org.apache.xerces.xni.parser.XMLConfigurationException;
import org.apache.xerces.xni.parser.XMLDTDScanner;
import org.apache.xerces.xni.parser.XMLInputSource;

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.

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

  • http://xml.org/sax/features/validation
  • http://apache.org/xml/features/scanner/notify-char-refs
  • http://apache.org/xml/properties/internal/symbol-table
  • http://apache.org/xml/properties/internal/error-reporter
  • http://apache.org/xml/properties/internal/entity-manager
Author:Arnaud Le Hors, IBM, Andy Clark, IBM, Glenn Marcy, IBM, Eric Ye, IBM
@xerces.internal
Version:$Id: XMLDTDScannerImpl.java 1514806 2013-08-16 17:29:02Z mrglavas $
/** * 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> * * @xerces.internal * * @author Arnaud Le Hors, IBM * @author Andy Clark, IBM * @author Glenn Marcy, IBM * @author Eric Ye, IBM * * @version $Id: XMLDTDScannerImpl.java 1514806 2013-08-16 17:29:02Z mrglavas $ */
public class XMLDTDScannerImpl extends XMLScanner implements XMLDTDScanner, XMLComponent, XMLEntityHandler { // // Constants // // scanner states
Scanner state: end of input.
/** Scanner state: end of input. */
protected static final int SCANNER_STATE_END_OF_INPUT = 0;
Scanner state: text declaration.
/** Scanner state: text declaration. */
protected static final int SCANNER_STATE_TEXT_DECL = 1;
Scanner state: markup declaration.
/** Scanner state: markup declaration. */
protected static final int SCANNER_STATE_MARKUP_DECL = 2; // recognized features and properties
Recognized features.
/** Recognized features. */
private static final String[] RECOGNIZED_FEATURES = { VALIDATION, NOTIFY_CHAR_REFS, };
Feature defaults.
/** Feature defaults. */
private static final Boolean[] FEATURE_DEFAULTS = { null, Boolean.FALSE, };
Recognized properties.
/** Recognized properties. */
private static final String[] RECOGNIZED_PROPERTIES = { SYMBOL_TABLE, ERROR_REPORTER, ENTITY_MANAGER, };
Property defaults.
/** Property defaults. */
private static final Object[] PROPERTY_DEFAULTS = { null, null, null, }; // debugging
Debug scanner state.
/** Debug scanner state. */
private static final boolean DEBUG_SCANNER_STATE = false; // // Data // // handlers
DTD handler.
/** DTD handler. */
protected XMLDTDHandler fDTDHandler;
DTD content model handler.
/** DTD content model handler. */
protected XMLDTDContentModelHandler fDTDContentModelHandler; // state
Scanner state.
/** Scanner state. */
protected int fScannerState;
Standalone.
/** Standalone. */
protected boolean fStandalone;
Seen external DTD.
/** Seen external DTD. */
protected boolean fSeenExternalDTD;
Seen a parameter entity reference.
/** Seen a parameter entity reference. */
protected boolean fSeenPEReferences; // private data
Start DTD called.
/** Start DTD called. */
private boolean fStartDTDCalled;
Stack of content operators (either '|' or ',') in children content.
/** * Stack of content operators (either '|' or ',') in children * content. */
private int[] fContentStack = new int[5];
Size of content stack.
/** Size of content stack. */
private int fContentDepth;
Parameter entity stack to check well-formedness.
/** Parameter entity stack to check well-formedness. */
private int[] fPEStack = new int[5];
Parameter entity stack to report start/end entity calls.
/** Parameter entity stack to report start/end entity calls. */
private boolean[] fPEReport = new boolean[5];
Number of opened parameter entities.
/** Number of opened parameter entities. */
private int fPEDepth;
Markup depth.
/** Markup depth. */
private int fMarkUpDepth;
Number of opened external entities.
/** Number of opened external entities. */
private int fExtEntityDepth;
Number of opened include sections.
/** Number of opened include sections. */
private int fIncludeSectDepth; // temporary variables
Array of 3 strings.
/** Array of 3 strings. */
private final String[] fStrings = new String[3];
String.
/** String. */
private final XMLString fString = new XMLString();
String buffer.
/** String buffer. */
private final XMLStringBuffer fStringBuffer = new XMLStringBuffer();
String buffer.
/** String buffer. */
private final XMLStringBuffer fStringBuffer2 = new XMLStringBuffer();
Literal text.
/** Literal text. */
private final XMLString fLiteral = new XMLString();
Literal text.
/** Literal text. */
private final XMLString fLiteral2 = new XMLString();
Enumeration values.
/** Enumeration values. */
private String[] fEnumeration = new String[5];
Enumeration values count.
/** Enumeration values count. */
private int fEnumerationCount;
Ignore conditional section buffer.
/** Ignore conditional section buffer. */
private final XMLStringBuffer fIgnoreConditionalBuffer = new XMLStringBuffer(128); // // Constructors //
Default constructor.
/** Default constructor. */
public XMLDTDScannerImpl() {} // <init>()
Constructor for he use of non-XMLComponentManagers.
/** 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.
Params:
  • inputSource – The input source or null.
Throws:
/** * 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); } return; } fEntityManager.setEntityHandler(this); fEntityManager.startDTDEntity(inputSource); } // setInputSource(XMLInputSource)
Scans the external subset of the document.
Params:
  • 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.
Returns:True if there is more to scan, false otherwise.
/** * 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.
Params:
  • 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.
  • standalone – True if the document was specified as standalone. This value is important for verifying certain well-formedness constraints.
  • hasExternalSubset – 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.
Returns:True if there is more to scan, false otherwise.
/** * 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 hasExternalSubset 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 fEntityScanner = fEntityManager.getEntityScanner(); fEntityManager.setEntityHandler(this); fStandalone = standalone; if (fScannerState == SCANNER_STATE_TEXT_DECL) { // call handler if (fDTDHandler != null) { fDTDHandler.startDTD(fEntityScanner, null); fStartDTDCalled = true; } // 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); } // 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
Params:
  • componentManager –
/** * 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(); }
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.
/** * 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.
/** * 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.
Params:
  • featureId – The feature identifier.
Since:Xerces 2.2.0
/** * 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.
Params:
  • propertyId – The property identifier.
Since:Xerces 2.2.0
/** * 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
Params:
  • dtdHandler –
/** * setDTDHandler * * @param dtdHandler */
public void setDTDHandler(XMLDTDHandler dtdHandler) { fDTDHandler = dtdHandler; } // setDTDHandler(XMLDTDHandler)
getDTDHandler
Returns:the XMLDTDHandler
/** * getDTDHandler * * @return the XMLDTDHandler */
public XMLDTDHandler getDTDHandler() { return fDTDHandler; } // getDTDHandler(): XMLDTDHandler // // XMLDTDContentModelSource methods //
setDTDContentModelHandler
Params:
  • dtdContentModelHandler –
/** * setDTDContentModelHandler * * @param dtdContentModelHandler */
public void setDTDContentModelHandler(XMLDTDContentModelHandler dtdContentModelHandler) { fDTDContentModelHandler = dtdContentModelHandler; } // setDTDContentModelHandler
getDTDContentModelHandler
Returns:XMLDTDContentModelHandler
/** * 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.
Params:
  • name – The name of the entity.
  • identifier – The resource identifier.
  • 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).
  • augs – Additional information that may include infoset augmentations
Throws:
/** * 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(); 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, augs); } } // 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.
Params:
  • name – The name of the entity.
  • augs – Additional information that may include infoset augmentations
Throws:
/** * 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. * @param augs Additional information that may include infoset augmentations * * @throws XNIException Thrown by handler to signal an error. */
public void endEntity(String name, Augmentations augs) throws XNIException { 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 if (fDTDHandler != null && reportEntity) { fDTDHandler.endParameterEntity(name, augs); } } // end DTD else if (name.equals("[dtd]")) { if (fIncludeSectDepth != 0) { reportFatalError("IncludeSectUnterminated", null); } fScannerState = SCANNER_STATE_END_OF_INPUT; // call handler fEntityManager.endExternalSubset(); if (fDTDHandler != null) { fDTDHandler.endExternalSubset(null); fDTDHandler.endDTD(null); } fExtEntityDepth--; } } // endEntity(String) // helper methods
Sets the scanner state.
Params:
  • state – The new scanner state.
/** * 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.
/** 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
Params:
  • name – The name of the parameter entity to start (without the '%')
  • literal – Whether this is happening within a literal
Returns:The name of the parameter entity (with the '%')
/** * 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 * * @return The name of the parameter entity (with the '%') */
protected String startPE(String name, boolean literal) throws IOException, XNIException { int depth = fPEDepth; String pName = "%"+name; if (!fSeenPEReferences) { fSeenPEReferences = true; fEntityManager.notifyHasPEReferences(); } if (fValidation && !fEntityManager.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(); } return pName; }
Dispatch an XML "event".
Throws:
Returns:true if a TextDecl was scanned.
/** * Dispatch an XML "event". * * @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"); if (fNamespaces) { while (isValidNCName(fEntityScanner.peekChar())) { fStringBuffer.append((char)fEntityScanner.scanChar()); } } else { 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.setXMLVersion(version); if (!fEntityScanner.fCurrentEntity.isEncodingExternallySpecified()) { 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 starts with "xml". (e.g. xmlfoo)
Params:
  • target – The PI target
  • data – The string to fill in with the data
/** * 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 { super.scanPIData(target, data); fMarkUpDepth--; // call handler if (fDTDHandler != null) { fDTDHandler.processingInstruction(target, data, null); } } // scanPIData(String)
Scans a comment.

[15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'

Note: Called after scanning past '<!--'

/** * Scans a comment. * <p> * <pre> * [15] Comment ::= '&lt!--' ((Char - '-') | ('-' (Char - '-')))* '-->' * </pre> * <p> * <strong>Note:</strong> Called after scanning past '&lt;!--' */
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

[45]    elementdecl    ::=    '<!ELEMENT' S Name S contentspec S? '>'
[46]    contentspec    ::=    'EMPTY' | 'ANY' | Mixed | children  

Note: Called after scanning past '<!ELEMENT'

/** * Scans an element declaration * <p> * <pre> * [45] elementdecl ::= '&lt;!ELEMENT' S Name S contentspec S? '>' * [46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | children * </pre> * <p> * <strong>Note:</strong> Called after scanning past '&lt;!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); } } // scanElementDecl()
scan Mixed content model This assumes the content model has been parsed up to #PCDATA and can simply append to fStringBuffer.
[51]    Mixed    ::=    '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*'  
                      | '(' S? '#PCDATA' S? ')'  
Params:
  • elName – The element type name this declaration is about. Note: Called after scanning past '(#PCDATA'.
/** * 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.
[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? ')' 
Params:
  • elName – The element type name this declaration is about. Note: Called after scanning past the first open paranthesis.
/** * 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

[52]  AttlistDecl    ::=   '<!ATTLIST' S Name AttDef* S? '>' 
[53]  AttDef         ::=   S Name S AttType S DefaultDecl 

Note: Called after scanning past '<!ATTLIST'

/** * Scans an attlist declaration * <p> * <pre> * [52] AttlistDecl ::= '&lt;!ATTLIST' S Name AttDef* S? '>' * [53] AttDef ::= S Name S AttType S DefaultDecl * </pre> * <p> * <strong>Note:</strong> Called after scanning past '&lt;!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 // call handler if (fDTDHandler != null) { String[] enumeration = null; if (fEnumerationCount != 0) { enumeration = new String[fEnumerationCount]; System.arraycopy(fEnumeration, 0, enumeration, 0, fEnumerationCount); } // 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"))) { fDTDHandler.attributeDecl(elName, name, type, enumeration, defaultType, null, null, null); } else { fDTDHandler.attributeDecl(elName, name, type, enumeration, defaultType, fLiteral, fLiteral2, null); } } skipSeparator(false, !scanningInternalSubset()); } // call handler if (fDTDHandler != null) { fDTDHandler.endAttlist(null); } fMarkUpDepth--; fReportEntity = true; } // scanAttlistDecl()
Scans an attribute type definition

[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? ')' 

Note: Called after scanning past '<!ATTLIST'

Params:
  • elName – The element type name this declaration is about.
  • atName – The attribute name this declaration is about.
/** * 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 '&lt;!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}); c = skipInvalidEnumerationValue(); if (c == '|') { continue; } break; } 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}); c = skipInvalidEnumerationValue(); if (c == '|') { continue; } break; } 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

[60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue)
Params:
  • elName –
  • atName – The name of the attribute being scanned.
  • type –
  • defaultVal – The string to fill in with the default value.
  • nonNormalizedDefaultVal –
/** * Scans an attribute default declaration * <p> * <pre> * [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue) * </pre> * * @param elName * @param atName The name of the attribute being scanned. * @param type * @param defaultVal The string to fill in with the default value. * @param nonNormalizedDefaultVal */
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 || fSeenPEReferences); scanAttributeValue(defaultVal, nonNormalizedDefaultVal, atName, isVC, elName); } return defaultType; } // ScanAttDefaultDecl
Scans an entity declaration

[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 

Note: Called after scanning past '<!ENTITY'

/** * Scans an entity declaration * <p> * <pre> * [70] EntityDecl ::= GEDecl | PEDecl * [71] GEDecl ::= '&lt;!ENTITY' S Name S EntityDef S? '>' * [72] PEDecl ::= '&lt;!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 '&lt;!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_PEDECL", 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 = null; if(fNamespaces) { name = fEntityScanner.scanNCName(); } else { name = fEntityScanner.scanName(); } if (name == null) { reportFatalError("MSG_ENTITY_NAME_REQUIRED_IN_ENTITYDECL", null); } // spaces if (!skipSeparator(true, !scanningInternalSubset())) { if(fNamespaces && fEntityScanner.peekChar() == ':') { fEntityScanner.scanChar(); XMLStringBuffer colonName = new XMLStringBuffer(name); colonName.append(':'); String str = fEntityScanner.scanName(); if (str != null) colonName.append(str); reportFatalError("ColonNotLegalWithNS", new Object[] {colonName.toString()}); if (!skipSeparator(true, !scanningInternalSubset())) { reportFatalError("MSG_SPACE_REQUIRED_AFTER_ENTITY_NAME_IN_ENTITYDECL", new Object[]{name}); } } else { 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]; 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}); } } // count of direct and indirect references to parameter entities in the value of the entity. int paramEntityRefs = 0; // internal entity if (systemId == null) { paramEntityRefs = 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) { fEntityManager.addUnparsedEntity(name, publicId, systemId, baseSystemId, notation); } else { fEntityManager.addExternalEntity(name, publicId, systemId, baseSystemId); } if (fDTDHandler != null) { fResourceIdentifier.setValues(publicId, systemId, baseSystemId, XMLEntityManager.expandSystemId(systemId, baseSystemId, false)); if (notation != null) { fDTDHandler.unparsedEntityDecl(name, fResourceIdentifier, notation, null); } else { fDTDHandler.externalEntityDecl(name, fResourceIdentifier, null); } } } else { fEntityManager.addInternalEntity(name, fStringBuffer.toString(), paramEntityRefs); if (fDTDHandler != null) { fDTDHandler.internalEntityDecl(name, fStringBuffer, fStringBuffer2, null); } } fReportEntity = true; } // scanEntityDecl()

Scans an entity value.

Note: This method uses fString, fStringBuffer (through the use of scanCharReferenceValue), and fStringBuffer2, anything in them at the time of calling is lost.

Params:
  • value – The string to fill in with the value.
  • nonNormalizedValue – The string to fill in with the non-normalized value.
Returns:Count of direct and indirect references to parameter entities in the value of the entity.
/** * <p>Scans an entity value.</p> * * <p><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.</p> * * @param value The string to fill in with the value. * @param nonNormalizedValue The string to fill in with the * non-normalized value. * * @return Count of direct and indirect references to parameter entities in the value of the entity. */
protected final int 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; // count of direct and indirect references to parameter entities in the value of the entity. int paramEntityRefs = 0; 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(';'); } final String pNameWithPct = startPE(peName, true); paramEntityRefs += (fEntityManager.getParamEntityRefCount(pNameWithPct) + 1); // 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); } return paramEntityRefs; } // scanEntityValue(XMLString,XMLString):int
Scans a notation declaration

[82] NotationDecl ::= '<!NOTATION' S Name S (ExternalID|PublicID) S? '>'
[83]  PublicID    ::= 'PUBLIC' S PubidLiteral  

Note: Called after scanning past '<!NOTATION'

/** * Scans a notation declaration * <p> * <pre> * [82] NotationDecl ::= '&lt;!NOTATION' S Name S (ExternalID|PublicID) S? '>' * [83] PublicID ::= 'PUBLIC' S PubidLiteral * </pre> * <p> * <strong>Note:</strong> Called after scanning past '&lt;!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 = null; if(fNamespaces) { name = fEntityScanner.scanNCName(); } else { name = fEntityScanner.scanName(); } if (name == null) { reportFatalError("MSG_NOTATION_NAME_REQUIRED_IN_NOTATIONDECL", null); } // spaces if (!skipSeparator(true, !scanningInternalSubset())) { // check for invalid ":" if(fNamespaces && fEntityScanner.peekChar() == ':') { fEntityScanner.scanChar(); XMLStringBuffer colonName = new XMLStringBuffer(name); colonName.append(':'); colonName.append(fEntityScanner.scanName()); reportFatalError("ColonNotLegalWithNS", new Object[] {colonName.toString()}); skipSeparator(true, !scanningInternalSubset()); } else { 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--; // call handler if (fDTDHandler != null) { 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).

[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*)  

Note: Called after scanning past '<!['

/** * 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 ::= '&lt;![' S? 'INCLUDE' S? '[' extSubsetDecl ']]>' * [63] ignoreSect ::= '&lt;![' S? 'IGNORE' S? '[' ignoreSectContents* ']]>' * [64] ignoreSectContents ::= Ignore ('&lt;![' ignoreSectContents ']]>' Ignore)* * [65] Ignore ::= Char* - (Char* ('&lt;![' | ']]>') Char*) * </pre> * <p> * <strong>Note:</strong> Called after scanning past '&lt;![' */
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".
Params:
  • complete – True if this method is intended to scan and dispatch as much as possible.
Throws:
Returns:True if there is more to scan.
/** * 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; while (again && fScannerState == SCANNER_STATE_MARKUP_DECL) { again = complete; if (fEntityScanner.skipChar('<')) { fMarkUpDepth++; if (fEntityScanner.skipChar('?')) { scanPI(); } 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); // Skip the part in error int ch; do { // Ignore the current character fEntityScanner.scanChar(); // Skip any separators skipSeparator(false, true); // Keeping getting the next character, // until it's one of the expected ones ch = fEntityScanner.peekChar(); } while (ch != '<' && ch != ']' && !XMLChar.isSpace(ch)); } 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.

If there are some it "expands them" by calling the corresponding entity from the entity manager.

This is recursive and will process has many refs as possible.

Params:
  • spaceRequired – Specify whether some leading whitespace should be found
  • lookForPERefs – Specify whether parameter entity references should be looked for
Returns:True if any leading whitespace was found or the end of a parameter entity was crossed.
/** * 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
/** pop the stack */
private final int popPEStack() { return fPEStack[--fPEDepth]; }
look at the top of the stack
/** 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; fSeenPEReferences = false; // set starting state setScannerState(SCANNER_STATE_TEXT_DECL); } private int skipInvalidEnumerationValue() throws IOException { int c; do { c = fEntityScanner.scanChar(); } while (c != '|' && c != ')'); ensureEnumerationSize(fEnumerationCount + 1); fEnumeration[fEnumerationCount++] = XMLSymbols.EMPTY_STRING; return c; } } // class XMLDTDScannerImpl