/* * The Apache Software License, Version 1.1 * * * Copyright (c) 1999-2004 The Apache Software Foundation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Xerces" and "Apache Software Foundation" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", * nor may "Apache" appear in their name, without prior written * permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation and was * originally based on software copyright (c) 1999, International * Business Machines, Inc., http://www.apache.org. For more * information on the Apache Software Foundation, please see * . */ package com.sun.org.apache.xerces.internal.impl; 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.Augmentations; 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; /** * 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: *

* * @author Arnaud Le Hors, IBM * @author Andy Clark, IBM * @author Glenn Marcy, IBM * @author Eric Ye, IBM * * @version $Id: XMLDTDScannerImpl.java,v 1.49 2004/02/27 20:36:07 mrglavas 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. */ protected XMLDTDHandler fDTDHandler; /** 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); // // Constructors // /** Default constructor. */ public XMLDTDScannerImpl() {} // () /** 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); } 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 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 * * @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. */ 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(); 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. * * @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 boolean dtdEntity = name.equals("[dtd]"); if (fDTDHandler != null && !dtdEntity && reportEntity) { fDTDHandler.endParameterEntity(name, augs); } // end DTD if (dtdEntity) { 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. * * @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 && !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(); } } /** * 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("starts with "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 '<!--' */ 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' */ 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? ')'  
     * 
* * @param elName The element type name this declaration is about. * * Note: 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? ')' 
     * 
* * @param elName The element type name this declaration is about. * * Note: 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' */ 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' * * @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 *

*

     * [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue)
     * 
* * @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, 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' */ private final void scanEntityDecl() throws IOException, XNIException { boolean isPEDecl = false; boolean sawPERef = false; fReportEntity = false; if (fEntityScanner.skipSpaces()) { if (!fEntityScanner.skipChar('%')) { isPEDecl = false; // } else if (skipSeparator(true, !scanningInternalSubset())) { // isPEDecl = true; } else if (scanningInternalSubset()) { reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL", null); isPEDecl = true; } else if (fEntityScanner.peekChar() == '%') { // is legal skipSeparator(false, !scanningInternalSubset()); isPEDecl = true; } else { sawPERef = true; } } else if (scanningInternalSubset() || !fEntityScanner.skipChar('%')) { // or reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL", null); isPEDecl = false; } else if (fEntityScanner.skipSpaces()) { // 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]; 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) { 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()); 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. * * Note: 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 *

*

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

* Note: 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 = 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 '<![' */ 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 // '<', 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; 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. * * @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); } } // class XMLDTDScannerImpl