0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: package org.apache.xerces.impl;
0019:
0020: import java.io.IOException;
0021:
0022: import org.apache.xerces.impl.msg.XMLMessageFormatter;
0023: import org.apache.xerces.util.SymbolTable;
0024: import org.apache.xerces.util.XMLChar;
0025: import org.apache.xerces.util.XMLStringBuffer;
0026: import org.apache.xerces.util.XMLSymbols;
0027: import org.apache.xerces.xni.Augmentations;
0028: import org.apache.xerces.xni.XMLDTDContentModelHandler;
0029: import org.apache.xerces.xni.XMLDTDHandler;
0030: import org.apache.xerces.xni.XMLResourceIdentifier;
0031: import org.apache.xerces.xni.XMLString;
0032: import org.apache.xerces.xni.XNIException;
0033: import org.apache.xerces.xni.parser.XMLComponent;
0034: import org.apache.xerces.xni.parser.XMLComponentManager;
0035: import org.apache.xerces.xni.parser.XMLConfigurationException;
0036: import org.apache.xerces.xni.parser.XMLDTDScanner;
0037: import org.apache.xerces.xni.parser.XMLInputSource;
0038:
0039: /**
0040: * This class is responsible for scanning the declarations found
0041: * in the internal and external subsets of a DTD in an XML document.
0042: * The scanner acts as the sources for the DTD information which is
0043: * communicated to the DTD handlers.
0044: * <p>
0045: * This component requires the following features and properties from the
0046: * component manager that uses it:
0047: * <ul>
0048: * <li>http://xml.org/sax/features/validation</li>
0049: * <li>http://apache.org/xml/features/scanner/notify-char-refs</li>
0050: * <li>http://apache.org/xml/properties/internal/symbol-table</li>
0051: * <li>http://apache.org/xml/properties/internal/error-reporter</li>
0052: * <li>http://apache.org/xml/properties/internal/entity-manager</li>
0053: * </ul>
0054: *
0055: * @xerces.internal
0056: *
0057: * @author Arnaud Le Hors, IBM
0058: * @author Andy Clark, IBM
0059: * @author Glenn Marcy, IBM
0060: * @author Eric Ye, IBM
0061: *
0062: * @version $Id: XMLDTDScannerImpl.java 572055 2007-09-02 17:55:43Z mrglavas $
0063: */
0064: public class XMLDTDScannerImpl extends XMLScanner implements
0065: XMLDTDScanner, XMLComponent, XMLEntityHandler {
0066:
0067: //
0068: // Constants
0069: //
0070:
0071: // scanner states
0072:
0073: /** Scanner state: end of input. */
0074: protected static final int SCANNER_STATE_END_OF_INPUT = 0;
0075:
0076: /** Scanner state: text declaration. */
0077: protected static final int SCANNER_STATE_TEXT_DECL = 1;
0078:
0079: /** Scanner state: markup declaration. */
0080: protected static final int SCANNER_STATE_MARKUP_DECL = 2;
0081:
0082: // recognized features and properties
0083:
0084: /** Recognized features. */
0085: private static final String[] RECOGNIZED_FEATURES = { VALIDATION,
0086: NOTIFY_CHAR_REFS, };
0087:
0088: /** Feature defaults. */
0089: private static final Boolean[] FEATURE_DEFAULTS = { null,
0090: Boolean.FALSE, };
0091:
0092: /** Recognized properties. */
0093: private static final String[] RECOGNIZED_PROPERTIES = {
0094: SYMBOL_TABLE, ERROR_REPORTER, ENTITY_MANAGER, };
0095:
0096: /** Property defaults. */
0097: private static final Object[] PROPERTY_DEFAULTS = { null, null,
0098: null, };
0099:
0100: // debugging
0101:
0102: /** Debug scanner state. */
0103: private static final boolean DEBUG_SCANNER_STATE = false;
0104:
0105: //
0106: // Data
0107: //
0108:
0109: // handlers
0110:
0111: /** DTD handler. */
0112: protected XMLDTDHandler fDTDHandler;
0113:
0114: /** DTD content model handler. */
0115: protected XMLDTDContentModelHandler fDTDContentModelHandler;
0116:
0117: // state
0118:
0119: /** Scanner state. */
0120: protected int fScannerState;
0121:
0122: /** Standalone. */
0123: protected boolean fStandalone;
0124:
0125: /** Seen external DTD. */
0126: protected boolean fSeenExternalDTD;
0127:
0128: /** Seen a parameter entity reference. */
0129: protected boolean fSeenPEReferences;
0130:
0131: // private data
0132:
0133: /** Start DTD called. */
0134: private boolean fStartDTDCalled;
0135:
0136: /**
0137: * Stack of content operators (either '|' or ',') in children
0138: * content.
0139: */
0140: private int[] fContentStack = new int[5];
0141:
0142: /** Size of content stack. */
0143: private int fContentDepth;
0144:
0145: /** Parameter entity stack to check well-formedness. */
0146: private int[] fPEStack = new int[5];
0147:
0148: /** Parameter entity stack to report start/end entity calls. */
0149: private boolean[] fPEReport = new boolean[5];
0150:
0151: /** Number of opened parameter entities. */
0152: private int fPEDepth;
0153:
0154: /** Markup depth. */
0155: private int fMarkUpDepth;
0156:
0157: /** Number of opened external entities. */
0158: private int fExtEntityDepth;
0159:
0160: /** Number of opened include sections. */
0161: private int fIncludeSectDepth;
0162:
0163: // temporary variables
0164:
0165: /** Array of 3 strings. */
0166: private final String[] fStrings = new String[3];
0167:
0168: /** String. */
0169: private final XMLString fString = new XMLString();
0170:
0171: /** String buffer. */
0172: private final XMLStringBuffer fStringBuffer = new XMLStringBuffer();
0173:
0174: /** String buffer. */
0175: private final XMLStringBuffer fStringBuffer2 = new XMLStringBuffer();
0176:
0177: /** Literal text. */
0178: private final XMLString fLiteral = new XMLString();
0179:
0180: /** Literal text. */
0181: private final XMLString fLiteral2 = new XMLString();
0182:
0183: /** Enumeration values. */
0184: private String[] fEnumeration = new String[5];
0185:
0186: /** Enumeration values count. */
0187: private int fEnumerationCount;
0188:
0189: /** Ignore conditional section buffer. */
0190: private final XMLStringBuffer fIgnoreConditionalBuffer = new XMLStringBuffer(
0191: 128);
0192:
0193: //
0194: // Constructors
0195: //
0196:
0197: /** Default constructor. */
0198: public XMLDTDScannerImpl() {
0199: } // <init>()
0200:
0201: /** Constructor for he use of non-XMLComponentManagers. */
0202: public XMLDTDScannerImpl(SymbolTable symbolTable,
0203: XMLErrorReporter errorReporter,
0204: XMLEntityManager entityManager) {
0205: fSymbolTable = symbolTable;
0206: fErrorReporter = errorReporter;
0207: fEntityManager = entityManager;
0208: entityManager.setProperty(SYMBOL_TABLE, fSymbolTable);
0209: }
0210:
0211: //
0212: // XMLDTDScanner methods
0213: //
0214:
0215: /**
0216: * Sets the input source.
0217: *
0218: * @param inputSource The input source or null.
0219: *
0220: * @throws IOException Thrown on i/o error.
0221: */
0222: public void setInputSource(XMLInputSource inputSource)
0223: throws IOException {
0224: if (inputSource == null) {
0225: // no system id was available
0226: if (fDTDHandler != null) {
0227: fDTDHandler.startDTD(null, null);
0228: fDTDHandler.endDTD(null);
0229: }
0230: return;
0231: }
0232: fEntityManager.setEntityHandler(this );
0233: fEntityManager.startDTDEntity(inputSource);
0234: } // setInputSource(XMLInputSource)
0235:
0236: /**
0237: * Scans the external subset of the document.
0238: *
0239: * @param complete True if the scanner should scan the document
0240: * completely, pushing all events to the registered
0241: * document handler. A value of false indicates that
0242: * that the scanner should only scan the next portion
0243: * of the document and return. A scanner instance is
0244: * permitted to completely scan a document if it does
0245: * not support this "pull" scanning model.
0246: *
0247: * @return True if there is more to scan, false otherwise.
0248: */
0249: public boolean scanDTDExternalSubset(boolean complete)
0250: throws IOException, XNIException {
0251:
0252: fEntityManager.setEntityHandler(this );
0253: if (fScannerState == SCANNER_STATE_TEXT_DECL) {
0254: fSeenExternalDTD = true;
0255: boolean textDecl = scanTextDecl();
0256: if (fScannerState == SCANNER_STATE_END_OF_INPUT) {
0257: return false;
0258: } else {
0259: // next state is markup decls regardless of whether there
0260: // is a TextDecl or not
0261: setScannerState(SCANNER_STATE_MARKUP_DECL);
0262: if (textDecl && !complete) {
0263: return true;
0264: }
0265: }
0266: }
0267: // keep dispatching "events"
0268: do {
0269: if (!scanDecls(complete)) {
0270: return false;
0271: }
0272: } while (complete);
0273:
0274: // return that there is more to scan
0275: return true;
0276:
0277: } // scanDTDExternalSubset(boolean):boolean
0278:
0279: /**
0280: * Scans the internal subset of the document.
0281: *
0282: * @param complete True if the scanner should scan the document
0283: * completely, pushing all events to the registered
0284: * document handler. A value of false indicates that
0285: * that the scanner should only scan the next portion
0286: * of the document and return. A scanner instance is
0287: * permitted to completely scan a document if it does
0288: * not support this "pull" scanning model.
0289: * @param standalone True if the document was specified as standalone.
0290: * This value is important for verifying certain
0291: * well-formedness constraints.
0292: * @param hasExternalSubset True if the document has an external DTD.
0293: * This allows the scanner to properly notify
0294: * the handler of the end of the DTD in the
0295: * absence of an external subset.
0296: *
0297: * @return True if there is more to scan, false otherwise.
0298: */
0299: public boolean scanDTDInternalSubset(boolean complete,
0300: boolean standalone, boolean hasExternalSubset)
0301: throws IOException, XNIException {
0302: // reset entity scanner
0303: fEntityScanner = fEntityManager.getEntityScanner();
0304: fEntityManager.setEntityHandler(this );
0305: fStandalone = standalone;
0306: if (fScannerState == SCANNER_STATE_TEXT_DECL) {
0307: // call handler
0308: if (fDTDHandler != null) {
0309: fDTDHandler.startDTD(fEntityScanner, null);
0310: fStartDTDCalled = true;
0311: }
0312: // set starting state for internal subset
0313: setScannerState(SCANNER_STATE_MARKUP_DECL);
0314: }
0315: // keep dispatching "events"
0316: do {
0317: if (!scanDecls(complete)) {
0318: // call handler
0319: if (fDTDHandler != null && hasExternalSubset == false) {
0320: fDTDHandler.endDTD(null);
0321: }
0322: // we're done, set starting state for external subset
0323: setScannerState(SCANNER_STATE_TEXT_DECL);
0324: return false;
0325: }
0326: } while (complete);
0327:
0328: // return that there is more to scan
0329: return true;
0330:
0331: } // scanDTDInternalSubset(boolean,boolean,boolean):boolean
0332:
0333: //
0334: // XMLComponent methods
0335: //
0336:
0337: /**
0338: * reset
0339: *
0340: * @param componentManager
0341: */
0342: public void reset(XMLComponentManager componentManager)
0343: throws XMLConfigurationException {
0344:
0345: super .reset(componentManager);
0346: init();
0347:
0348: } // reset(XMLComponentManager)
0349:
0350: // this is made for something like XMLDTDLoader--XMLComponentManager-free operation...
0351: public void reset() {
0352: super .reset();
0353: init();
0354: }
0355:
0356: /**
0357: * Returns a list of feature identifiers that are recognized by
0358: * this component. This method may return null if no features
0359: * are recognized by this component.
0360: */
0361: public String[] getRecognizedFeatures() {
0362: return (String[]) (RECOGNIZED_FEATURES.clone());
0363: } // getRecognizedFeatures():String[]
0364:
0365: /**
0366: * Returns a list of property identifiers that are recognized by
0367: * this component. This method may return null if no properties
0368: * are recognized by this component.
0369: */
0370: public String[] getRecognizedProperties() {
0371: return (String[]) (RECOGNIZED_PROPERTIES.clone());
0372: } // getRecognizedProperties():String[]
0373:
0374: /**
0375: * Returns the default state for a feature, or null if this
0376: * component does not want to report a default value for this
0377: * feature.
0378: *
0379: * @param featureId The feature identifier.
0380: *
0381: * @since Xerces 2.2.0
0382: */
0383: public Boolean getFeatureDefault(String featureId) {
0384: for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
0385: if (RECOGNIZED_FEATURES[i].equals(featureId)) {
0386: return FEATURE_DEFAULTS[i];
0387: }
0388: }
0389: return null;
0390: } // getFeatureDefault(String):Boolean
0391:
0392: /**
0393: * Returns the default state for a property, or null if this
0394: * component does not want to report a default value for this
0395: * property.
0396: *
0397: * @param propertyId The property identifier.
0398: *
0399: * @since Xerces 2.2.0
0400: */
0401: public Object getPropertyDefault(String propertyId) {
0402: for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
0403: if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
0404: return PROPERTY_DEFAULTS[i];
0405: }
0406: }
0407: return null;
0408: } // getPropertyDefault(String):Object
0409:
0410: //
0411: // XMLDTDSource methods
0412: //
0413:
0414: /**
0415: * setDTDHandler
0416: *
0417: * @param dtdHandler
0418: */
0419: public void setDTDHandler(XMLDTDHandler dtdHandler) {
0420: fDTDHandler = dtdHandler;
0421: } // setDTDHandler(XMLDTDHandler)
0422:
0423: /**
0424: * getDTDHandler
0425: *
0426: * @return the XMLDTDHandler
0427: */
0428: public XMLDTDHandler getDTDHandler() {
0429: return fDTDHandler;
0430: } // getDTDHandler(): XMLDTDHandler
0431:
0432: //
0433: // XMLDTDContentModelSource methods
0434: //
0435:
0436: /**
0437: * setDTDContentModelHandler
0438: *
0439: * @param dtdContentModelHandler
0440: */
0441: public void setDTDContentModelHandler(
0442: XMLDTDContentModelHandler dtdContentModelHandler) {
0443: fDTDContentModelHandler = dtdContentModelHandler;
0444: } // setDTDContentModelHandler
0445:
0446: /**
0447: * getDTDContentModelHandler
0448: *
0449: * @return XMLDTDContentModelHandler
0450: */
0451: public XMLDTDContentModelHandler getDTDContentModelHandler() {
0452: return fDTDContentModelHandler;
0453: } // setDTDContentModelHandler
0454:
0455: //
0456: // XMLEntityHandler methods
0457: //
0458:
0459: /**
0460: * This method notifies of the start of an entity. The DTD has the
0461: * pseudo-name of "[dtd]" parameter entity names start with '%'; and
0462: * general entities are just specified by their name.
0463: *
0464: * @param name The name of the entity.
0465: * @param identifier The resource identifier.
0466: * @param encoding The auto-detected IANA encoding name of the entity
0467: * stream. This value will be null in those situations
0468: * where the entity encoding is not auto-detected (e.g.
0469: * internal entities or a document entity that is
0470: * parsed from a java.io.Reader).
0471: * @param augs Additional information that may include infoset augmentations
0472: *
0473: * @throws XNIException Thrown by handler to signal an error.
0474: */
0475: public void startEntity(String name,
0476: XMLResourceIdentifier identifier, String encoding,
0477: Augmentations augs) throws XNIException {
0478:
0479: super .startEntity(name, identifier, encoding, augs);
0480:
0481: boolean dtdEntity = name.equals("[dtd]");
0482: if (dtdEntity) {
0483: // call handler
0484: if (fDTDHandler != null && !fStartDTDCalled) {
0485: fDTDHandler.startDTD(fEntityScanner, null);
0486: }
0487: if (fDTDHandler != null) {
0488: fDTDHandler.startExternalSubset(identifier, null);
0489: }
0490: fEntityManager.startExternalSubset();
0491: fExtEntityDepth++;
0492: } else if (name.charAt(0) == '%') {
0493: pushPEStack(fMarkUpDepth, fReportEntity);
0494: if (fEntityScanner.isExternal()) {
0495: fExtEntityDepth++;
0496: }
0497: }
0498:
0499: // call handler
0500: if (fDTDHandler != null && !dtdEntity && fReportEntity) {
0501: fDTDHandler.startParameterEntity(name, identifier,
0502: encoding, augs);
0503: }
0504:
0505: } // startEntity(String,XMLResourceIdentifier,String)
0506:
0507: /**
0508: * This method notifies the end of an entity. The DTD has the pseudo-name
0509: * of "[dtd]" parameter entity names start with '%'; and general entities
0510: * are just specified by their name.
0511: *
0512: * @param name The name of the entity.
0513: * @param augs Additional information that may include infoset augmentations
0514: *
0515: * @throws XNIException Thrown by handler to signal an error.
0516: */
0517: public void endEntity(String name, Augmentations augs)
0518: throws XNIException {
0519:
0520: super .endEntity(name, augs);
0521:
0522: // if there is no data after the doctype
0523: //
0524: if (fScannerState == SCANNER_STATE_END_OF_INPUT)
0525: return;
0526:
0527: // Handle end of PE
0528: boolean reportEntity = fReportEntity;
0529: if (name.startsWith("%")) {
0530: reportEntity = peekReportEntity();
0531: // check well-formedness of the enity
0532: int startMarkUpDepth = popPEStack();
0533: // throw fatalError if this entity was incomplete and
0534: // was a freestanding decl
0535: if (startMarkUpDepth == 0
0536: && startMarkUpDepth < fMarkUpDepth) {
0537: fErrorReporter
0538: .reportError(
0539: XMLMessageFormatter.XML_DOMAIN,
0540: "ILL_FORMED_PARAMETER_ENTITY_WHEN_USED_IN_DECL",
0541: new Object[] { fEntityManager.fCurrentEntity.name },
0542: XMLErrorReporter.SEVERITY_FATAL_ERROR);
0543: }
0544: if (startMarkUpDepth != fMarkUpDepth) {
0545: reportEntity = false;
0546: if (fValidation) {
0547: // Proper nesting of parameter entities is a Validity Constraint
0548: // and must not be enforced when validation is off
0549: fErrorReporter.reportError(
0550: XMLMessageFormatter.XML_DOMAIN,
0551: "ImproperDeclarationNesting",
0552: new Object[] { name },
0553: XMLErrorReporter.SEVERITY_ERROR);
0554: }
0555: }
0556: if (fEntityScanner.isExternal()) {
0557: fExtEntityDepth--;
0558: }
0559: // call handler
0560: if (fDTDHandler != null && reportEntity) {
0561: fDTDHandler.endParameterEntity(name, augs);
0562: }
0563: }
0564: // end DTD
0565: else if (name.equals("[dtd]")) {
0566: if (fIncludeSectDepth != 0) {
0567: reportFatalError("IncludeSectUnterminated", null);
0568: }
0569: fScannerState = SCANNER_STATE_END_OF_INPUT;
0570: // call handler
0571: fEntityManager.endExternalSubset();
0572: if (fDTDHandler != null) {
0573: fDTDHandler.endExternalSubset(null);
0574: fDTDHandler.endDTD(null);
0575: }
0576: fExtEntityDepth--;
0577: }
0578:
0579: } // endEntity(String)
0580:
0581: // helper methods
0582:
0583: /**
0584: * Sets the scanner state.
0585: *
0586: * @param state The new scanner state.
0587: */
0588: protected final void setScannerState(int state) {
0589:
0590: fScannerState = state;
0591: if (DEBUG_SCANNER_STATE) {
0592: System.out.print("### setScannerState: ");
0593: System.out.print(getScannerStateName(state));
0594: System.out.println();
0595: }
0596:
0597: } // setScannerState(int)
0598:
0599: //
0600: // Private methods
0601: //
0602:
0603: /** Returns the scanner state name. */
0604: private static String getScannerStateName(int state) {
0605:
0606: if (DEBUG_SCANNER_STATE) {
0607: switch (state) {
0608: case SCANNER_STATE_END_OF_INPUT:
0609: return "SCANNER_STATE_END_OF_INPUT";
0610: case SCANNER_STATE_TEXT_DECL:
0611: return "SCANNER_STATE_TEXT_DECL";
0612: case SCANNER_STATE_MARKUP_DECL:
0613: return "SCANNER_STATE_MARKUP_DECL";
0614: }
0615: }
0616:
0617: return "??? (" + state + ')';
0618:
0619: } // getScannerStateName(int):String
0620:
0621: protected final boolean scanningInternalSubset() {
0622: return fExtEntityDepth == 0;
0623: }
0624:
0625: /**
0626: * start a parameter entity dealing with the textdecl if there is any
0627: *
0628: * @param name The name of the parameter entity to start (without the '%')
0629: * @param literal Whether this is happening within a literal
0630: */
0631: protected void startPE(String name, boolean literal)
0632: throws IOException, XNIException {
0633: int depth = fPEDepth;
0634: String pName = "%" + name;
0635: if (!fSeenPEReferences) {
0636: fSeenPEReferences = true;
0637: fEntityManager.notifyHasPEReferences();
0638: }
0639: if (fValidation && !fEntityManager.isDeclaredEntity(pName)) {
0640: fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
0641: "EntityNotDeclared", new Object[] { name },
0642: XMLErrorReporter.SEVERITY_ERROR);
0643: }
0644: fEntityManager.startEntity(fSymbolTable.addSymbol(pName),
0645: literal);
0646: // if we actually got a new entity and it's external
0647: // parse text decl if there is any
0648: if (depth != fPEDepth && fEntityScanner.isExternal()) {
0649: scanTextDecl();
0650: }
0651: }
0652:
0653: /**
0654: * Dispatch an XML "event".
0655: *
0656: * @return true if a TextDecl was scanned.
0657: *
0658: * @throws IOException Thrown on i/o error.
0659: * @throws XNIException Thrown on parse error.
0660: *
0661: */
0662: protected final boolean scanTextDecl() throws IOException,
0663: XNIException {
0664:
0665: // scan XMLDecl
0666: boolean textDecl = false;
0667: if (fEntityScanner.skipString("<?xml")) {
0668: fMarkUpDepth++;
0669: // NOTE: special case where document starts with a PI
0670: // whose name starts with "xml" (e.g. "xmlfoo")
0671: if (isValidNameChar(fEntityScanner.peekChar())) {
0672: fStringBuffer.clear();
0673: fStringBuffer.append("xml");
0674: if (fNamespaces) {
0675: while (isValidNCName(fEntityScanner.peekChar())) {
0676: fStringBuffer.append((char) fEntityScanner
0677: .scanChar());
0678: }
0679: } else {
0680: while (isValidNameChar(fEntityScanner.peekChar())) {
0681: fStringBuffer.append((char) fEntityScanner
0682: .scanChar());
0683: }
0684: }
0685: String target = fSymbolTable.addSymbol(
0686: fStringBuffer.ch, fStringBuffer.offset,
0687: fStringBuffer.length);
0688: scanPIData(target, fString);
0689: }
0690:
0691: // standard Text declaration
0692: else {
0693: // pseudo-attribute values
0694: String version = null;
0695: String encoding = null;
0696:
0697: scanXMLDeclOrTextDecl(true, fStrings);
0698: textDecl = true;
0699: fMarkUpDepth--;
0700:
0701: version = fStrings[0];
0702: encoding = fStrings[1];
0703:
0704: fEntityScanner.setXMLVersion(version);
0705: if (!fEntityScanner.fCurrentEntity
0706: .isEncodingExternallySpecified()) {
0707: fEntityScanner.setEncoding(encoding);
0708: }
0709:
0710: // call handler
0711: if (fDTDHandler != null) {
0712: fDTDHandler.textDecl(version, encoding, null);
0713: }
0714: }
0715: }
0716: fEntityManager.fCurrentEntity.mayReadChunks = true;
0717:
0718: return textDecl;
0719:
0720: } // scanTextDecl(boolean):boolean
0721:
0722: /**
0723: * Scans a processing data. This is needed to handle the situation
0724: * where a document starts with a processing instruction whose
0725: * target name <em>starts with</em> "xml". (e.g. xmlfoo)
0726: *
0727: * @param target The PI target
0728: * @param data The string to fill in with the data
0729: */
0730: protected final void scanPIData(String target, XMLString data)
0731: throws IOException, XNIException {
0732:
0733: super .scanPIData(target, data);
0734: fMarkUpDepth--;
0735:
0736: // call handler
0737: if (fDTDHandler != null) {
0738: fDTDHandler.processingInstruction(target, data, null);
0739: }
0740:
0741: } // scanPIData(String)
0742:
0743: /**
0744: * Scans a comment.
0745: * <p>
0746: * <pre>
0747: * [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
0748: * </pre>
0749: * <p>
0750: * <strong>Note:</strong> Called after scanning past '<!--'
0751: */
0752: protected final void scanComment() throws IOException, XNIException {
0753:
0754: fReportEntity = false;
0755: scanComment(fStringBuffer);
0756: fMarkUpDepth--;
0757:
0758: // call handler
0759: if (fDTDHandler != null) {
0760: fDTDHandler.comment(fStringBuffer, null);
0761: }
0762: fReportEntity = true;
0763:
0764: } // scanComment()
0765:
0766: /**
0767: * Scans an element declaration
0768: * <p>
0769: * <pre>
0770: * [45] elementdecl ::= '<!ELEMENT' S Name S contentspec S? '>'
0771: * [46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | children
0772: * </pre>
0773: * <p>
0774: * <strong>Note:</strong> Called after scanning past '<!ELEMENT'
0775: */
0776: protected final void scanElementDecl() throws IOException,
0777: XNIException {
0778:
0779: // spaces
0780: fReportEntity = false;
0781: if (!skipSeparator(true, !scanningInternalSubset())) {
0782: reportFatalError(
0783: "MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ELEMENTDECL",
0784: null);
0785: }
0786:
0787: // element name
0788: String name = fEntityScanner.scanName();
0789: if (name == null) {
0790: reportFatalError(
0791: "MSG_ELEMENT_TYPE_REQUIRED_IN_ELEMENTDECL", null);
0792: }
0793:
0794: // spaces
0795: if (!skipSeparator(true, !scanningInternalSubset())) {
0796: reportFatalError(
0797: "MSG_SPACE_REQUIRED_BEFORE_CONTENTSPEC_IN_ELEMENTDECL",
0798: new Object[] { name });
0799: }
0800:
0801: // content model
0802: if (fDTDContentModelHandler != null) {
0803: fDTDContentModelHandler.startContentModel(name, null);
0804: }
0805: String contentModel = null;
0806: fReportEntity = true;
0807: if (fEntityScanner.skipString("EMPTY")) {
0808: contentModel = "EMPTY";
0809: // call handler
0810: if (fDTDContentModelHandler != null) {
0811: fDTDContentModelHandler.empty(null);
0812: }
0813: } else if (fEntityScanner.skipString("ANY")) {
0814: contentModel = "ANY";
0815: // call handler
0816: if (fDTDContentModelHandler != null) {
0817: fDTDContentModelHandler.any(null);
0818: }
0819: } else {
0820: if (!fEntityScanner.skipChar('(')) {
0821: reportFatalError(
0822: "MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN",
0823: new Object[] { name });
0824: }
0825: if (fDTDContentModelHandler != null) {
0826: fDTDContentModelHandler.startGroup(null);
0827: }
0828: fStringBuffer.clear();
0829: fStringBuffer.append('(');
0830: fMarkUpDepth++;
0831: skipSeparator(false, !scanningInternalSubset());
0832:
0833: // Mixed content model
0834: if (fEntityScanner.skipString("#PCDATA")) {
0835: scanMixed(name);
0836: } else { // children content
0837: scanChildren(name);
0838: }
0839: contentModel = fStringBuffer.toString();
0840: }
0841:
0842: // call handler
0843: if (fDTDContentModelHandler != null) {
0844: fDTDContentModelHandler.endContentModel(null);
0845: }
0846:
0847: fReportEntity = false;
0848: skipSeparator(false, !scanningInternalSubset());
0849: // end
0850: if (!fEntityScanner.skipChar('>')) {
0851: reportFatalError("ElementDeclUnterminated",
0852: new Object[] { name });
0853: }
0854: fReportEntity = true;
0855: fMarkUpDepth--;
0856:
0857: // call handler
0858: if (fDTDHandler != null) {
0859: fDTDHandler.elementDecl(name, contentModel, null);
0860: }
0861:
0862: } // scanElementDecl()
0863:
0864: /**
0865: * scan Mixed content model
0866: * This assumes the content model has been parsed up to #PCDATA and
0867: * can simply append to fStringBuffer.
0868: * <pre>
0869: * [51] Mixed ::= '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*'
0870: * | '(' S? '#PCDATA' S? ')'
0871: * </pre>
0872: *
0873: * @param elName The element type name this declaration is about.
0874: *
0875: * <strong>Note:</strong> Called after scanning past '(#PCDATA'.
0876: */
0877: private final void scanMixed(String elName) throws IOException,
0878: XNIException {
0879:
0880: String childName = null;
0881:
0882: fStringBuffer.append("#PCDATA");
0883: // call handler
0884: if (fDTDContentModelHandler != null) {
0885: fDTDContentModelHandler.pcdata(null);
0886: }
0887: skipSeparator(false, !scanningInternalSubset());
0888: while (fEntityScanner.skipChar('|')) {
0889: fStringBuffer.append('|');
0890: // call handler
0891: if (fDTDContentModelHandler != null) {
0892: fDTDContentModelHandler.separator(
0893: XMLDTDContentModelHandler.SEPARATOR_CHOICE,
0894: null);
0895: }
0896: skipSeparator(false, !scanningInternalSubset());
0897:
0898: childName = fEntityScanner.scanName();
0899: if (childName == null) {
0900: reportFatalError(
0901: "MSG_ELEMENT_TYPE_REQUIRED_IN_MIXED_CONTENT",
0902: new Object[] { elName });
0903: }
0904: fStringBuffer.append(childName);
0905: // call handler
0906: if (fDTDContentModelHandler != null) {
0907: fDTDContentModelHandler.element(childName, null);
0908: }
0909: skipSeparator(false, !scanningInternalSubset());
0910: }
0911: // The following check must be done in a single call (as opposed to one
0912: // for ')' and then one for '*') to guarantee that callbacks are
0913: // properly nested. We do not want to trigger endEntity too early in
0914: // case we cross the boundary of an entity between the two characters.
0915: if (fEntityScanner.skipString(")*")) {
0916: fStringBuffer.append(")*");
0917: // call handler
0918: if (fDTDContentModelHandler != null) {
0919: fDTDContentModelHandler.endGroup(null);
0920: fDTDContentModelHandler.occurrence(
0921: XMLDTDContentModelHandler.OCCURS_ZERO_OR_MORE,
0922: null);
0923: }
0924: } else if (childName != null) {
0925: reportFatalError("MixedContentUnterminated",
0926: new Object[] { elName });
0927: } else if (fEntityScanner.skipChar(')')) {
0928: fStringBuffer.append(')');
0929: // call handler
0930: if (fDTDContentModelHandler != null) {
0931: fDTDContentModelHandler.endGroup(null);
0932: }
0933: } else {
0934: reportFatalError("MSG_CLOSE_PAREN_REQUIRED_IN_CHILDREN",
0935: new Object[] { elName });
0936: }
0937: fMarkUpDepth--;
0938: // we are done
0939: }
0940:
0941: /**
0942: * scan children content model
0943: * This assumes it can simply append to fStringBuffer.
0944: * <pre>
0945: * [47] children ::= (choice | seq) ('?' | '*' | '+')?
0946: * [48] cp ::= (Name | choice | seq) ('?' | '*' | '+')?
0947: * [49] choice ::= '(' S? cp ( S? '|' S? cp )+ S? ')'
0948: * [50] seq ::= '(' S? cp ( S? ',' S? cp )* S? ')'
0949: * </pre>
0950: *
0951: * @param elName The element type name this declaration is about.
0952: *
0953: * <strong>Note:</strong> Called after scanning past the first open
0954: * paranthesis.
0955: */
0956: private final void scanChildren(String elName) throws IOException,
0957: XNIException {
0958:
0959: fContentDepth = 0;
0960: pushContentStack(0);
0961: int currentOp = 0;
0962: int c;
0963: while (true) {
0964: if (fEntityScanner.skipChar('(')) {
0965: fMarkUpDepth++;
0966: fStringBuffer.append('(');
0967: // call handler
0968: if (fDTDContentModelHandler != null) {
0969: fDTDContentModelHandler.startGroup(null);
0970: }
0971: // push current op on stack and reset it
0972: pushContentStack(currentOp);
0973: currentOp = 0;
0974: skipSeparator(false, !scanningInternalSubset());
0975: continue;
0976: }
0977: skipSeparator(false, !scanningInternalSubset());
0978: String childName = fEntityScanner.scanName();
0979: if (childName == null) {
0980: reportFatalError(
0981: "MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN",
0982: new Object[] { elName });
0983: return;
0984: }
0985: // call handler
0986: if (fDTDContentModelHandler != null) {
0987: fDTDContentModelHandler.element(childName, null);
0988: }
0989: fStringBuffer.append(childName);
0990: c = fEntityScanner.peekChar();
0991: if (c == '?' || c == '*' || c == '+') {
0992: // call handler
0993: if (fDTDContentModelHandler != null) {
0994: short oc;
0995: if (c == '?') {
0996: oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_ONE;
0997: } else if (c == '*') {
0998: oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_MORE;
0999: } else {
1000: oc = XMLDTDContentModelHandler.OCCURS_ONE_OR_MORE;
1001: }
1002: fDTDContentModelHandler.occurrence(oc, null);
1003: }
1004: fEntityScanner.scanChar();
1005: fStringBuffer.append((char) c);
1006: }
1007: while (true) {
1008: skipSeparator(false, !scanningInternalSubset());
1009: c = fEntityScanner.peekChar();
1010: if (c == ',' && currentOp != '|') {
1011: currentOp = c;
1012: // call handler
1013: if (fDTDContentModelHandler != null) {
1014: fDTDContentModelHandler
1015: .separator(
1016: XMLDTDContentModelHandler.SEPARATOR_SEQUENCE,
1017: null);
1018: }
1019: fEntityScanner.scanChar();
1020: fStringBuffer.append(',');
1021: break;
1022: } else if (c == '|' && currentOp != ',') {
1023: currentOp = c;
1024: // call handler
1025: if (fDTDContentModelHandler != null) {
1026: fDTDContentModelHandler
1027: .separator(
1028: XMLDTDContentModelHandler.SEPARATOR_CHOICE,
1029: null);
1030: }
1031: fEntityScanner.scanChar();
1032: fStringBuffer.append('|');
1033: break;
1034: } else if (c != ')') {
1035: reportFatalError(
1036: "MSG_CLOSE_PAREN_REQUIRED_IN_CHILDREN",
1037: new Object[] { elName });
1038: }
1039: // call handler
1040: if (fDTDContentModelHandler != null) {
1041: fDTDContentModelHandler.endGroup(null);
1042: }
1043: // restore previous op
1044: currentOp = popContentStack();
1045: short oc;
1046: // The following checks must be done in a single call (as
1047: // opposed to one for ')' and then one for '?', '*', and '+')
1048: // to guarantee that callbacks are properly nested. We do not
1049: // want to trigger endEntity too early in case we cross the
1050: // boundary of an entity between the two characters.
1051: if (fEntityScanner.skipString(")?")) {
1052: fStringBuffer.append(")?");
1053: // call handler
1054: if (fDTDContentModelHandler != null) {
1055: oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_ONE;
1056: fDTDContentModelHandler.occurrence(oc, null);
1057: }
1058: } else if (fEntityScanner.skipString(")+")) {
1059: fStringBuffer.append(")+");
1060: // call handler
1061: if (fDTDContentModelHandler != null) {
1062: oc = XMLDTDContentModelHandler.OCCURS_ONE_OR_MORE;
1063: fDTDContentModelHandler.occurrence(oc, null);
1064: }
1065: } else if (fEntityScanner.skipString(")*")) {
1066: fStringBuffer.append(")*");
1067: // call handler
1068: if (fDTDContentModelHandler != null) {
1069: oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_MORE;
1070: fDTDContentModelHandler.occurrence(oc, null);
1071: }
1072: } else {
1073: // no occurrence specified
1074: fEntityScanner.scanChar();
1075: fStringBuffer.append(')');
1076: }
1077: fMarkUpDepth--;
1078: if (fContentDepth == 0) {
1079: return;
1080: }
1081: }
1082: skipSeparator(false, !scanningInternalSubset());
1083: }
1084: }
1085:
1086: /**
1087: * Scans an attlist declaration
1088: * <p>
1089: * <pre>
1090: * [52] AttlistDecl ::= '<!ATTLIST' S Name AttDef* S? '>'
1091: * [53] AttDef ::= S Name S AttType S DefaultDecl
1092: * </pre>
1093: * <p>
1094: * <strong>Note:</strong> Called after scanning past '<!ATTLIST'
1095: */
1096: protected final void scanAttlistDecl() throws IOException,
1097: XNIException {
1098:
1099: // spaces
1100: fReportEntity = false;
1101: if (!skipSeparator(true, !scanningInternalSubset())) {
1102: reportFatalError(
1103: "MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ATTLISTDECL",
1104: null);
1105: }
1106:
1107: // element name
1108: String elName = fEntityScanner.scanName();
1109: if (elName == null) {
1110: reportFatalError(
1111: "MSG_ELEMENT_TYPE_REQUIRED_IN_ATTLISTDECL", null);
1112: }
1113:
1114: // call handler
1115: if (fDTDHandler != null) {
1116: fDTDHandler.startAttlist(elName, null);
1117: }
1118:
1119: // spaces
1120: if (!skipSeparator(true, !scanningInternalSubset())) {
1121: // no space, is it the end yet?
1122: if (fEntityScanner.skipChar('>')) {
1123: // yes, stop here
1124: // call handler
1125: if (fDTDHandler != null) {
1126: fDTDHandler.endAttlist(null);
1127: }
1128: fMarkUpDepth--;
1129: return;
1130: } else {
1131: reportFatalError(
1132: "MSG_SPACE_REQUIRED_BEFORE_ATTRIBUTE_NAME_IN_ATTDEF",
1133: new Object[] { elName });
1134: }
1135: }
1136:
1137: // definitions
1138: while (!fEntityScanner.skipChar('>')) {
1139: String name = fEntityScanner.scanName();
1140: if (name == null) {
1141: reportFatalError("AttNameRequiredInAttDef",
1142: new Object[] { elName });
1143: }
1144: // spaces
1145: if (!skipSeparator(true, !scanningInternalSubset())) {
1146: reportFatalError(
1147: "MSG_SPACE_REQUIRED_BEFORE_ATTTYPE_IN_ATTDEF",
1148: new Object[] { elName, name });
1149: }
1150: // type
1151: String type = scanAttType(elName, name);
1152:
1153: // spaces
1154: if (!skipSeparator(true, !scanningInternalSubset())) {
1155: reportFatalError(
1156: "MSG_SPACE_REQUIRED_BEFORE_DEFAULTDECL_IN_ATTDEF",
1157: new Object[] { elName, name });
1158: }
1159:
1160: // default decl
1161: String defaultType = scanAttDefaultDecl(elName, name, type,
1162: fLiteral, fLiteral2);
1163: // REVISIT: Should we do anything with the non-normalized
1164: // default attribute value? -Ac
1165: // yes--according to bug 5073. - neilg
1166:
1167: // call handler
1168: if (fDTDHandler != null) {
1169: String[] enumeration = null;
1170: if (fEnumerationCount != 0) {
1171: enumeration = new String[fEnumerationCount];
1172: System.arraycopy(fEnumeration, 0, enumeration, 0,
1173: fEnumerationCount);
1174: }
1175: // Determine whether the default value to be passed should be null.
1176: // REVISIT: should probably check whether fLiteral.ch is null instead. LM.
1177: if (defaultType != null
1178: && (defaultType.equals("#REQUIRED") || defaultType
1179: .equals("#IMPLIED"))) {
1180: fDTDHandler.attributeDecl(elName, name, type,
1181: enumeration, defaultType, null, null, null);
1182: } else {
1183: fDTDHandler.attributeDecl(elName, name, type,
1184: enumeration, defaultType, fLiteral,
1185: fLiteral2, null);
1186: }
1187: }
1188: skipSeparator(false, !scanningInternalSubset());
1189: }
1190:
1191: // call handler
1192: if (fDTDHandler != null) {
1193: fDTDHandler.endAttlist(null);
1194: }
1195: fMarkUpDepth--;
1196: fReportEntity = true;
1197:
1198: } // scanAttlistDecl()
1199:
1200: /**
1201: * Scans an attribute type definition
1202: * <p>
1203: * <pre>
1204: * [54] AttType ::= StringType | TokenizedType | EnumeratedType
1205: * [55] StringType ::= 'CDATA'
1206: * [56] TokenizedType ::= 'ID'
1207: * | 'IDREF'
1208: * | 'IDREFS'
1209: * | 'ENTITY'
1210: * | 'ENTITIES'
1211: * | 'NMTOKEN'
1212: * | 'NMTOKENS'
1213: * [57] EnumeratedType ::= NotationType | Enumeration
1214: * [58] NotationType ::= 'NOTATION' S '(' S? Name (S? '|' S? Name)* S? ')'
1215: * [59] Enumeration ::= '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')'
1216: * </pre>
1217: * <p>
1218: * <strong>Note:</strong> Called after scanning past '<!ATTLIST'
1219: *
1220: * @param elName The element type name this declaration is about.
1221: * @param atName The attribute name this declaration is about.
1222: */
1223: private final String scanAttType(String elName, String atName)
1224: throws IOException, XNIException {
1225:
1226: String type = null;
1227: fEnumerationCount = 0;
1228: /*
1229: * Watchout: the order here is important: when a string happens to
1230: * be a substring of another string, the longer one needs to be
1231: * looked for first!!
1232: */
1233: if (fEntityScanner.skipString("CDATA")) {
1234: type = "CDATA";
1235: } else if (fEntityScanner.skipString("IDREFS")) {
1236: type = "IDREFS";
1237: } else if (fEntityScanner.skipString("IDREF")) {
1238: type = "IDREF";
1239: } else if (fEntityScanner.skipString("ID")) {
1240: type = "ID";
1241: } else if (fEntityScanner.skipString("ENTITY")) {
1242: type = "ENTITY";
1243: } else if (fEntityScanner.skipString("ENTITIES")) {
1244: type = "ENTITIES";
1245: } else if (fEntityScanner.skipString("NMTOKENS")) {
1246: type = "NMTOKENS";
1247: } else if (fEntityScanner.skipString("NMTOKEN")) {
1248: type = "NMTOKEN";
1249: } else if (fEntityScanner.skipString("NOTATION")) {
1250: type = "NOTATION";
1251: // spaces
1252: if (!skipSeparator(true, !scanningInternalSubset())) {
1253: reportFatalError(
1254: "MSG_SPACE_REQUIRED_AFTER_NOTATION_IN_NOTATIONTYPE",
1255: new Object[] { elName, atName });
1256: }
1257: // open paren
1258: int c = fEntityScanner.scanChar();
1259: if (c != '(') {
1260: reportFatalError(
1261: "MSG_OPEN_PAREN_REQUIRED_IN_NOTATIONTYPE",
1262: new Object[] { elName, atName });
1263: }
1264: fMarkUpDepth++;
1265: do {
1266: skipSeparator(false, !scanningInternalSubset());
1267: String aName = fEntityScanner.scanName();
1268: if (aName == null) {
1269: reportFatalError(
1270: "MSG_NAME_REQUIRED_IN_NOTATIONTYPE",
1271: new Object[] { elName, atName });
1272: c = skipInvalidEnumerationValue();
1273: if (c == '|') {
1274: continue;
1275: }
1276: break;
1277: }
1278: ensureEnumerationSize(fEnumerationCount + 1);
1279: fEnumeration[fEnumerationCount++] = aName;
1280: skipSeparator(false, !scanningInternalSubset());
1281: c = fEntityScanner.scanChar();
1282: } while (c == '|');
1283: if (c != ')') {
1284: reportFatalError("NotationTypeUnterminated",
1285: new Object[] { elName, atName });
1286: }
1287: fMarkUpDepth--;
1288: } else { // Enumeration
1289: type = "ENUMERATION";
1290: // open paren
1291: int c = fEntityScanner.scanChar();
1292: if (c != '(') {
1293: // "OPEN_PAREN_REQUIRED_BEFORE_ENUMERATION_IN_ATTRDECL",
1294: reportFatalError("AttTypeRequiredInAttDef",
1295: new Object[] { elName, atName });
1296: }
1297: fMarkUpDepth++;
1298: do {
1299: skipSeparator(false, !scanningInternalSubset());
1300: String token = fEntityScanner.scanNmtoken();
1301: if (token == null) {
1302: reportFatalError(
1303: "MSG_NMTOKEN_REQUIRED_IN_ENUMERATION",
1304: new Object[] { elName, atName });
1305: c = skipInvalidEnumerationValue();
1306: if (c == '|') {
1307: continue;
1308: }
1309: break;
1310: }
1311: ensureEnumerationSize(fEnumerationCount + 1);
1312: fEnumeration[fEnumerationCount++] = token;
1313: skipSeparator(false, !scanningInternalSubset());
1314: c = fEntityScanner.scanChar();
1315: } while (c == '|');
1316: if (c != ')') {
1317: reportFatalError("EnumerationUnterminated",
1318: new Object[] { elName, atName });
1319: }
1320: fMarkUpDepth--;
1321: }
1322: return type;
1323:
1324: } // scanAttType():String
1325:
1326: /**
1327: * Scans an attribute default declaration
1328: * <p>
1329: * <pre>
1330: * [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue)
1331: * </pre>
1332: *
1333: * @param elName
1334: * @param atName The name of the attribute being scanned.
1335: * @param type
1336: * @param defaultVal The string to fill in with the default value.
1337: * @param nonNormalizedDefaultVal
1338: */
1339: protected final String scanAttDefaultDecl(String elName,
1340: String atName, String type, XMLString defaultVal,
1341: XMLString nonNormalizedDefaultVal) throws IOException,
1342: XNIException {
1343:
1344: String defaultType = null;
1345: fString.clear();
1346: defaultVal.clear();
1347: if (fEntityScanner.skipString("#REQUIRED")) {
1348: defaultType = "#REQUIRED";
1349: } else if (fEntityScanner.skipString("#IMPLIED")) {
1350: defaultType = "#IMPLIED";
1351: } else {
1352: if (fEntityScanner.skipString("#FIXED")) {
1353: defaultType = "#FIXED";
1354: // spaces
1355: if (!skipSeparator(true, !scanningInternalSubset())) {
1356: reportFatalError(
1357: "MSG_SPACE_REQUIRED_AFTER_FIXED_IN_DEFAULTDECL",
1358: new Object[] { elName, atName });
1359: }
1360: }
1361: // AttValue
1362: boolean isVC = !fStandalone
1363: && (fSeenExternalDTD || fSeenPEReferences);
1364: scanAttributeValue(defaultVal, nonNormalizedDefaultVal,
1365: atName, isVC, elName);
1366: }
1367: return defaultType;
1368:
1369: } // ScanAttDefaultDecl
1370:
1371: /**
1372: * Scans an entity declaration
1373: * <p>
1374: * <pre>
1375: * [70] EntityDecl ::= GEDecl | PEDecl
1376: * [71] GEDecl ::= '<!ENTITY' S Name S EntityDef S? '>'
1377: * [72] PEDecl ::= '<!ENTITY' S '%' S Name S PEDef S? '>'
1378: * [73] EntityDef ::= EntityValue | (ExternalID NDataDecl?)
1379: * [74] PEDef ::= EntityValue | ExternalID
1380: * [75] ExternalID ::= 'SYSTEM' S SystemLiteral
1381: * | 'PUBLIC' S PubidLiteral S SystemLiteral
1382: * [76] NDataDecl ::= S 'NDATA' S Name
1383: * </pre>
1384: * <p>
1385: * <strong>Note:</strong> Called after scanning past '<!ENTITY'
1386: */
1387: private final void scanEntityDecl() throws IOException,
1388: XNIException {
1389:
1390: boolean isPEDecl = false;
1391: boolean sawPERef = false;
1392: fReportEntity = false;
1393: if (fEntityScanner.skipSpaces()) {
1394: if (!fEntityScanner.skipChar('%')) {
1395: isPEDecl = false; // <!ENTITY x "x">
1396: } else if (skipSeparator(true, !scanningInternalSubset())) {
1397: // <!ENTITY % x "x">
1398: isPEDecl = true;
1399: } else if (scanningInternalSubset()) {
1400: reportFatalError(
1401: "MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL",
1402: null);
1403: isPEDecl = true;
1404: } else if (fEntityScanner.peekChar() == '%') {
1405: // <!ENTITY %%x; "x"> is legal
1406: skipSeparator(false, !scanningInternalSubset());
1407: isPEDecl = true;
1408: } else {
1409: sawPERef = true;
1410: }
1411: } else if (scanningInternalSubset()
1412: || !fEntityScanner.skipChar('%')) {
1413: // <!ENTITY[^ ]...> or <!ENTITY[^ %]...>
1414: reportFatalError(
1415: "MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL",
1416: null);
1417: isPEDecl = false;
1418: } else if (fEntityScanner.skipSpaces()) {
1419: // <!ENTITY% ...>
1420: reportFatalError(
1421: "MSG_SPACE_REQUIRED_BEFORE_PERCENT_IN_PEDECL", null);
1422: isPEDecl = false;
1423: } else {
1424: sawPERef = true;
1425: }
1426: if (sawPERef) {
1427: while (true) {
1428: String peName = fEntityScanner.scanName();
1429: if (peName == null) {
1430: reportFatalError("NameRequiredInPEReference", null);
1431: } else if (!fEntityScanner.skipChar(';')) {
1432: reportFatalError("SemicolonRequiredInPEReference",
1433: new Object[] { peName });
1434: } else {
1435: startPE(peName, false);
1436: }
1437: fEntityScanner.skipSpaces();
1438: if (!fEntityScanner.skipChar('%'))
1439: break;
1440: if (!isPEDecl) {
1441: if (skipSeparator(true, !scanningInternalSubset())) {
1442: isPEDecl = true;
1443: break;
1444: }
1445: isPEDecl = fEntityScanner.skipChar('%');
1446: }
1447: }
1448: }
1449:
1450: // name
1451: String name = null;
1452: if (fNamespaces) {
1453: name = fEntityScanner.scanNCName();
1454: } else {
1455: name = fEntityScanner.scanName();
1456: }
1457: if (name == null) {
1458: reportFatalError("MSG_ENTITY_NAME_REQUIRED_IN_ENTITYDECL",
1459: null);
1460: }
1461: // spaces
1462: if (!skipSeparator(true, !scanningInternalSubset())) {
1463: if (fNamespaces && fEntityScanner.peekChar() == ':') {
1464: fEntityScanner.scanChar();
1465: XMLStringBuffer colonName = new XMLStringBuffer(name);
1466: colonName.append(":");
1467: String str = fEntityScanner.scanName();
1468: if (str != null)
1469: colonName.append(str);
1470: reportFatalError("ColonNotLegalWithNS",
1471: new Object[] { colonName.toString() });
1472: if (!skipSeparator(true, !scanningInternalSubset())) {
1473: reportFatalError(
1474: "MSG_SPACE_REQUIRED_AFTER_ENTITY_NAME_IN_ENTITYDECL",
1475: new Object[] { name });
1476: }
1477: } else {
1478: reportFatalError(
1479: "MSG_SPACE_REQUIRED_AFTER_ENTITY_NAME_IN_ENTITYDECL",
1480: new Object[] { name });
1481: }
1482: }
1483:
1484: // external id
1485: scanExternalID(fStrings, false);
1486: String systemId = fStrings[0];
1487: String publicId = fStrings[1];
1488:
1489: String notation = null;
1490: // NDATA
1491: boolean sawSpace = skipSeparator(true,
1492: !scanningInternalSubset());
1493: if (!isPEDecl && fEntityScanner.skipString("NDATA")) {
1494: // check whether there was space before NDATA
1495: if (!sawSpace) {
1496: reportFatalError(
1497: "MSG_SPACE_REQUIRED_BEFORE_NDATA_IN_UNPARSED_ENTITYDECL",
1498: new Object[] { name });
1499: }
1500:
1501: // spaces
1502: if (!skipSeparator(true, !scanningInternalSubset())) {
1503: reportFatalError(
1504: "MSG_SPACE_REQUIRED_BEFORE_NOTATION_NAME_IN_UNPARSED_ENTITYDECL",
1505: new Object[] { name });
1506: }
1507: notation = fEntityScanner.scanName();
1508: if (notation == null) {
1509: reportFatalError(
1510: "MSG_NOTATION_NAME_REQUIRED_FOR_UNPARSED_ENTITYDECL",
1511: new Object[] { name });
1512: }
1513: }
1514:
1515: // internal entity
1516: if (systemId == null) {
1517: scanEntityValue(fLiteral, fLiteral2);
1518: // since we need it's value anyway, let's snag it so it doesn't get corrupted
1519: // if a new load takes place before we store the entity values
1520: fStringBuffer.clear();
1521: fStringBuffer2.clear();
1522: fStringBuffer.append(fLiteral.ch, fLiteral.offset,
1523: fLiteral.length);
1524: fStringBuffer2.append(fLiteral2.ch, fLiteral2.offset,
1525: fLiteral2.length);
1526: }
1527:
1528: // skip possible trailing space
1529: skipSeparator(false, !scanningInternalSubset());
1530:
1531: // end
1532: if (!fEntityScanner.skipChar('>')) {
1533: reportFatalError("EntityDeclUnterminated",
1534: new Object[] { name });
1535: }
1536: fMarkUpDepth--;
1537:
1538: // register entity and make callback
1539: if (isPEDecl) {
1540: name = "%" + name;
1541: }
1542: if (systemId != null) {
1543: String baseSystemId = fEntityScanner.getBaseSystemId();
1544: if (notation != null) {
1545: fEntityManager.addUnparsedEntity(name, publicId,
1546: systemId, baseSystemId, notation);
1547: } else {
1548: fEntityManager.addExternalEntity(name, publicId,
1549: systemId, baseSystemId);
1550: }
1551: if (fDTDHandler != null) {
1552: fResourceIdentifier.setValues(publicId, systemId,
1553: baseSystemId, XMLEntityManager.expandSystemId(
1554: systemId, baseSystemId, false));
1555: if (notation != null) {
1556: fDTDHandler.unparsedEntityDecl(name,
1557: fResourceIdentifier, notation, null);
1558: } else {
1559: fDTDHandler.externalEntityDecl(name,
1560: fResourceIdentifier, null);
1561: }
1562: }
1563: } else {
1564: fEntityManager.addInternalEntity(name, fStringBuffer
1565: .toString());
1566: if (fDTDHandler != null) {
1567: fDTDHandler.internalEntityDecl(name, fStringBuffer,
1568: fStringBuffer2, null);
1569: }
1570: }
1571: fReportEntity = true;
1572:
1573: } // scanEntityDecl()
1574:
1575: /**
1576: * Scans an entity value.
1577: *
1578: * @param value The string to fill in with the value.
1579: * @param nonNormalizedValue The string to fill in with the
1580: * non-normalized value.
1581: *
1582: * <strong>Note:</strong> This method uses fString, fStringBuffer (through
1583: * the use of scanCharReferenceValue), and fStringBuffer2, anything in them
1584: * at the time of calling is lost.
1585: */
1586: protected final void scanEntityValue(XMLString value,
1587: XMLString nonNormalizedValue) throws IOException,
1588: XNIException {
1589: int quote = fEntityScanner.scanChar();
1590: if (quote != '\'' && quote != '"') {
1591: reportFatalError("OpenQuoteMissingInDecl", null);
1592: }
1593: // store at which depth of entities we start
1594: int entityDepth = fEntityDepth;
1595:
1596: XMLString literal = fString;
1597: XMLString literal2 = fString;
1598: if (fEntityScanner.scanLiteral(quote, fString) != quote) {
1599: fStringBuffer.clear();
1600: fStringBuffer2.clear();
1601: do {
1602: fStringBuffer.append(fString);
1603: fStringBuffer2.append(fString);
1604: if (fEntityScanner.skipChar('&')) {
1605: if (fEntityScanner.skipChar('#')) {
1606: fStringBuffer2.append("&#");
1607: scanCharReferenceValue(fStringBuffer,
1608: fStringBuffer2);
1609: } else {
1610: fStringBuffer.append('&');
1611: fStringBuffer2.append('&');
1612: String eName = fEntityScanner.scanName();
1613: if (eName == null) {
1614: reportFatalError("NameRequiredInReference",
1615: null);
1616: } else {
1617: fStringBuffer.append(eName);
1618: fStringBuffer2.append(eName);
1619: }
1620: if (!fEntityScanner.skipChar(';')) {
1621: reportFatalError(
1622: "SemicolonRequiredInReference",
1623: new Object[] { eName });
1624: } else {
1625: fStringBuffer.append(';');
1626: fStringBuffer2.append(';');
1627: }
1628: }
1629: } else if (fEntityScanner.skipChar('%')) {
1630: while (true) {
1631: fStringBuffer2.append('%');
1632: String peName = fEntityScanner.scanName();
1633: if (peName == null) {
1634: reportFatalError(
1635: "NameRequiredInPEReference", null);
1636: } else if (!fEntityScanner.skipChar(';')) {
1637: reportFatalError(
1638: "SemicolonRequiredInPEReference",
1639: new Object[] { peName });
1640: } else {
1641: if (scanningInternalSubset()) {
1642: reportFatalError(
1643: "PEReferenceWithinMarkup",
1644: new Object[] { peName });
1645: }
1646: fStringBuffer2.append(peName);
1647: fStringBuffer2.append(';');
1648: }
1649: startPE(peName, true);
1650: // REVISIT: [Q] Why do we skip spaces here? -Ac
1651: // REVISIT: This will make returning the non-
1652: // normalized value harder. -Ac
1653: fEntityScanner.skipSpaces();
1654: if (!fEntityScanner.skipChar('%'))
1655: break;
1656: }
1657: } else {
1658: int c = fEntityScanner.peekChar();
1659: if (XMLChar.isHighSurrogate(c)) {
1660: scanSurrogates(fStringBuffer2);
1661: } else if (isInvalidLiteral(c)) {
1662: reportFatalError("InvalidCharInLiteral",
1663: new Object[] { Integer.toHexString(c) });
1664: fEntityScanner.scanChar();
1665: }
1666: // if it's not the delimiting quote or if it is but from a
1667: // different entity than the one this literal started from,
1668: // simply append the character to our buffer
1669: else if (c != quote || entityDepth != fEntityDepth) {
1670: fStringBuffer.append((char) c);
1671: fStringBuffer2.append((char) c);
1672: fEntityScanner.scanChar();
1673: }
1674: }
1675: } while (fEntityScanner.scanLiteral(quote, fString) != quote);
1676: fStringBuffer.append(fString);
1677: fStringBuffer2.append(fString);
1678: literal = fStringBuffer;
1679: literal2 = fStringBuffer2;
1680: }
1681: value.setValues(literal);
1682: nonNormalizedValue.setValues(literal2);
1683: if (!fEntityScanner.skipChar(quote)) {
1684: reportFatalError("CloseQuoteMissingInDecl", null);
1685: }
1686: } // scanEntityValue(XMLString,XMLString):void
1687:
1688: /**
1689: * Scans a notation declaration
1690: * <p>
1691: * <pre>
1692: * [82] NotationDecl ::= '<!NOTATION' S Name S (ExternalID|PublicID) S? '>'
1693: * [83] PublicID ::= 'PUBLIC' S PubidLiteral
1694: * </pre>
1695: * <p>
1696: * <strong>Note:</strong> Called after scanning past '<!NOTATION'
1697: */
1698: private final void scanNotationDecl() throws IOException,
1699: XNIException {
1700:
1701: // spaces
1702: fReportEntity = false;
1703: if (!skipSeparator(true, !scanningInternalSubset())) {
1704: reportFatalError(
1705: "MSG_SPACE_REQUIRED_BEFORE_NOTATION_NAME_IN_NOTATIONDECL",
1706: null);
1707: }
1708:
1709: // notation name
1710: String name = null;
1711: if (fNamespaces) {
1712: name = fEntityScanner.scanNCName();
1713: } else {
1714: name = fEntityScanner.scanName();
1715: }
1716: if (name == null) {
1717: reportFatalError(
1718: "MSG_NOTATION_NAME_REQUIRED_IN_NOTATIONDECL", null);
1719: }
1720:
1721: // spaces
1722: if (!skipSeparator(true, !scanningInternalSubset())) {
1723: // check for invalid ":"
1724: if (fNamespaces && fEntityScanner.peekChar() == ':') {
1725: fEntityScanner.scanChar();
1726: XMLStringBuffer colonName = new XMLStringBuffer(name);
1727: colonName.append(":");
1728: colonName.append(fEntityScanner.scanName());
1729: reportFatalError("ColonNotLegalWithNS",
1730: new Object[] { colonName.toString() });
1731: skipSeparator(true, !scanningInternalSubset());
1732: } else {
1733: reportFatalError(
1734: "MSG_SPACE_REQUIRED_AFTER_NOTATION_NAME_IN_NOTATIONDECL",
1735: new Object[] { name });
1736: }
1737: }
1738:
1739: // external id
1740: scanExternalID(fStrings, true);
1741: String systemId = fStrings[0];
1742: String publicId = fStrings[1];
1743: String baseSystemId = fEntityScanner.getBaseSystemId();
1744:
1745: if (systemId == null && publicId == null) {
1746: reportFatalError("ExternalIDorPublicIDRequired",
1747: new Object[] { name });
1748: }
1749:
1750: // skip possible trailing space
1751: skipSeparator(false, !scanningInternalSubset());
1752:
1753: // end
1754: if (!fEntityScanner.skipChar('>')) {
1755: reportFatalError("NotationDeclUnterminated",
1756: new Object[] { name });
1757: }
1758: fMarkUpDepth--;
1759:
1760: // call handler
1761: if (fDTDHandler != null) {
1762: fResourceIdentifier.setValues(publicId, systemId,
1763: baseSystemId, XMLEntityManager.expandSystemId(
1764: systemId, baseSystemId, false));
1765: fDTDHandler.notationDecl(name, fResourceIdentifier, null);
1766: }
1767: fReportEntity = true;
1768:
1769: } // scanNotationDecl()
1770:
1771: /**
1772: * Scans a conditional section. If it's a section to ignore the whole
1773: * section gets scanned through and this method only returns after the
1774: * closing bracket has been found. When it's an include section though, it
1775: * returns to let the main loop take care of scanning it. In that case the
1776: * end of the section if handled by the main loop (scanDecls).
1777: * <p>
1778: * <pre>
1779: * [61] conditionalSect ::= includeSect | ignoreSect
1780: * [62] includeSect ::= '<![' S? 'INCLUDE' S? '[' extSubsetDecl ']]>'
1781: * [63] ignoreSect ::= '<![' S? 'IGNORE' S? '[' ignoreSectContents* ']]>'
1782: * [64] ignoreSectContents ::= Ignore ('<![' ignoreSectContents ']]>' Ignore)*
1783: * [65] Ignore ::= Char* - (Char* ('<![' | ']]>') Char*)
1784: * </pre>
1785: * <p>
1786: * <strong>Note:</strong> Called after scanning past '<![' */
1787: private final void scanConditionalSect(int currPEDepth)
1788: throws IOException, XNIException {
1789:
1790: fReportEntity = false;
1791: skipSeparator(false, !scanningInternalSubset());
1792:
1793: if (fEntityScanner.skipString("INCLUDE")) {
1794: skipSeparator(false, !scanningInternalSubset());
1795: if (currPEDepth != fPEDepth && fValidation) {
1796: fErrorReporter
1797: .reportError(
1798: XMLMessageFormatter.XML_DOMAIN,
1799: "INVALID_PE_IN_CONDITIONAL",
1800: new Object[] { fEntityManager.fCurrentEntity.name },
1801: XMLErrorReporter.SEVERITY_ERROR);
1802: }
1803: // call handler
1804: if (!fEntityScanner.skipChar('[')) {
1805: reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD",
1806: null);
1807: }
1808:
1809: if (fDTDHandler != null) {
1810: fDTDHandler.startConditional(
1811: XMLDTDHandler.CONDITIONAL_INCLUDE, null);
1812: }
1813: fIncludeSectDepth++;
1814: // just stop there and go back to the main loop
1815: fReportEntity = true;
1816: } else if (fEntityScanner.skipString("IGNORE")) {
1817: skipSeparator(false, !scanningInternalSubset());
1818: if (currPEDepth != fPEDepth && fValidation) {
1819: fErrorReporter
1820: .reportError(
1821: XMLMessageFormatter.XML_DOMAIN,
1822: "INVALID_PE_IN_CONDITIONAL",
1823: new Object[] { fEntityManager.fCurrentEntity.name },
1824: XMLErrorReporter.SEVERITY_ERROR);
1825: }
1826: // call handler
1827: if (fDTDHandler != null) {
1828: fDTDHandler.startConditional(
1829: XMLDTDHandler.CONDITIONAL_IGNORE, null);
1830: }
1831: if (!fEntityScanner.skipChar('[')) {
1832: reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD",
1833: null);
1834: }
1835: fReportEntity = true;
1836: int initialDepth = ++fIncludeSectDepth;
1837: if (fDTDHandler != null) {
1838: fIgnoreConditionalBuffer.clear();
1839: }
1840: while (true) {
1841: if (fEntityScanner.skipChar('<')) {
1842: if (fDTDHandler != null) {
1843: fIgnoreConditionalBuffer.append('<');
1844: }
1845: //
1846: // These tests are split so that we handle cases like
1847: // '<<![' and '<!<![' which we might otherwise miss.
1848: //
1849: if (fEntityScanner.skipChar('!')) {
1850: if (fEntityScanner.skipChar('[')) {
1851: if (fDTDHandler != null) {
1852: fIgnoreConditionalBuffer.append("![");
1853: }
1854: fIncludeSectDepth++;
1855: } else {
1856: if (fDTDHandler != null) {
1857: fIgnoreConditionalBuffer.append("!");
1858: }
1859: }
1860: }
1861: } else if (fEntityScanner.skipChar(']')) {
1862: if (fDTDHandler != null) {
1863: fIgnoreConditionalBuffer.append(']');
1864: }
1865: //
1866: // The same thing goes for ']<![' and '<]]>', etc.
1867: //
1868: if (fEntityScanner.skipChar(']')) {
1869: if (fDTDHandler != null) {
1870: fIgnoreConditionalBuffer.append(']');
1871: }
1872: while (fEntityScanner.skipChar(']')) {
1873: /* empty loop body */
1874: if (fDTDHandler != null) {
1875: fIgnoreConditionalBuffer.append(']');
1876: }
1877: }
1878: if (fEntityScanner.skipChar('>')) {
1879: if (fIncludeSectDepth-- == initialDepth) {
1880: fMarkUpDepth--;
1881: // call handler
1882: if (fDTDHandler != null) {
1883: fLiteral
1884: .setValues(
1885: fIgnoreConditionalBuffer.ch,
1886: 0,
1887: fIgnoreConditionalBuffer.length - 2);
1888: fDTDHandler.ignoredCharacters(
1889: fLiteral, null);
1890: fDTDHandler.endConditional(null);
1891: }
1892: return;
1893: } else if (fDTDHandler != null) {
1894: fIgnoreConditionalBuffer.append('>');
1895: }
1896: }
1897: }
1898: } else {
1899: int c = fEntityScanner.scanChar();
1900: if (fScannerState == SCANNER_STATE_END_OF_INPUT) {
1901: reportFatalError("IgnoreSectUnterminated", null);
1902: return;
1903: }
1904: if (fDTDHandler != null) {
1905: fIgnoreConditionalBuffer.append((char) c);
1906: }
1907: }
1908: }
1909: } else {
1910: reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
1911: }
1912:
1913: } // scanConditionalSect()
1914:
1915: /**
1916: * Dispatch an XML "event".
1917: *
1918: * @param complete True if this method is intended to scan
1919: * and dispatch as much as possible.
1920: *
1921: * @return True if there is more to scan.
1922: *
1923: * @throws IOException Thrown on i/o error.
1924: * @throws XNIException Thrown on parse error.
1925: *
1926: */
1927: protected final boolean scanDecls(boolean complete)
1928: throws IOException, XNIException {
1929:
1930: skipSeparator(false, true);
1931: boolean again = true;
1932: while (again && fScannerState == SCANNER_STATE_MARKUP_DECL) {
1933: again = complete;
1934: if (fEntityScanner.skipChar('<')) {
1935: fMarkUpDepth++;
1936: if (fEntityScanner.skipChar('?')) {
1937: scanPI();
1938: } else if (fEntityScanner.skipChar('!')) {
1939: if (fEntityScanner.skipChar('-')) {
1940: if (!fEntityScanner.skipChar('-')) {
1941: reportFatalError(
1942: "MSG_MARKUP_NOT_RECOGNIZED_IN_DTD",
1943: null);
1944: } else {
1945: scanComment();
1946: }
1947: } else if (fEntityScanner.skipString("ELEMENT")) {
1948: scanElementDecl();
1949: } else if (fEntityScanner.skipString("ATTLIST")) {
1950: scanAttlistDecl();
1951: } else if (fEntityScanner.skipString("ENTITY")) {
1952: scanEntityDecl();
1953: } else if (fEntityScanner.skipString("NOTATION")) {
1954: scanNotationDecl();
1955: } else if (fEntityScanner.skipChar('[')
1956: && !scanningInternalSubset()) {
1957: scanConditionalSect(fPEDepth);
1958: } else {
1959: fMarkUpDepth--;
1960: reportFatalError(
1961: "MSG_MARKUP_NOT_RECOGNIZED_IN_DTD",
1962: null);
1963: }
1964: } else {
1965: fMarkUpDepth--;
1966: reportFatalError(
1967: "MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
1968: }
1969: } else if (fIncludeSectDepth > 0
1970: && fEntityScanner.skipChar(']')) {
1971: // end of conditional section?
1972: if (!fEntityScanner.skipChar(']')
1973: || !fEntityScanner.skipChar('>')) {
1974: reportFatalError("IncludeSectUnterminated", null);
1975: }
1976: // call handler
1977: if (fDTDHandler != null) {
1978: fDTDHandler.endConditional(null);
1979: }
1980: // decreaseMarkupDepth();
1981: fIncludeSectDepth--;
1982: fMarkUpDepth--;
1983: } else if (scanningInternalSubset()
1984: && fEntityScanner.peekChar() == ']') {
1985: // this is the end of the internal subset, let's stop here
1986: return false;
1987: } else if (fEntityScanner.skipSpaces()) {
1988: // simply skip
1989: } else {
1990: reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD",
1991: null);
1992: // Skip the part in error
1993: int ch;
1994: do {
1995: // Ignore the current character
1996: fEntityScanner.scanChar();
1997: // Skip any separators
1998: skipSeparator(false, true);
1999: // Keeping getting the next character,
2000: // until it's one of the expected ones
2001: ch = fEntityScanner.peekChar();
2002: } while (ch != '<' && ch != ']' && !XMLChar.isSpace(ch));
2003: }
2004: skipSeparator(false, true);
2005: }
2006: return fScannerState != SCANNER_STATE_END_OF_INPUT;
2007: }
2008:
2009: /**
2010: * Skip separator. This is typically just whitespace but it can also be one
2011: * or more parameter entity references.
2012: * <p>
2013: * If there are some it "expands them" by calling the corresponding entity
2014: * from the entity manager.
2015: * <p>
2016: * This is recursive and will process has many refs as possible.
2017: *
2018: * @param spaceRequired Specify whether some leading whitespace should be
2019: * found
2020: * @param lookForPERefs Specify whether parameter entity references should
2021: * be looked for
2022: * @return True if any leading whitespace was found or the end of a
2023: * parameter entity was crossed.
2024: */
2025: private boolean skipSeparator(boolean spaceRequired,
2026: boolean lookForPERefs) throws IOException, XNIException {
2027: int depth = fPEDepth;
2028: boolean sawSpace = fEntityScanner.skipSpaces();
2029: if (!lookForPERefs || !fEntityScanner.skipChar('%')) {
2030: return !spaceRequired || sawSpace || (depth != fPEDepth);
2031: }
2032: while (true) {
2033: String name = fEntityScanner.scanName();
2034: if (name == null) {
2035: reportFatalError("NameRequiredInPEReference", null);
2036: } else if (!fEntityScanner.skipChar(';')) {
2037: reportFatalError("SemicolonRequiredInPEReference",
2038: new Object[] { name });
2039: }
2040: startPE(name, false);
2041: fEntityScanner.skipSpaces();
2042: if (!fEntityScanner.skipChar('%'))
2043: return true;
2044: }
2045: }
2046:
2047: /*
2048: * Element Children Content Stack
2049: */
2050: private final void pushContentStack(int c) {
2051: if (fContentStack.length == fContentDepth) {
2052: int[] newStack = new int[fContentDepth * 2];
2053: System.arraycopy(fContentStack, 0, newStack, 0,
2054: fContentDepth);
2055: fContentStack = newStack;
2056: }
2057: fContentStack[fContentDepth++] = c;
2058: }
2059:
2060: private final int popContentStack() {
2061: return fContentStack[--fContentDepth];
2062: }
2063:
2064: /*
2065: * Parameter Entity Stack
2066: */
2067: private final void pushPEStack(int depth, boolean report) {
2068: if (fPEStack.length == fPEDepth) {
2069: int[] newIntStack = new int[fPEDepth * 2];
2070: System.arraycopy(fPEStack, 0, newIntStack, 0, fPEDepth);
2071: fPEStack = newIntStack;
2072: // report end/start calls
2073: boolean[] newBooleanStack = new boolean[fPEDepth * 2];
2074: System
2075: .arraycopy(fPEReport, 0, newBooleanStack, 0,
2076: fPEDepth);
2077: fPEReport = newBooleanStack;
2078:
2079: }
2080: fPEReport[fPEDepth] = report;
2081: fPEStack[fPEDepth++] = depth;
2082: }
2083:
2084: /** pop the stack */
2085: private final int popPEStack() {
2086: return fPEStack[--fPEDepth];
2087: }
2088:
2089: /** look at the top of the stack */
2090: private final boolean peekReportEntity() {
2091: return fPEReport[fPEDepth - 1];
2092: }
2093:
2094: /*
2095: * Utility method
2096: */
2097: private final void ensureEnumerationSize(int size) {
2098: if (fEnumeration.length == size) {
2099: String[] newEnum = new String[size * 2];
2100: System.arraycopy(fEnumeration, 0, newEnum, 0, size);
2101: fEnumeration = newEnum;
2102: }
2103: }
2104:
2105: // private methods
2106: private void init() {
2107: // reset state related data
2108: fStartDTDCalled = false;
2109: fExtEntityDepth = 0;
2110: fIncludeSectDepth = 0;
2111: fMarkUpDepth = 0;
2112: fPEDepth = 0;
2113:
2114: fStandalone = false;
2115: fSeenExternalDTD = false;
2116: fSeenPEReferences = false;
2117:
2118: // set starting state
2119: setScannerState(SCANNER_STATE_TEXT_DECL);
2120: }
2121:
2122: private int skipInvalidEnumerationValue() throws IOException {
2123: int c;
2124: do {
2125: c = fEntityScanner.scanChar();
2126: } while (c != '|' && c != ')');
2127: ensureEnumerationSize(fEnumerationCount + 1);
2128: fEnumeration[fEnumerationCount++] = XMLSymbols.EMPTY_STRING;
2129: return c;
2130: }
2131:
2132: } // class XMLDTDScannerImpl
|