/*
* 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: *
* Note: Components should silently ignore features * that do not affect the operation of the component. * * @param featureId The feature identifier. * @param state The state of the feature. * * @throws SAXNotRecognizedException The component should not throw * this exception. * @throws SAXNotSupportedException The component should not throw * this exception. */ public void setFeature(String featureId, boolean state) throws XMLConfigurationException { super.setFeature(featureId, state); // Xerces properties if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) { final int suffixLength = featureId.length() - Constants.XERCES_FEATURE_PREFIX.length(); if (suffixLength == Constants.LOAD_EXTERNAL_DTD_FEATURE.length() && featureId.endsWith(Constants.LOAD_EXTERNAL_DTD_FEATURE)) { fLoadExternalDTD = state; return; } else if (suffixLength == Constants.DISALLOW_DOCTYPE_DECL_FEATURE.length() && featureId.endsWith(Constants.DISALLOW_DOCTYPE_DECL_FEATURE)) { fDisallowDoctype = state; return; } } } // setFeature(String,boolean) /** * Returns a list of property identifiers that are recognized by * this component. This method may return null if no properties * are recognized by this component. */ public String[] getRecognizedProperties() { String[] propertyIds = super.getRecognizedProperties(); int length = propertyIds != null ? propertyIds.length : 0; String[] combinedPropertyIds = new String[length + RECOGNIZED_PROPERTIES.length]; if (propertyIds != null) { System.arraycopy(propertyIds, 0, combinedPropertyIds, 0, propertyIds.length); } System.arraycopy(RECOGNIZED_PROPERTIES, 0, combinedPropertyIds, length, RECOGNIZED_PROPERTIES.length); return combinedPropertyIds; } // getRecognizedProperties():String[] /** * Sets the value of a property. This method is called by the component * manager any time after reset when a property changes value. *
* Note: Components should silently ignore properties * that do not affect the operation of the component. * * @param propertyId The property identifier. * @param value The value of the property. * * @throws SAXNotRecognizedException The component should not throw * this exception. * @throws SAXNotSupportedException The component should not throw * this exception. */ public void setProperty(String propertyId, Object value) throws XMLConfigurationException { super.setProperty(propertyId, value); // Xerces properties if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) { final int suffixLength = propertyId.length() - Constants.XERCES_PROPERTY_PREFIX.length(); if (suffixLength == Constants.DTD_SCANNER_PROPERTY.length() && propertyId.endsWith(Constants.DTD_SCANNER_PROPERTY)) { fDTDScanner = (XMLDTDScanner)value; } if (suffixLength == Constants.NAMESPACE_CONTEXT_PROPERTY.length() && propertyId.endsWith(Constants.NAMESPACE_CONTEXT_PROPERTY)) { if (value != null) { fNamespaceContext = (NamespaceContext)value; } } return; } } // setProperty(String,Object) /** * Returns the default state for a feature, or null if this * component does not want to report a default value for this * feature. * * @param featureId The feature identifier. * * @since Xerces 2.2.0 */ public Boolean getFeatureDefault(String featureId) { for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) { if (RECOGNIZED_FEATURES[i].equals(featureId)) { return FEATURE_DEFAULTS[i]; } } return super.getFeatureDefault(featureId); } // getFeatureDefault(String):Boolean /** * Returns the default state for a property, or null if this * component does not want to report a default value for this * property. * * @param propertyId The property identifier. * * @since Xerces 2.2.0 */ public Object getPropertyDefault(String propertyId) { for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) { if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) { return PROPERTY_DEFAULTS[i]; } } return super.getPropertyDefault(propertyId); } // getPropertyDefault(String):Object // // XMLEntityHandler methods // /** * This method notifies of the start of an entity. The DTD has the * pseudo-name of "[dtd]" parameter entity names start with '%'; and * general entities are just specified by their name. * * @param name The name of the entity. * @param identifier The resource identifier. * @param encoding The auto-detected IANA encoding name of the entity * stream. This value will be null in those situations * where the entity encoding is not auto-detected (e.g. * internal entities or a document entity that is * parsed from a java.io.Reader). * * @throws XNIException Thrown by handler to signal an error. */ public void startEntity(String name, XMLResourceIdentifier identifier, String encoding, Augmentations augs) throws XNIException { super.startEntity(name, identifier, encoding, augs); // prepare to look for a TextDecl if external general entity if (!name.equals("[xml]") && fEntityScanner.isExternal()) { setScannerState(SCANNER_STATE_TEXT_DECL); } // call handler if (fDocumentHandler != null && name.equals("[xml]")) { fDocumentHandler.startDocument(fEntityScanner, encoding, fNamespaceContext, null); } } // startEntity(String,identifier,String) /** * This method notifies the end of an entity. The DTD has the pseudo-name * of "[dtd]" parameter entity names start with '%'; and general entities * are just specified by their name. * * @param name The name of the entity. * * @throws XNIException Thrown by handler to signal an error. */ public void endEntity(String name, Augmentations augs) throws XNIException { super.endEntity(name, augs); // call handler if (fDocumentHandler != null && name.equals("[xml]")) { fDocumentHandler.endDocument(null); } } // endEntity(String) // // Protected methods // // dispatcher factory methods /** Creates a content dispatcher. */ protected Dispatcher createContentDispatcher() { return new ContentDispatcher(); } // createContentDispatcher():Dispatcher // scanning methods /** Scans a doctype declaration. */ protected boolean scanDoctypeDecl() throws IOException, XNIException { // spaces if (!fEntityScanner.skipSpaces()) { reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ROOT_ELEMENT_TYPE_IN_DOCTYPEDECL", null); } // root element name fDoctypeName = fEntityScanner.scanName(); if (fDoctypeName == null) { reportFatalError("MSG_ROOT_ELEMENT_TYPE_REQUIRED", null); } // external id if (fEntityScanner.skipSpaces()) { scanExternalID(fStrings, false); fDoctypeSystemId = fStrings[0]; fDoctypePublicId = fStrings[1]; fEntityScanner.skipSpaces(); } fHasExternalDTD = fDoctypeSystemId != null; // Attempt to locate an external subset with an external subset resolver. if (!fHasExternalDTD && fExternalSubsetResolver != null) { XMLDTDDescription desc = new XMLDTDDescription(null, null, fEntityManager.getCurrentResourceIdentifier().getExpandedSystemId(), null, fDoctypeName); fExternalSubsetSource = fExternalSubsetResolver.getExternalSubset(desc); fHasExternalDTD = fExternalSubsetSource != null; } // call handler if (fDocumentHandler != null) { // NOTE: I don't like calling the doctypeDecl callback until // end of the *full* doctype line (including internal // subset) is parsed correctly but SAX2 requires that // it knows the root element name and public and system // identifier for the startDTD call. -Ac if (fExternalSubsetSource == null) { fDocumentHandler.doctypeDecl(fDoctypeName, fDoctypePublicId, fDoctypeSystemId, null); } else { fDocumentHandler.doctypeDecl(fDoctypeName, fExternalSubsetSource.getPublicId(), fExternalSubsetSource.getSystemId(), null); } } // is there an internal subset? boolean internalSubset = true; if (!fEntityScanner.skipChar('[')) { internalSubset = false; fEntityScanner.skipSpaces(); if (!fEntityScanner.skipChar('>')) { reportFatalError("DoctypedeclUnterminated", new Object[]{fDoctypeName}); } fMarkupDepth--; } return internalSubset; } // scanDoctypeDecl():boolean // // Private methods // /** Returns the scanner state name. */ protected String getScannerStateName(int state) { switch (state) { case SCANNER_STATE_XML_DECL: return "SCANNER_STATE_XML_DECL"; case SCANNER_STATE_PROLOG: return "SCANNER_STATE_PROLOG"; case SCANNER_STATE_TRAILING_MISC: return "SCANNER_STATE_TRAILING_MISC"; case SCANNER_STATE_DTD_INTERNAL_DECLS: return "SCANNER_STATE_DTD_INTERNAL_DECLS"; case SCANNER_STATE_DTD_EXTERNAL: return "SCANNER_STATE_DTD_EXTERNAL"; case SCANNER_STATE_DTD_EXTERNAL_DECLS: return "SCANNER_STATE_DTD_EXTERNAL_DECLS"; } return super.getScannerStateName(state); } // getScannerStateName(int):String // // Classes // /** * Dispatcher to handle XMLDecl scanning. * * @author Andy Clark, IBM */ protected final class XMLDeclDispatcher implements Dispatcher { // // Dispatcher methods // /** * Dispatch an XML "event". * * @param complete True if this dispatcher is intended to scan * and dispatch as much as possible. * * @return True if there is more to dispatch either from this * or a another dispatcher. * * @throws IOException Thrown on i/o error. * @throws XNIException Thrown on parse error. */ public boolean dispatch(boolean complete) throws IOException, XNIException { // next dispatcher is prolog regardless of whether there // is an XMLDecl in this document setScannerState(SCANNER_STATE_PROLOG); setDispatcher(fPrologDispatcher); // scan XMLDecl try { if (fEntityScanner.skipString("')) { reportFatalError("DoctypedeclUnterminated", new Object[]{fDoctypeName}); } fMarkupDepth--; // scan external subset next if (fDoctypeSystemId != null) { if ((fValidation || fLoadExternalDTD) && (fValidationManager == null || !fValidationManager.isCachedDTD())) { setScannerState(SCANNER_STATE_DTD_EXTERNAL); break; } } else if (fExternalSubsetSource != null) { if ((fValidation || fLoadExternalDTD) && (fValidationManager == null || !fValidationManager.isCachedDTD())) { // This handles the case of a DOCTYPE that only had an internal subset. fDTDScanner.setInputSource(fExternalSubsetSource); fExternalSubsetSource = null; setScannerState(SCANNER_STATE_DTD_EXTERNAL_DECLS); break; } } // break out of this dispatcher. setScannerState(SCANNER_STATE_PROLOG); setDispatcher(fPrologDispatcher); fEntityManager.setEntityHandler(XMLDocumentScannerImpl.this); return true; } break; } case SCANNER_STATE_DTD_EXTERNAL: { entityDescription.setDescription("[dtd]", fDoctypePublicId, fDoctypeSystemId, null, null); XMLInputSource xmlInputSource = fEntityManager.resolveEntity(entityDescription); fDTDScanner.setInputSource(xmlInputSource); setScannerState(SCANNER_STATE_DTD_EXTERNAL_DECLS); again = true; break; } case SCANNER_STATE_DTD_EXTERNAL_DECLS: { // REVISIT: Should there be a feature for // the "complete" parameter? boolean completeDTD = true; boolean moreToScan = fDTDScanner.scanDTDExternalSubset(completeDTD); if (!moreToScan) { setScannerState(SCANNER_STATE_PROLOG); setDispatcher(fPrologDispatcher); fEntityManager.setEntityHandler(XMLDocumentScannerImpl.this); return true; } break; } default: { throw new XNIException("DTDDispatcher#dispatch: scanner state="+fScannerState+" ("+getScannerStateName(fScannerState)+')'); } } } while (complete || again); } // encoding errors catch (MalformedByteSequenceException e) { fErrorReporter.reportError(e.getDomain(), e.getKey(), e.getArguments(), XMLErrorReporter.SEVERITY_FATAL_ERROR); return false; } catch (CharConversionException e) { reportFatalError("CharConversionFailure", null); return false; } // premature end of file catch (EOFException e) { reportFatalError("PrematureEOF", null); return false; //throw e; } // cleanup finally { fEntityManager.setEntityHandler(XMLDocumentScannerImpl.this); } return true; } // dispatch(boolean):boolean } // class DTDDispatcher /** * Dispatcher to handle content scanning. * * @author Andy Clark, IBM * @author Eric Ye, IBM */ protected class ContentDispatcher extends FragmentContentDispatcher { // // Protected methods // // hooks // NOTE: These hook methods are added so that the full document // scanner can share the majority of code with this class. /** * Scan for DOCTYPE hook. This method is a hook for subclasses * to add code to handle scanning for a the "DOCTYPE" string * after the string "Attempt to locate an external subset for a document that does not otherwise * have one. If an external subset is located, then it is scanned.
*/ protected void resolveExternalSubsetAndRead() throws IOException, XNIException { XMLDTDDescription desc = new XMLDTDDescription(null, null, fEntityManager.getCurrentResourceIdentifier().getExpandedSystemId(), null, fElementQName.rawname); XMLInputSource src = fExternalSubsetResolver.getExternalSubset(desc); if (src != null) { fDoctypeName = fElementQName.rawname; fDoctypePublicId = src.getPublicId(); fDoctypeSystemId = src.getSystemId(); // call document handler if (fDocumentHandler != null) { // This inserts a doctypeDecl event into the stream though no // DOCTYPE existed in the instance document. fDocumentHandler.doctypeDecl(fDoctypeName, fDoctypePublicId, fDoctypeSystemId, null); } try { fDTDScanner.setInputSource(src); while (fDTDScanner.scanDTDExternalSubset(true)); } finally { fEntityManager.setEntityHandler(XMLDocumentScannerImpl.this); } } } // resolveExternalSubsetAndRead() } // class ContentDispatcher /** * Dispatcher to handle trailing miscellaneous section scanning. * * @author Andy Clark, IBM * @author Eric Ye, IBM */ protected final class TrailingMiscDispatcher implements Dispatcher { // // Dispatcher methods // /** * Dispatch an XML "event". * * @param complete True if this dispatcher is intended to scan * and dispatch as much as possible. * * @return True if there is more to dispatch either from this * or a another dispatcher. * * @throws IOException Thrown on i/o error. * @throws XNIException Thrown on parse error. */ public boolean dispatch(boolean complete) throws IOException, XNIException { try { boolean again; do { again = false; switch (fScannerState) { case SCANNER_STATE_TRAILING_MISC: { fEntityScanner.skipSpaces(); if (fEntityScanner.skipChar('<')) { setScannerState(SCANNER_STATE_START_OF_MARKUP); again = true; } else { setScannerState(SCANNER_STATE_CONTENT); again = true; } break; } case SCANNER_STATE_START_OF_MARKUP: { fMarkupDepth++; if (fEntityScanner.skipChar('?')) { setScannerState(SCANNER_STATE_PI); again = true; } else if (fEntityScanner.skipChar('!')) { setScannerState(SCANNER_STATE_COMMENT); again = true; } else if (fEntityScanner.skipChar('/')) { reportFatalError("MarkupNotRecognizedInMisc", null); again = true; } else if (isValidNameStartChar(fEntityScanner.peekChar())) { reportFatalError("MarkupNotRecognizedInMisc", null); scanStartElement(); setScannerState(SCANNER_STATE_CONTENT); } else if (isValidNameStartHighSurrogate(fEntityScanner.peekChar())) { reportFatalError("MarkupNotRecognizedInMisc", null); scanStartElement(); setScannerState(SCANNER_STATE_CONTENT); } else { reportFatalError("MarkupNotRecognizedInMisc", null); } break; } case SCANNER_STATE_PI: { scanPI(); setScannerState(SCANNER_STATE_TRAILING_MISC); break; } case SCANNER_STATE_COMMENT: { if (!fEntityScanner.skipString("--")) { reportFatalError("InvalidCommentStart", null); } scanComment(); setScannerState(SCANNER_STATE_TRAILING_MISC); break; } case SCANNER_STATE_CONTENT: { int ch = fEntityScanner.peekChar(); if (ch == -1) { setScannerState(SCANNER_STATE_TERMINATED); return false; } reportFatalError("ContentIllegalInTrailingMisc", null); fEntityScanner.scanChar(); setScannerState(SCANNER_STATE_TRAILING_MISC); break; } case SCANNER_STATE_REFERENCE: { reportFatalError("ReferenceIllegalInTrailingMisc", null); setScannerState(SCANNER_STATE_TRAILING_MISC); break; } case SCANNER_STATE_TERMINATED: { return false; } } } while (complete || again); } // encoding errors catch (MalformedByteSequenceException e) { fErrorReporter.reportError(e.getDomain(), e.getKey(), e.getArguments(), XMLErrorReporter.SEVERITY_FATAL_ERROR); return false; } catch (CharConversionException e) { reportFatalError("CharConversionFailure", null); return false; } catch (EOFException e) { // NOTE: This is the only place we're allowed to reach // the real end of the document stream. Unless the // end of file was reached prematurely. if (fMarkupDepth != 0) { reportFatalError("PrematureEOF", null); return false; //throw e; } setScannerState(SCANNER_STATE_TERMINATED); return false; } return true; } // dispatch(boolean):boolean } // class TrailingMiscDispatcher } // class XMLDocumentScannerImpl