/*
* 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
*
* This component requires the following features and properties from the * component manager that uses it: *
*
* [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