All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.xerces.xinclude.XIncludeHandler Maven / Gradle / Ivy

Go to download

Xerces2 provides high performance, fully compliant XML parsers in the Apache Xerces family. This new version of Xerces continues to build upon the Xerces Native Interface (XNI), a complete framework for building parser components and configurations that is extremely modular and easy to program. The Apache Xerces2 parser is the reference implementation of XNI but other parser components, configurations, and parsers can be written using the Xerces Native Interface. For complete design and implementation documents, refer to the XNI Manual. Xerces2 provides fully conforming XML Schema 1.0 and 1.1 processors. An experimental implementation of the "XML Schema Definition Language (XSD): Component Designators (SCD) Candidate Recommendation (January 2010)" is also provided for evaluation. For more information, refer to the XML Schema page. Xerces2 also provides a complete implementation of the Document Object Model Level 3 Core and Load/Save W3C Recommendations and provides a complete implementation of the XML Inclusions (XInclude) W3C Recommendation. It also provides support for OASIS XML Catalogs v1.1. Xerces2 is able to parse documents written according to the XML 1.1 Recommendation, except that it does not yet provide an option to enable normalization checking as described in section 2.13 of this specification. It also handles namespaces according to the XML Namespaces 1.1 Recommendation, and will correctly serialize XML 1.1 documents if the DOM level 3 load/save APIs are in use.

There is a newer version: 2.12.2
Show newest version
/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 2001-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 [email protected].
 *
 * 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) 2003, International
 * Business Machines, Inc., http://www.apache.org.  For more
 * information on the Apache Software Foundation, please see
 * .
 */
package org.apache.xerces.xinclude;

import java.io.IOException;
import java.util.Enumeration;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.Vector;

import org.apache.xerces.impl.Constants;
import org.apache.xerces.impl.XMLEntityManager;
import org.apache.xerces.impl.XMLErrorReporter;
import org.apache.xerces.util.AugmentationsImpl;
import org.apache.xerces.util.IntStack;
import org.apache.xerces.util.ParserConfigurationSettings;
import org.apache.xerces.util.SecurityManager;
import org.apache.xerces.util.URI;
import org.apache.xerces.util.XMLAttributesImpl;
import org.apache.xerces.util.XMLResourceIdentifierImpl;
import org.apache.xerces.util.XMLSymbols;
import org.apache.xerces.util.URI.MalformedURIException;
import org.apache.xerces.xni.Augmentations;
import org.apache.xerces.xni.NamespaceContext;
import org.apache.xerces.xni.QName;
import org.apache.xerces.xni.XMLAttributes;
import org.apache.xerces.xni.XMLDTDHandler;
import org.apache.xerces.xni.XMLDocumentHandler;
import org.apache.xerces.xni.XMLLocator;
import org.apache.xerces.xni.XMLResourceIdentifier;
import org.apache.xerces.xni.XMLString;
import org.apache.xerces.xni.XNIException;
import org.apache.xerces.xni.parser.XMLComponent;
import org.apache.xerces.xni.parser.XMLComponentManager;
import org.apache.xerces.xni.parser.XMLConfigurationException;
import org.apache.xerces.xni.parser.XMLDTDFilter;
import org.apache.xerces.xni.parser.XMLDTDSource;
import org.apache.xerces.xni.parser.XMLDocumentFilter;
import org.apache.xerces.xni.parser.XMLDocumentSource;
import org.apache.xerces.xni.parser.XMLEntityResolver;
import org.apache.xerces.xni.parser.XMLInputSource;
import org.apache.xerces.xni.parser.XMLParserConfiguration;

/**
 * 

* This is a pipeline component which performs XInclude handling, according to the * W3C specification for XML Inclusions. *

*

* This component analyzes each event in the pipeline, looking for <include> * elements. An <include> element is one which has a namespace of * http://www.w3.org/2003/XInclude and a localname of include. * When it finds an <include> element, it attempts to include the file specified * in the href attribute of the element. If inclusion succeeds, all * children of the <include> element are ignored (with the exception of * checking for invalid children as outlined in the specification). If the inclusion * fails, the <fallback> child of the <include> element is processed. *

*

* See the XInclude specification for * more information on how XInclude is to be used. *

*

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

    *
  • http://xml.org/sax/features/allow-dtd-events-after-endDTD
  • *
  • http://apache.org/xml/properties/internal/error-reporter
  • *
  • http://apache.org/xml/properties/internal/entity-resolver
  • *
* Furthermore, the NamespaceContext used in the pipeline is required * to be an instance of XIncludeNamespaceSupport. *

*

* Currently, this implementation has only partial support for the XInclude specification. * Specifically, it is missing support for XPointer document fragments. Thus, only whole * documents can be included using this component in the pipeline. *

* * @author Peter McCracken, IBM * * @version $Id: XIncludeHandler.java,v 1.21 2004/02/17 07:14:49 neeraj Exp $ * * @see XIncludeNamespaceSupport */ public class XIncludeHandler implements XMLComponent, XMLDocumentFilter, XMLDTDFilter { public final static String XINCLUDE_DEFAULT_CONFIGURATION = "org.apache.xerces.parsers.XIncludeParserConfiguration"; public final static String HTTP_ACCEPT = "Accept"; public final static String HTTP_ACCEPT_LANGUAGE = "Accept-Language"; public final static String HTTP_ACCEPT_CHARSET = "Accept-Charset"; public final static String XPOINTER = "xpointer"; public final static String XINCLUDE_NS_URI = "http://www.w3.org/2003/XInclude".intern(); public final static String XINCLUDE_NS_URI_OLD = "http://www.w3.org/2001/XInclude".intern(); public final static String XINCLUDE_INCLUDE = "include".intern(); public final static String XINCLUDE_FALLBACK = "fallback".intern(); public final static String XINCLUDE_PARSE_XML = "xml".intern(); public final static String XINCLUDE_PARSE_TEXT = "text".intern(); public final static String XINCLUDE_ATTR_HREF = "href".intern(); public final static String XINCLUDE_ATTR_PARSE = "parse".intern(); public final static String XINCLUDE_ATTR_ENCODING = "encoding".intern(); public final static String XINCLUDE_ATTR_ACCEPT = "accept".intern(); public final static String XINCLUDE_ATTR_ACCEPT_LANGUAGE = "accept-language".intern(); public final static String XINCLUDE_ATTR_ACCEPT_CHARSET = "accept-charset".intern(); // Top Level Information Items have [included] property in infoset public final static String XINCLUDE_INCLUDED = "[included]".intern(); /** The identifier for the Augmentation that contains the current base URI */ public final static String CURRENT_BASE_URI = "currentBaseURI"; // used for adding [base URI] attributes public final static String XINCLUDE_BASE = "base"; public final static QName XML_BASE_QNAME = new QName( XMLSymbols.PREFIX_XML, XINCLUDE_BASE, XMLSymbols.PREFIX_XML + ":" + XINCLUDE_BASE, NamespaceContext.XML_URI); public final static QName NEW_NS_ATTR_QNAME = new QName( XMLSymbols.PREFIX_XMLNS, "", XMLSymbols.PREFIX_XMLNS + ":", NamespaceContext.XMLNS_URI); // Processing States private final static int STATE_NORMAL_PROCESSING = 1; // we go into this state after a successful include (thus we ignore the children // of the include) or after a fallback private final static int STATE_IGNORE = 2; // we go into this state after a failed include. If we don't encounter a fallback // before we reach the end include tag, it's a fatal error private final static int STATE_EXPECT_FALLBACK = 3; // recognized features and properties /** Feature identifier: allow notation and unparsed entity events to be sent out of order. */ protected static final String ALLOW_UE_AND_NOTATION_EVENTS = Constants.SAX_FEATURE_PREFIX + Constants.ALLOW_DTD_EVENTS_AFTER_ENDDTD_FEATURE; /** Property identifier: error reporter. */ protected static final String ERROR_REPORTER = Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY; /** Property identifier: entity resolver. */ protected static final String ENTITY_RESOLVER = Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY; /** property identifier: security manager. */ protected static final String SECURITY_MANAGER = Constants.XERCES_PROPERTY_PREFIX + Constants.SECURITY_MANAGER_PROPERTY; /** Recognized features. */ private static final String[] RECOGNIZED_FEATURES = { ALLOW_UE_AND_NOTATION_EVENTS }; /** Feature defaults. */ private static final Boolean[] FEATURE_DEFAULTS = { Boolean.TRUE }; /** Recognized properties. */ private static final String[] RECOGNIZED_PROPERTIES = { ERROR_REPORTER, ENTITY_RESOLVER, SECURITY_MANAGER }; /** Property defaults. */ private static final Object[] PROPERTY_DEFAULTS = { null, null, null }; // instance variables // for XMLDocumentFilter protected XMLDocumentHandler fDocumentHandler; protected XMLDocumentSource fDocumentSource; // for XMLDTDFilter protected XMLDTDHandler fDTDHandler; protected XMLDTDSource fDTDSource; // for XIncludeHandler protected XIncludeHandler fParentXIncludeHandler; // It's "feels wrong" to store this value here. However, // calculating it can be time consuming, so we cache it. // It's never going to change in the lifetime of this XIncludeHandler protected String fParentRelativeURI; // we cache the child parser configuration, so we don't have to re-create // the objects when the parser is re-used protected XMLParserConfiguration fChildConfig; protected XMLLocator fDocLocation; protected XIncludeNamespaceSupport fNamespaceContext; protected XMLErrorReporter fErrorReporter; protected XMLEntityResolver fEntityResolver; protected SecurityManager fSecurityManager; // these are needed for XML Base processing protected XMLResourceIdentifier fCurrentBaseURI; protected IntStack baseURIScope; protected Stack baseURI; protected Stack literalSystemID; protected Stack expandedSystemID; // used for passing features on to child XIncludeHandler objects protected ParserConfigurationSettings fSettings; // The current element depth. We start at depth 0 (before we've reached any elements) // The first element is at depth 1. private int fDepth; // this value must be at least 1 private static final int INITIAL_SIZE = 8; // Used to ensure that fallbacks are always children of include elements, // and that include elements are never children of other include elements. // An index contains true if the ancestor of the current element which resides // at that depth was an include element. private boolean[] fSawInclude = new boolean[INITIAL_SIZE]; // Ensures that only one fallback element can be at a single depth. // An index contains true if we have seen any fallback elements at that depth, // and it is only reset to false when the end tag of the parent is encountered. private boolean[] fSawFallback = new boolean[INITIAL_SIZE]; // The state of the processor at each given depth. private int[] fState = new int[INITIAL_SIZE]; // buffering the necessary DTD events private Vector fNotations; private Vector fUnparsedEntities; // for SAX compatibility. // Has the value of the ALLOW_UE_AND_NOTATION_EVENTS feature private boolean fSendUEAndNotationEvents; // track the version of the document being parsed private boolean fIsXML11; // track whether a DTD is being parsed private boolean fInDTD; // track whether a warning has already been reported for // use of the old http://www.w3.org/2001/XInclude namespace. private boolean fOldNamespaceWarningIssued; // Constructors public XIncludeHandler() { fDepth = 0; fSawFallback[fDepth] = false; fSawInclude[fDepth] = false; fState[fDepth] = STATE_NORMAL_PROCESSING; fNotations = new Vector(); fUnparsedEntities = new Vector(); baseURIScope = new IntStack(); baseURI = new Stack(); literalSystemID = new Stack(); expandedSystemID = new Stack(); fCurrentBaseURI = new XMLResourceIdentifierImpl(); } // XMLComponent methods public void reset(XMLComponentManager componentManager) throws XNIException { fNamespaceContext = null; fDepth = 0; fNotations = new Vector(); fUnparsedEntities = new Vector(); fParentRelativeURI = null; fIsXML11 = false; fInDTD = false; fOldNamespaceWarningIssued = false; baseURIScope.clear(); baseURI.clear(); literalSystemID.clear(); expandedSystemID.clear(); // REVISIT: Find a better method for maintaining // the state of the XInclude processor. These arrays // can potentially grow quite large. Cleaning them // out on reset may be very time consuming. -- mrglavas // // clear the previous settings from the arrays for (int i = 0; i < fState.length; ++i) { fState[i] = STATE_NORMAL_PROCESSING; } for (int i = 0; i < fSawFallback.length; ++i) { fSawFallback[i] = false; } for (int i = 0; i < fSawInclude.length; ++i) { fSawInclude[i] = false; } try { fSendUEAndNotationEvents = componentManager.getFeature(ALLOW_UE_AND_NOTATION_EVENTS); if (fChildConfig != null) { fChildConfig.setFeature( ALLOW_UE_AND_NOTATION_EVENTS, fSendUEAndNotationEvents); } } catch (XMLConfigurationException e) { } // Get error reporter. try { XMLErrorReporter value = (XMLErrorReporter)componentManager.getProperty(ERROR_REPORTER); if (value != null) { setErrorReporter(value); if (fChildConfig != null) { fChildConfig.setProperty(ERROR_REPORTER, value); } } } catch (XMLConfigurationException e) { fErrorReporter = null; } // Get entity resolver. try { XMLEntityResolver value = (XMLEntityResolver)componentManager.getProperty( ENTITY_RESOLVER); if (value != null) { fEntityResolver = value; if (fChildConfig != null) { fChildConfig.setProperty(ENTITY_RESOLVER, value); } } } catch (XMLConfigurationException e) { fEntityResolver = null; } // Get security manager. try { SecurityManager value = (SecurityManager)componentManager.getProperty( SECURITY_MANAGER); if (value != null) { fSecurityManager = value; if (fChildConfig != null) { fChildConfig.setProperty(SECURITY_MANAGER, value); } } } catch (XMLConfigurationException e) { fSecurityManager = null; } fSettings = new ParserConfigurationSettings(); copyFeatures(componentManager, fSettings); // Don't reset fChildConfig -- we don't want it to share the same components. // It will be reset when it is actually used to parse something. } // reset(XMLComponentManager) /** * Returns a list of feature identifiers that are recognized by * this component. This method may return null if no features * are recognized by this component. */ public String[] getRecognizedFeatures() { return RECOGNIZED_FEATURES; } // getRecognizedFeatures():String[] /** * Sets the state of a feature. This method is called by the component * manager any time after reset when a feature changes state. *

* 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 { if (featureId.equals(ALLOW_UE_AND_NOTATION_EVENTS)) { fSendUEAndNotationEvents = state; } if (fSettings != null) { fSettings.setFeature(featureId, state); } } // 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() { return RECOGNIZED_PROPERTIES; } // 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 { if (propertyId.equals(ERROR_REPORTER)) { setErrorReporter((XMLErrorReporter)value); if (fChildConfig != null) { fChildConfig.setProperty(propertyId, value); } } if (propertyId.equals(ENTITY_RESOLVER)) { fEntityResolver = (XMLEntityResolver)value; if (fChildConfig != null) { fChildConfig.setProperty(propertyId, value); } } if (propertyId.equals(SECURITY_MANAGER)) { fSecurityManager = (SecurityManager)value; if (fChildConfig != null) { fChildConfig.setProperty(propertyId, value); } } } // 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 null; } // getFeatureDefault(String):Boolean /** * Returns the default state for a property, or null if this * component does not want to report a default value for this * property. * * @param propertyId The property identifier. * * @since Xerces 2.2.0 */ public Object getPropertyDefault(String propertyId) { for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) { if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) { return PROPERTY_DEFAULTS[i]; } } return null; } // getPropertyDefault(String):Object public void setDocumentHandler(XMLDocumentHandler handler) { fDocumentHandler = handler; } public XMLDocumentHandler getDocumentHandler() { return fDocumentHandler; } // XMLDocumentHandler methods /** * Event sent at the start of the document. * * A fatal error will occur here, if it is detected that this document has been processed * before. * * This event is only passed on to the document handler if this is the root document. */ public void startDocument( XMLLocator locator, String encoding, NamespaceContext namespaceContext, Augmentations augs) throws XNIException { // we do this to ensure that the proper location is reported in errors // otherwise, the locator from the root document would always be used fErrorReporter.setDocumentLocator(locator); if (!isRootDocument() && fParentXIncludeHandler.searchForRecursiveIncludes(locator)) { reportFatalError( "RecursiveInclude", new Object[] { locator.getExpandedSystemId()}); } if (!(namespaceContext instanceof XIncludeNamespaceSupport)) { reportFatalError("IncompatibleNamespaceContext"); } fNamespaceContext = (XIncludeNamespaceSupport)namespaceContext; fDocLocation = locator; // initialize the current base URI fCurrentBaseURI.setBaseSystemId(locator.getBaseSystemId()); fCurrentBaseURI.setExpandedSystemId(locator.getExpandedSystemId()); fCurrentBaseURI.setLiteralSystemId(locator.getLiteralSystemId()); saveBaseURI(); if (augs == null) { augs = new AugmentationsImpl(); } augs.putItem(CURRENT_BASE_URI, fCurrentBaseURI); if (isRootDocument() && fDocumentHandler != null) { fDocumentHandler.startDocument( locator, encoding, namespaceContext, augs); } } public void xmlDecl( String version, String encoding, String standalone, Augmentations augs) throws XNIException { fIsXML11 = "1.1".equals(version); if (isRootDocument() && fDocumentHandler != null) { fDocumentHandler.xmlDecl(version, encoding, standalone, augs); } } public void doctypeDecl( String rootElement, String publicId, String systemId, Augmentations augs) throws XNIException { if (isRootDocument() && fDocumentHandler != null) { fDocumentHandler.doctypeDecl(rootElement, publicId, systemId, augs); } } public void comment(XMLString text, Augmentations augs) throws XNIException { if (!fInDTD) { if (fDocumentHandler != null && getState() == STATE_NORMAL_PROCESSING) { fDepth++; augs = modifyAugmentations(augs); fDocumentHandler.comment(text, augs); fDepth--; } } else if (fDTDHandler != null) { fDTDHandler.comment(text, augs); } } public void processingInstruction( String target, XMLString data, Augmentations augs) throws XNIException { if (!fInDTD) { if (fDocumentHandler != null && getState() == STATE_NORMAL_PROCESSING) { // we need to change the depth like this so that modifyAugmentations() works fDepth++; augs = modifyAugmentations(augs); fDocumentHandler.processingInstruction(target, data, augs); fDepth--; } } else if (fDTDHandler != null) { fDTDHandler.processingInstruction(target, data, augs); } } public void startElement( QName element, XMLAttributes attributes, Augmentations augs) throws XNIException { fDepth++; setState(getState(fDepth - 1)); // we process the xml:base attributes regardless of what type of element it is processXMLBaseAttributes(attributes); if (isIncludeElement(element)) { boolean success = this.handleIncludeElement(attributes); if (success) { setState(STATE_IGNORE); } else { setState(STATE_EXPECT_FALLBACK); } } else if (isFallbackElement(element)) { this.handleFallbackElement(); } else if ( fDocumentHandler != null && getState() == STATE_NORMAL_PROCESSING) { augs = modifyAugmentations(augs); attributes = processAttributes(attributes); fDocumentHandler.startElement(element, attributes, augs); } } public void emptyElement( QName element, XMLAttributes attributes, Augmentations augs) throws XNIException { fDepth++; setState(getState(fDepth - 1)); // we process the xml:base attributes regardless of what type of element it is processXMLBaseAttributes(attributes); if (isIncludeElement(element)) { boolean success = this.handleIncludeElement(attributes); if (success) { setState(STATE_IGNORE); } else { reportFatalError("NoFallback"); } } else if (isFallbackElement(element)) { this.handleFallbackElement(); } else if (hasXIncludeNamespace(element)) { if (getSawInclude(fDepth - 1)) { reportFatalError( "IncludeChild", new Object[] { element.rawname }); } } else if ( fDocumentHandler != null && getState() == STATE_NORMAL_PROCESSING) { augs = modifyAugmentations(augs); attributes = processAttributes(attributes); fDocumentHandler.emptyElement(element, attributes, augs); } // reset the out of scope stack elements setSawFallback(fDepth + 1, false); setSawInclude(fDepth + 1, false); // check if an xml:base has gone out of scope if (baseURIScope.size() > 0 && fDepth == baseURIScope.peek()) { // pop the values from the stack restoreBaseURI(); } fDepth--; } public void endElement(QName element, Augmentations augs) throws XNIException { if (isIncludeElement(element)) { // if we're ending an include element, and we were expecting a fallback // we check to see if the children of this include element contained a fallback if (getState() == STATE_EXPECT_FALLBACK && !getSawFallback(fDepth + 1)) { reportFatalError("NoFallback"); } } if (isFallbackElement(element)) { // the state would have been set to normal processing if we were expecting the fallback element // now that we're done processing it, we should ignore all the other children of the include element if (getState() == STATE_NORMAL_PROCESSING) { setState(STATE_IGNORE); } } else if ( fDocumentHandler != null && getState() == STATE_NORMAL_PROCESSING) { fDocumentHandler.endElement(element, augs); } // reset the out of scope stack elements setSawFallback(fDepth + 1, false); setSawInclude(fDepth + 1, false); // check if an xml:base has gone out of scope if (baseURIScope.size() > 0 && fDepth == baseURIScope.peek()) { // pop the values from the stack restoreBaseURI(); } fDepth--; } public void startGeneralEntity( String name, XMLResourceIdentifier resId, String encoding, Augmentations augs) throws XNIException { if (fDocumentHandler != null && getState() == STATE_NORMAL_PROCESSING) { fDocumentHandler.startGeneralEntity(name, resId, encoding, augs); } } public void textDecl(String version, String encoding, Augmentations augs) throws XNIException { if (fDocumentHandler != null && getState() == STATE_NORMAL_PROCESSING) { fDocumentHandler.textDecl(version, encoding, augs); } } public void endGeneralEntity(String name, Augmentations augs) throws XNIException { if (fDocumentHandler != null && getState() == STATE_NORMAL_PROCESSING) { fDocumentHandler.endGeneralEntity(name, augs); } } public void characters(XMLString text, Augmentations augs) throws XNIException { if (fDocumentHandler != null && getState() == STATE_NORMAL_PROCESSING) { // we need to change the depth like this so that modifyAugmentations() works fDepth++; augs = modifyAugmentations(augs); fDocumentHandler.characters(text, augs); fDepth--; } } public void ignorableWhitespace(XMLString text, Augmentations augs) throws XNIException { if (fDocumentHandler != null && getState() == STATE_NORMAL_PROCESSING) { fDocumentHandler.ignorableWhitespace(text, augs); } } public void startCDATA(Augmentations augs) throws XNIException { if (fDocumentHandler != null && getState() == STATE_NORMAL_PROCESSING) { fDocumentHandler.startCDATA(augs); } } public void endCDATA(Augmentations augs) throws XNIException { if (fDocumentHandler != null && getState() == STATE_NORMAL_PROCESSING) { fDocumentHandler.endCDATA(augs); } } public void endDocument(Augmentations augs) throws XNIException { if (isRootDocument() && fDocumentHandler != null) { fDocumentHandler.endDocument(augs); } } public void setDocumentSource(XMLDocumentSource source) { fDocumentSource = source; } public XMLDocumentSource getDocumentSource() { return fDocumentSource; } // DTDHandler methods // We are only interested in the notation and unparsed entity declarations, // the rest we just pass on /* (non-Javadoc) * @see org.apache.xerces.xni.XMLDTDHandler#attributeDecl(java.lang.String, java.lang.String, java.lang.String, java.lang.String[], java.lang.String, org.apache.xerces.xni.XMLString, org.apache.xerces.xni.XMLString, org.apache.xerces.xni.Augmentations) */ public void attributeDecl( String elementName, String attributeName, String type, String[] enumeration, String defaultType, XMLString defaultValue, XMLString nonNormalizedDefaultValue, Augmentations augmentations) throws XNIException { if (fDTDHandler != null) { fDTDHandler.attributeDecl( elementName, attributeName, type, enumeration, defaultType, defaultValue, nonNormalizedDefaultValue, augmentations); } } /* (non-Javadoc) * @see org.apache.xerces.xni.XMLDTDHandler#elementDecl(java.lang.String, java.lang.String, org.apache.xerces.xni.Augmentations) */ public void elementDecl( String name, String contentModel, Augmentations augmentations) throws XNIException { if (fDTDHandler != null) { fDTDHandler.elementDecl(name, contentModel, augmentations); } } /* (non-Javadoc) * @see org.apache.xerces.xni.XMLDTDHandler#endAttlist(org.apache.xerces.xni.Augmentations) */ public void endAttlist(Augmentations augmentations) throws XNIException { if (fDTDHandler != null) { fDTDHandler.endAttlist(augmentations); } } /* (non-Javadoc) * @see org.apache.xerces.xni.XMLDTDHandler#endConditional(org.apache.xerces.xni.Augmentations) */ public void endConditional(Augmentations augmentations) throws XNIException { if (fDTDHandler != null) { fDTDHandler.endConditional(augmentations); } } /* (non-Javadoc) * @see org.apache.xerces.xni.XMLDTDHandler#endDTD(org.apache.xerces.xni.Augmentations) */ public void endDTD(Augmentations augmentations) throws XNIException { if (fDTDHandler != null) { fDTDHandler.endDTD(augmentations); } fInDTD = false; } /* (non-Javadoc) * @see org.apache.xerces.xni.XMLDTDHandler#endExternalSubset(org.apache.xerces.xni.Augmentations) */ public void endExternalSubset(Augmentations augmentations) throws XNIException { if (fDTDHandler != null) { fDTDHandler.endExternalSubset(augmentations); } } /* (non-Javadoc) * @see org.apache.xerces.xni.XMLDTDHandler#endParameterEntity(java.lang.String, org.apache.xerces.xni.Augmentations) */ public void endParameterEntity(String name, Augmentations augmentations) throws XNIException { if (fDTDHandler != null) { fDTDHandler.endParameterEntity(name, augmentations); } } /* (non-Javadoc) * @see org.apache.xerces.xni.XMLDTDHandler#externalEntityDecl(java.lang.String, org.apache.xerces.xni.XMLResourceIdentifier, org.apache.xerces.xni.Augmentations) */ public void externalEntityDecl( String name, XMLResourceIdentifier identifier, Augmentations augmentations) throws XNIException { if (fDTDHandler != null) { fDTDHandler.externalEntityDecl(name, identifier, augmentations); } } /* (non-Javadoc) * @see org.apache.xerces.xni.XMLDTDHandler#getDTDSource() */ public XMLDTDSource getDTDSource() { return fDTDSource; } /* (non-Javadoc) * @see org.apache.xerces.xni.XMLDTDHandler#ignoredCharacters(org.apache.xerces.xni.XMLString, org.apache.xerces.xni.Augmentations) */ public void ignoredCharacters(XMLString text, Augmentations augmentations) throws XNIException { if (fDTDHandler != null) { fDTDHandler.ignoredCharacters(text, augmentations); } } /* (non-Javadoc) * @see org.apache.xerces.xni.XMLDTDHandler#internalEntityDecl(java.lang.String, org.apache.xerces.xni.XMLString, org.apache.xerces.xni.XMLString, org.apache.xerces.xni.Augmentations) */ public void internalEntityDecl( String name, XMLString text, XMLString nonNormalizedText, Augmentations augmentations) throws XNIException { if (fDTDHandler != null) { fDTDHandler.internalEntityDecl( name, text, nonNormalizedText, augmentations); } } /* (non-Javadoc) * @see org.apache.xerces.xni.XMLDTDHandler#notationDecl(java.lang.String, org.apache.xerces.xni.XMLResourceIdentifier, org.apache.xerces.xni.Augmentations) */ public void notationDecl( String name, XMLResourceIdentifier identifier, Augmentations augmentations) throws XNIException { this.addNotation(name, identifier, augmentations); if (fDTDHandler != null) { fDTDHandler.notationDecl(name, identifier, augmentations); } } /* (non-Javadoc) * @see org.apache.xerces.xni.XMLDTDHandler#setDTDSource(org.apache.xerces.xni.parser.XMLDTDSource) */ public void setDTDSource(XMLDTDSource source) { fDTDSource = source; } /* (non-Javadoc) * @see org.apache.xerces.xni.XMLDTDHandler#startAttlist(java.lang.String, org.apache.xerces.xni.Augmentations) */ public void startAttlist(String elementName, Augmentations augmentations) throws XNIException { if (fDTDHandler != null) { fDTDHandler.startAttlist(elementName, augmentations); } } /* (non-Javadoc) * @see org.apache.xerces.xni.XMLDTDHandler#startConditional(short, org.apache.xerces.xni.Augmentations) */ public void startConditional(short type, Augmentations augmentations) throws XNIException { if (fDTDHandler != null) { fDTDHandler.startConditional(type, augmentations); } } /* (non-Javadoc) * @see org.apache.xerces.xni.XMLDTDHandler#startDTD(org.apache.xerces.xni.XMLLocator, org.apache.xerces.xni.Augmentations) */ public void startDTD(XMLLocator locator, Augmentations augmentations) throws XNIException { fInDTD = true; if (fDTDHandler != null) { fDTDHandler.startDTD(locator, augmentations); } } /* (non-Javadoc) * @see org.apache.xerces.xni.XMLDTDHandler#startExternalSubset(org.apache.xerces.xni.XMLResourceIdentifier, org.apache.xerces.xni.Augmentations) */ public void startExternalSubset( XMLResourceIdentifier identifier, Augmentations augmentations) throws XNIException { if (fDTDHandler != null) { fDTDHandler.startExternalSubset(identifier, augmentations); } } /* (non-Javadoc) * @see org.apache.xerces.xni.XMLDTDHandler#startParameterEntity(java.lang.String, org.apache.xerces.xni.XMLResourceIdentifier, java.lang.String, org.apache.xerces.xni.Augmentations) */ public void startParameterEntity( String name, XMLResourceIdentifier identifier, String encoding, Augmentations augmentations) throws XNIException { if (fDTDHandler != null) { fDTDHandler.startParameterEntity( name, identifier, encoding, augmentations); } } /* (non-Javadoc) * @see org.apache.xerces.xni.XMLDTDHandler#unparsedEntityDecl(java.lang.String, org.apache.xerces.xni.XMLResourceIdentifier, java.lang.String, org.apache.xerces.xni.Augmentations) */ public void unparsedEntityDecl( String name, XMLResourceIdentifier identifier, String notation, Augmentations augmentations) throws XNIException { this.addUnparsedEntity(name, identifier, notation, augmentations); if (fDTDHandler != null) { fDTDHandler.unparsedEntityDecl( name, identifier, notation, augmentations); } } /* (non-Javadoc) * @see org.apache.xerces.xni.parser.XMLDTDSource#getDTDHandler() */ public XMLDTDHandler getDTDHandler() { return fDTDHandler; } /* (non-Javadoc) * @see org.apache.xerces.xni.parser.XMLDTDSource#setDTDHandler(org.apache.xerces.xni.XMLDTDHandler) */ public void setDTDHandler(XMLDTDHandler handler) { fDTDHandler = handler; } // XIncludeHandler methods private void setErrorReporter(XMLErrorReporter reporter) { fErrorReporter = reporter; if (fErrorReporter != null) { fErrorReporter.putMessageFormatter( XIncludeMessageFormatter.XINCLUDE_DOMAIN, new XIncludeMessageFormatter()); // this ensures the proper location is displayed in error messages if (fDocLocation != null) { fErrorReporter.setDocumentLocator(fDocLocation); } } } protected void handleFallbackElement() { setSawInclude(fDepth, false); fNamespaceContext.setContextInvalid(); if (!getSawInclude(fDepth - 1)) { reportFatalError("FallbackParent"); } if (getSawFallback(fDepth)) { reportFatalError("MultipleFallbacks"); } else { setSawFallback(fDepth, true); } // Either the state is STATE_EXPECT_FALLBACK or it's STATE_IGNORE. // If we're ignoring, we want to stay ignoring. But if we're expecting this fallback element, // we want to signal that we should process the children. if (getState() == STATE_EXPECT_FALLBACK) { setState(STATE_NORMAL_PROCESSING); } } protected boolean handleIncludeElement(XMLAttributes attributes) throws XNIException { setSawInclude(fDepth, true); fNamespaceContext.setContextInvalid(); if (getSawInclude(fDepth - 1)) { reportFatalError("IncludeChild", new Object[] { XINCLUDE_INCLUDE }); } if (getState() == STATE_IGNORE) return true; // TODO: does Java use IURIs by default? // [Definition: An internationalized URI reference, or IURI, is a URI reference that directly uses [Unicode] characters.] // TODO: figure out what section 4.1.1 of the XInclude spec is talking about // has to do with disallowed ASCII character escaping // this ties in with the above IURI section, but I suspect Java already does it String href = attributes.getValue(XINCLUDE_ATTR_HREF); String parse = attributes.getValue(XINCLUDE_ATTR_PARSE); String xpointer = attributes.getValue(XPOINTER); if (href == null && xpointer == null) { reportFatalError("XpointerMissing"); } if (parse == null) { parse = XINCLUDE_PARSE_XML; } XMLInputSource includedSource = null; if (fEntityResolver != null) { try { XMLResourceIdentifier resourceIdentifier = new XMLResourceIdentifierImpl( null, href, fCurrentBaseURI.getExpandedSystemId(), XMLEntityManager.expandSystemId( href, fCurrentBaseURI.getExpandedSystemId(), false)); includedSource = fEntityResolver.resolveEntity(resourceIdentifier); } catch (IOException e) { reportResourceError( "XMLResourceError", new Object[] { href, e.getMessage()}); return false; } } if (includedSource == null) { includedSource = new XMLInputSource( null, href, fCurrentBaseURI.getExpandedSystemId()); } if (parse.equals(XINCLUDE_PARSE_XML)) { // Instead of always creating a new configuration, the first one can be reused if (fChildConfig == null) { String parserName = XINCLUDE_DEFAULT_CONFIGURATION; fChildConfig = (XMLParserConfiguration)ObjectFactory.newInstance( parserName, ObjectFactory.findClassLoader(), true); // use the same error reporter, entity resolver, and security manager. if (fErrorReporter != null) fChildConfig.setProperty(ERROR_REPORTER, fErrorReporter); if (fEntityResolver != null) fChildConfig.setProperty(ENTITY_RESOLVER, fEntityResolver); if (fSecurityManager != null) fChildConfig.setProperty(SECURITY_MANAGER, fSecurityManager); // use the same namespace context fChildConfig.setProperty( Constants.XERCES_PROPERTY_PREFIX + Constants.NAMESPACE_CONTEXT_PROPERTY, fNamespaceContext); XIncludeHandler newHandler = (XIncludeHandler)fChildConfig.getProperty( Constants.XERCES_PROPERTY_PREFIX + Constants.XINCLUDE_HANDLER_PROPERTY); newHandler.setParent(this); newHandler.setDocumentHandler(this.getDocumentHandler()); } // set all features on parserConfig to match this parser configuration copyFeatures(fSettings, fChildConfig); // we don't want a schema validator on the new pipeline, // so we set it to false, regardless of what was copied above fChildConfig.setFeature( Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_VALIDATION_FEATURE, false); try { // REVISIT: If we're going to support content negotation for // parse=xml we need to find a clean way to propogate the // parameters to the child parser. We cannot set those parameters // here because we may lose some information if we process the // URLConnection and then open a stream. The parser needs a // URLConnection to follow HTTP redirects and may also need // this object in the future to read external encoding information // which may be available in the Content-Type. -- mrglavas fNamespaceContext.pushScope(); fChildConfig.parse(includedSource); // necessary to make sure proper location is reported in errors if (fErrorReporter != null) { fErrorReporter.setDocumentLocator(fDocLocation); } } catch (XNIException e) { // necessary to make sure proper location is reported in errors if (fErrorReporter != null) { fErrorReporter.setDocumentLocator(fDocLocation); } reportFatalError("XMLParseError", new Object[] { href }); } catch (IOException e) { // necessary to make sure proper location is reported in errors if (fErrorReporter != null) { fErrorReporter.setDocumentLocator(fDocLocation); } // An IOException indicates that we had trouble reading the file, not // that it was an invalid XML file. So we send a resource error, not a // fatal error. reportResourceError( "XMLResourceError", new Object[] { href, e.getMessage()}); return false; } finally { fNamespaceContext.popScope(); } } else if (parse.equals(XINCLUDE_PARSE_TEXT)) { // we only care about encoding for parse="text" String encoding = attributes.getValue(XINCLUDE_ATTR_ENCODING); includedSource.setEncoding(encoding); XIncludeTextReader reader = null; try { if (fIsXML11) { reader = new XInclude11TextReader(includedSource, this); } else { reader = new XIncludeTextReader(includedSource, this); } if (includedSource.getCharacterStream() == null && includedSource.getByteStream() == null) { reader.setHttpProperties(attributes.getValue(XINCLUDE_ATTR_ACCEPT), attributes.getValue(XINCLUDE_ATTR_ACCEPT_CHARSET), attributes.getValue(XINCLUDE_ATTR_ACCEPT_LANGUAGE)); } reader.setErrorReporter(fErrorReporter); reader.parse(); } catch (IOException e) { reportResourceError( "TextResourceError", new Object[] { href, e.getMessage()}); return false; } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { reportResourceError( "TextResourceError", new Object[] { href, e.getMessage()}); return false; } } } } else { reportFatalError("InvalidParseValue", new Object[] { parse }); } return true; } /** * Returns true if the element has the namespace "http://www.w3.org/2003/XInclude" * @param element the element to check * @return true if the element has the namespace "http://www.w3.org/2003/XInclude" */ protected boolean hasXIncludeNamespace(QName element) { // REVISIT: The namespace of this element should be bound // already. Why are we looking it up from the namespace // context? -- mrglavas return element.uri == XINCLUDE_NS_URI || fNamespaceContext.getURI(element.prefix) == XINCLUDE_NS_URI; } /** * Returns true if the element has the namespace "http://www.w3.org/2001/XInclude" * @param element the element to check * @return true if the element has the namespace "http://www.w3.org/2001/XInclude" */ protected boolean hasXInclude2001Namespace(QName element) { // REVISIT: The namespace of this element should be bound // already. Why are we looking it up from the namespace // context? -- mrglavas return element.uri == XINCLUDE_NS_URI_OLD || fNamespaceContext.getURI(element.prefix) == XINCLUDE_NS_URI_OLD; } /** * Checks if the element is an <include> element. The element must have * the XInclude namespace, and a local name of "include". If the local name * is "include" and the namespace name is the old XInclude namespace * "http://www.w3.org/2001/XInclude" a warning is issued the first time * an element from the old namespace is encountered. * * @param element the element to check * @return true if the element is an <include> element * @see #hasXIncludeNamespace(QName) */ protected boolean isIncludeElement(QName element) { if (element.localpart.equals(XINCLUDE_INCLUDE)) { if (hasXIncludeNamespace(element)) { return true; } else if (!fOldNamespaceWarningIssued && hasXInclude2001Namespace(element)) { reportError("OldXIncludeNamespace", null, XMLErrorReporter.SEVERITY_WARNING); fOldNamespaceWarningIssued = true; } } return false; } /** * Checks if the element is an <fallback> element. The element must have * the XInclude namespace, and a local name of "fallback". If the local name * is "fallback" and the namespace name is the old XInclude namespace * "http://www.w3.org/2001/XInclude" a warning is issued the first time * an element from the old namespace is encountered. * * @param element the element to check * @return true if the element is an <fallback; element * @see #hasXIncludeNamespace(QName) */ protected boolean isFallbackElement(QName element) { if (element.localpart.equals(XINCLUDE_FALLBACK)) { if (hasXIncludeNamespace(element)) { return true; } else if (!fOldNamespaceWarningIssued && hasXInclude2001Namespace(element)) { reportError("OldXIncludeNamespace", null, XMLErrorReporter.SEVERITY_WARNING); fOldNamespaceWarningIssued = true; } } return false; } /** * Returns true if the current [base URI] is the same as the [base URI] that * was in effect on the include parent. This method should only be called * when the current element is a top level included element, i.e. the direct child * of a fallback element, or the root elements in an included document. * The "include parent" is the element which, in the result infoset, will be the * direct parent of the current element. * @return true if the [base URIs] are the same string */ protected boolean sameBaseURIAsIncludeParent() { String parentBaseURI = getIncludeParentBaseURI(); String baseURI = fCurrentBaseURI.getExpandedSystemId(); // REVISIT: should we use File#sameFile() ? // I think the benefit of using it is that it resolves host names // instead of just doing a string comparison. // TODO: [base URI] is still an open issue with the working group. // They're deciding if xml:base should be added if the [base URI] is different in terms // of resolving relative references, or if it should be added if they are different at all. // Revisit this after a final decision has been made. // The decision also affects whether we output the file name of the URI, or just the path. return parentBaseURI != null && parentBaseURI.equals(baseURI); } /** * Checks if the file indicated by the given XMLLocator has already been included * in the current stack. * @param includedSource the source to check for inclusion * @return true if the source has already been included */ protected boolean searchForRecursiveIncludes(XMLLocator includedSource) { String includedSystemId = includedSource.getExpandedSystemId(); if (includedSystemId == null) { try { includedSystemId = XMLEntityManager.expandSystemId( includedSource.getLiteralSystemId(), includedSource.getBaseSystemId(), false); } catch (MalformedURIException e) { reportFatalError("ExpandedSystemId"); } } if (includedSystemId.equals(fCurrentBaseURI.getExpandedSystemId())) { return true; } if (fParentXIncludeHandler == null) { return false; } return fParentXIncludeHandler.searchForRecursiveIncludes( includedSource); } /** * Returns true if the current element is a top level included item. This means * it's either the child of a fallback element, or the top level item in an * included document * @return true if the current element is a top level included item */ protected boolean isTopLevelIncludedItem() { return isTopLevelIncludedItemViaInclude() || isTopLevelIncludedItemViaFallback(); } protected boolean isTopLevelIncludedItemViaInclude() { return fDepth == 1 && !isRootDocument(); } protected boolean isTopLevelIncludedItemViaFallback() { // Technically, this doesn't check if the parent was a fallback, it also // would return true if any of the parent's sibling elements were fallbacks. // However, this doesn't matter, since we will always be ignoring elements // whose parent's siblings were fallbacks. return getSawFallback(fDepth - 1); } /** * Processes the XMLAttributes object of startElement() calls. Performs the following tasks: *

    *
  • If the element is a top level included item whose [base URI] is different from the * [base URI] of the include parent, then an xml:base attribute is added to specify the * true [base URI] *
  • For all namespace prefixes which are in-scope in an included item, but not in scope * in the include parent, a xmlns:prefix attribute is added *
  • For all attributes with a type of ENTITY, ENTITIES or NOTATIONS, the notations and * unparsed entities are processed as described in the spec, sections 4.5.1 and 4.5.2 *
* @param attributes * @return */ protected XMLAttributes processAttributes(XMLAttributes attributes) { if (isTopLevelIncludedItem()) { // Modify attributes to fix the base URI (spec 4.5.5). // We only do it to top level included elements, which have a different // base URI than their include parent. if (!sameBaseURIAsIncludeParent()) { if (attributes == null) { attributes = new XMLAttributesImpl(); } // This causes errors with schema validation, if the schema doesn't // specify that these elements can have an xml:base attribute // TODO: add a user option to turn this off? String uri = null; try { uri = this.getRelativeBaseURI(); } catch (MalformedURIException e) { // this shouldn't ever happen, since by definition, we had to traverse // the same URIs to even get to this place uri = fCurrentBaseURI.getExpandedSystemId(); } int index = attributes.addAttribute( XML_BASE_QNAME, XMLSymbols.fCDATASymbol, uri); attributes.setSpecified(index, true); } // Modify attributes of included items to do namespace-fixup. (spec 4.5.4) Enumeration inscopeNS = fNamespaceContext.getAllPrefixes(); while (inscopeNS.hasMoreElements()) { String prefix = (String)inscopeNS.nextElement(); String parentURI = fNamespaceContext.getURIFromIncludeParent(prefix); String uri = fNamespaceContext.getURI(prefix); if (parentURI != uri && attributes != null) { if (prefix == XMLSymbols.EMPTY_STRING) { if (attributes .getValue( NamespaceContext.XMLNS_URI, XMLSymbols.PREFIX_XMLNS) == null) { if (attributes == null) { attributes = new XMLAttributesImpl(); } QName ns = (QName)NEW_NS_ATTR_QNAME.clone(); ns.localpart = XMLSymbols.PREFIX_XMLNS; ns.rawname = XMLSymbols.PREFIX_XMLNS; attributes.addAttribute( ns, XMLSymbols.fCDATASymbol, uri); } } else if ( attributes.getValue(NamespaceContext.XMLNS_URI, prefix) == null) { if (attributes == null) { attributes = new XMLAttributesImpl(); } QName ns = (QName)NEW_NS_ATTR_QNAME.clone(); ns.localpart = prefix; ns.rawname += prefix; attributes.addAttribute( ns, XMLSymbols.fCDATASymbol, uri); } } } } if (attributes != null) { int length = attributes.getLength(); for (int i = 0; i < length; i++) { String type = attributes.getType(i); String value = attributes.getValue(i); if (type == XMLSymbols.fENTITYSymbol) { this.checkUnparsedEntity(value); } if (type == XMLSymbols.fENTITIESSymbol) { // 4.5.1 - Unparsed Entities StringTokenizer st = new StringTokenizer(value); while (st.hasMoreTokens()) { String entName = st.nextToken(); this.checkUnparsedEntity(entName); } } else if (type == XMLSymbols.fNOTATIONSymbol) { // 4.5.2 - Notations this.checkNotation(value); } /* We actually don't need to do anything for 4.5.3, because at this stage the * value of the attribute is just a string. It will be taken care of later * in the pipeline, when the IDREFs are actually resolved against IDs. * * if (type == XMLSymbols.fIDREFSymbol || type == XMLSymbols.fIDREFSSymbol) { } */ } } return attributes; } /** * Returns a URI, relative to the include parent's base URI, of the current * [base URI]. For instance, if the current [base URI] was "dir1/dir2/file.xml" * and the include parent's [base URI] was "dir/", this would return "dir2/file.xml". * @return the relative URI */ protected String getRelativeBaseURI() throws MalformedURIException { int includeParentDepth = getIncludeParentDepth(); String relativeURI = this.getRelativeURI(includeParentDepth); if (isRootDocument()) { return relativeURI; } else { if (relativeURI.equals("")) { relativeURI = fCurrentBaseURI.getLiteralSystemId(); } if (includeParentDepth == 0) { if (fParentRelativeURI == null) { fParentRelativeURI = fParentXIncludeHandler.getRelativeBaseURI(); } if (fParentRelativeURI.equals("")) { return relativeURI; } URI uri = new URI("file", fParentRelativeURI); uri = new URI(uri, relativeURI); return uri.getPath(); } else { return relativeURI; } } } /** * Returns the [base URI] of the include parent. * @return the base URI of the include parent. */ private String getIncludeParentBaseURI() { int depth = getIncludeParentDepth(); if (!isRootDocument() && depth == 0) { return fParentXIncludeHandler.getIncludeParentBaseURI(); } else { return this.getBaseURI(depth); } } /** * Returns the depth of the include parent. Here, the include parent is * calculated as the last non-include or non-fallback element. It is assumed * this method is called when the current element is a top level included item. * Returning 0 indicates that the top level element in this document * was an include element. * @return the depth of the top level include element */ private int getIncludeParentDepth() { // We don't start at fDepth, since it is either the top level included item, // or an include element, when this method is called. for (int i = fDepth - 1; i >= 0; i--) { // This technically might not always return the first non-include/fallback // element that it comes to, since sawFallback() returns true if a fallback // was ever encountered at that depth. However, if a fallback was encountered // at that depth, and it wasn't the direct descendant of the current element // then we can't be in a situation where we're calling this method (because // we'll always be in STATE_IGNORE) if (!getSawInclude(i) && !getSawFallback(i)) { return i; } } // shouldn't get here, since depth 0 should never have an include element or // a fallback element return 0; } /** * Modify the augmentations. Add an [included] infoset item, if the current * element is a top level included item. * @param augs the Augmentations to modify. * @return the modified Augmentations */ protected Augmentations modifyAugmentations(Augmentations augs) { return modifyAugmentations(augs, false); } /** * Modify the augmentations. Add an [included] infoset item, if force * is true, or if the current element is a top level included item. * @param augs the Augmentations to modify. * @param force whether to force modification * @return the modified Augmentations */ protected Augmentations modifyAugmentations( Augmentations augs, boolean force) { if (force || isTopLevelIncludedItem()) { if (augs == null) { augs = new AugmentationsImpl(); } augs.putItem(XINCLUDE_INCLUDED, Boolean.TRUE); } return augs; } protected int getState(int depth) { return fState[depth]; } protected int getState() { return fState[fDepth]; } protected void setState(int state) { if (fDepth >= fState.length) { int[] newarray = new int[fDepth * 2]; System.arraycopy(fState, 0, newarray, 0, fState.length); fState = newarray; } fState[fDepth] = state; } /** * Records that an <fallback> was encountered at the specified depth, * as an ancestor of the current element, or as a sibling of an ancestor of the * current element. * * @param depth * @param val */ protected void setSawFallback(int depth, boolean val) { if (depth >= fSawFallback.length) { boolean[] newarray = new boolean[depth * 2]; System.arraycopy(fSawFallback, 0, newarray, 0, fSawFallback.length); fSawFallback = newarray; } fSawFallback[depth] = val; } /** * Returns whether an <fallback> was encountered at the specified depth, * as an ancestor of the current element, or as a sibling of an ancestor of the * current element. * * @param depth */ protected boolean getSawFallback(int depth) { if (depth >= fSawFallback.length) { return false; } return fSawFallback[depth]; } /** * Records that an <include> was encountered at the specified depth, * as an ancestor of the current item. * * @param depth * @param val */ protected void setSawInclude(int depth, boolean val) { if (depth >= fSawInclude.length) { boolean[] newarray = new boolean[depth * 2]; System.arraycopy(fSawInclude, 0, newarray, 0, fSawInclude.length); fSawInclude = newarray; } fSawInclude[depth] = val; } /** * Return whether an <include> was encountered at the specified depth, * as an ancestor of the current item. * * @param depth * @return */ protected boolean getSawInclude(int depth) { if (depth >= fSawInclude.length) { return false; } return fSawInclude[depth]; } protected void reportResourceError(String key) { this.reportFatalError(key, null); } protected void reportResourceError(String key, Object[] args) { this.reportError(key, args, XMLErrorReporter.SEVERITY_WARNING); } protected void reportFatalError(String key) { this.reportFatalError(key, null); } protected void reportFatalError(String key, Object[] args) { this.reportError(key, args, XMLErrorReporter.SEVERITY_FATAL_ERROR); } private void reportError(String key, Object[] args, short severity) { if (fErrorReporter != null) { fErrorReporter.reportError( XIncludeMessageFormatter.XINCLUDE_DOMAIN, key, args, severity); } // we won't worry about when error reporter is null, since there should always be // at least the default error reporter } /** * Set the parent of this XIncludeHandler in the tree * @param parent */ protected void setParent(XIncludeHandler parent) { fParentXIncludeHandler = parent; } // used to know whether to pass declarations to the document handler protected boolean isRootDocument() { return fParentXIncludeHandler == null; } /** * Caches an unparsed entity. * @param name the name of the unparsed entity * @param identifier the location of the unparsed entity * @param augmentations any Augmentations that were on the original unparsed entity declaration */ protected void addUnparsedEntity( String name, XMLResourceIdentifier identifier, String notation, Augmentations augmentations) { UnparsedEntity ent = new UnparsedEntity(); ent.name = name; ent.systemId = identifier.getLiteralSystemId(); ent.publicId = identifier.getPublicId(); ent.baseURI = identifier.getBaseSystemId(); ent.notation = notation; ent.augmentations = augmentations; fUnparsedEntities.add(ent); } /** * Caches a notation. * @param name the name of the notation * @param identifier the location of the notation * @param augmentations any Augmentations that were on the original notation declaration */ protected void addNotation( String name, XMLResourceIdentifier identifier, Augmentations augmentations) { Notation not = new Notation(); not.name = name; not.systemId = identifier.getLiteralSystemId(); not.publicId = identifier.getPublicId(); not.baseURI = identifier.getBaseSystemId(); not.augmentations = augmentations; fNotations.add(not); } /** * Checks if an UnparsedEntity with the given name was declared in the DTD of the document * for the current pipeline. If so, then the notation for the UnparsedEntity is checked. * If that turns out okay, then the UnparsedEntity is passed to the root pipeline to * be checked for conflicts, and sent to the root DTDHandler. * * @param entName the name of the UnparsedEntity to check */ protected void checkUnparsedEntity(String entName) { UnparsedEntity ent = new UnparsedEntity(); ent.name = entName; int index = fUnparsedEntities.indexOf(ent); if (index != -1) { ent = (UnparsedEntity)fUnparsedEntities.get(index); // first check the notation of the unparsed entity checkNotation(ent.notation); checkAndSendUnparsedEntity(ent); } } /** * Checks if a Notation with the given name was declared in the DTD of the document * for the current pipeline. If so, that Notation is passed to the root pipeline to * be checked for conflicts, and sent to the root DTDHandler * * @param notName the name of the Notation to check */ protected void checkNotation(String notName) { Notation not = new Notation(); not.name = notName; int index = fNotations.indexOf(not); if (index != -1) { not = (Notation)fNotations.get(index); checkAndSendNotation(not); } } /** * The purpose of this method is to check if an UnparsedEntity conflicts with a previously * declared entity in the current pipeline stack. If there is no conflict, the * UnparsedEntity is sent by the root pipeline. * * @param ent the UnparsedEntity to check for conflicts */ protected void checkAndSendUnparsedEntity(UnparsedEntity ent) { if (isRootDocument()) { int index = fUnparsedEntities.indexOf(ent); if (index == -1) { // There is no unparsed entity with the same name that we have sent. // Calling unparsedEntityDecl() will add the entity to our local store, // and also send the unparsed entity to the DTDHandler XMLResourceIdentifier id = new XMLResourceIdentifierImpl( ent.publicId, ent.systemId, ent.baseURI, null); this.addUnparsedEntity( ent.name, id, ent.notation, ent.augmentations); if (fSendUEAndNotationEvents && fDTDHandler != null) { fDTDHandler.unparsedEntityDecl( ent.name, id, ent.notation, ent.augmentations); } } else { UnparsedEntity localEntity = (UnparsedEntity)fUnparsedEntities.get(index); if (!ent.isDuplicate(localEntity)) { reportFatalError( "NonDuplicateUnparsedEntity", new Object[] { ent.name }); } } } else { fParentXIncludeHandler.checkAndSendUnparsedEntity(ent); } } /** * The purpose of this method is to check if a Notation conflicts with a previously * declared notation in the current pipeline stack. If there is no conflict, the * Notation is sent by the root pipeline. * * @param not the Notation to check for conflicts */ protected void checkAndSendNotation(Notation not) { if (isRootDocument()) { int index = fNotations.indexOf(not); if (index == -1) { // There is no notation with the same name that we have sent. XMLResourceIdentifier id = new XMLResourceIdentifierImpl( not.publicId, not.systemId, not.baseURI, null); this.addNotation(not.name, id, not.augmentations); if (fSendUEAndNotationEvents && fDTDHandler != null) { fDTDHandler.notationDecl(not.name, id, not.augmentations); } } else { Notation localNotation = (Notation)fNotations.get(index); if (!not.isDuplicate(localNotation)) { reportFatalError( "NonDuplicateNotation", new Object[] { not.name }); } } } else { fParentXIncludeHandler.checkAndSendNotation(not); } } // It would be nice if we didn't have to repeat code like this, but there's no interface that has // setFeature() and addRecognizedFeatures() that the objects have in common. protected void copyFeatures( XMLComponentManager from, ParserConfigurationSettings to) { Enumeration features = Constants.getXercesFeatures(); copyFeatures1(features, Constants.XERCES_FEATURE_PREFIX, from, to); features = Constants.getSAXFeatures(); copyFeatures1(features, Constants.SAX_FEATURE_PREFIX, from, to); } protected void copyFeatures( XMLComponentManager from, XMLParserConfiguration to) { Enumeration features = Constants.getXercesFeatures(); copyFeatures1(features, Constants.XERCES_FEATURE_PREFIX, from, to); features = Constants.getSAXFeatures(); copyFeatures1(features, Constants.SAX_FEATURE_PREFIX, from, to); } private void copyFeatures1( Enumeration features, String featurePrefix, XMLComponentManager from, ParserConfigurationSettings to) { while (features.hasMoreElements()) { String featureId = featurePrefix + (String)features.nextElement(); to.addRecognizedFeatures(new String[] { featureId }); try { to.setFeature(featureId, from.getFeature(featureId)); } catch (XMLConfigurationException e) { // componentManager doesn't support this feature, // so we won't worry about it } } } private void copyFeatures1( Enumeration features, String featurePrefix, XMLComponentManager from, XMLParserConfiguration to) { while (features.hasMoreElements()) { String featureId = featurePrefix + (String)features.nextElement(); boolean value = from.getFeature(featureId); try { to.setFeature(featureId, value); } catch (XMLConfigurationException e) { // componentManager doesn't support this feature, // so we won't worry about it } } } // This is a storage class to hold information about the notations. // We're not using XMLNotationDecl because we don't want to lose the augmentations. protected class Notation { public String name; public String systemId; public String baseURI; public String publicId; public Augmentations augmentations; // equals() returns true if two Notations have the same name. // Useful for searching Vectors for notations with the same name public boolean equals(Object obj) { if (obj == null) { return false; } if (obj instanceof Notation) { Notation other = (Notation)obj; return name.equals(other.name); } return false; } // from 4.5.2 // Notation items with the same [name], [system identifier], // [public identifier], and [declaration base URI] are considered // to be duplicate public boolean isDuplicate(Object obj) { if (obj != null && obj instanceof Notation) { Notation other = (Notation)obj; return name.equals(other.name) && (systemId == other.systemId || (systemId != null && systemId.equals(other.systemId))) && (publicId == other.publicId || (publicId != null && publicId.equals(other.publicId))) && (baseURI == other.baseURI || (baseURI != null && baseURI.equals(other.baseURI))); } return false; } } // This is a storage class to hold information about the unparsed entities. // We're not using XMLEntityDecl because we don't want to lose the augmentations. protected class UnparsedEntity { public String name; public String systemId; public String baseURI; public String publicId; public String notation; public Augmentations augmentations; // equals() returns true if two UnparsedEntities have the same name. // Useful for searching Vectors for entities with the same name public boolean equals(Object obj) { if (obj == null) { return false; } if (obj instanceof UnparsedEntity) { UnparsedEntity other = (UnparsedEntity)obj; return name.equals(other.name); } return false; } // from 4.5.1: // Unparsed entity items with the same [name], [system identifier], // [public identifier], [declaration base URI], [notation name], and // [notation] are considered to be duplicate public boolean isDuplicate(Object obj) { if (obj != null && obj instanceof UnparsedEntity) { UnparsedEntity other = (UnparsedEntity)obj; return name.equals(other.name) && (systemId == other.systemId || (systemId != null && systemId.equals(other.systemId))) && (publicId == other.publicId || (publicId != null && publicId.equals(other.publicId))) && (baseURI == other.baseURI || (baseURI != null && baseURI.equals(other.baseURI))) && (notation == other.notation || (notation != null && notation.equals(other.notation))); } return false; } } // The following methods are used for XML Base processing /** * Saves the current base URI to the top of the stack. */ protected void saveBaseURI() { baseURIScope.push(fDepth); baseURI.push(fCurrentBaseURI.getBaseSystemId()); literalSystemID.push(fCurrentBaseURI.getLiteralSystemId()); expandedSystemID.push(fCurrentBaseURI.getExpandedSystemId()); } /** * Discards the URIs at the top of the stack, and restores the ones beneath it. */ protected void restoreBaseURI() { baseURI.pop(); literalSystemID.pop(); expandedSystemID.pop(); baseURIScope.pop(); fCurrentBaseURI.setBaseSystemId((String)baseURI.peek()); fCurrentBaseURI.setLiteralSystemId((String)literalSystemID.peek()); fCurrentBaseURI.setExpandedSystemId((String)expandedSystemID.peek()); } /** * Gets the base URI that was in use at that depth * @param depth * @return the base URI */ public String getBaseURI(int depth) { int scope = scopeOf(depth); return (String)expandedSystemID.elementAt(scope); } /** * Returns a relative URI, which when resolved against the base URI at the * specified depth, will create the current base URI. * This is accomplished by merged the literal system IDs. * @param depth the depth at which to start creating the relative URI * @return a relative URI to convert the base URI at the given depth to the current * base URI */ public String getRelativeURI(int depth) throws MalformedURIException { // The literal system id at the location given by "start" is *in focus* at // the given depth. So we need to adjust it to the next scope, so that we // only process out of focus literal system ids int start = scopeOf(depth) + 1; if (start == baseURIScope.size()) { // If that is the last system id, then we don't need a relative URI return ""; } URI uri = new URI("file", (String)literalSystemID.elementAt(start)); for (int i = start + 1; i < baseURIScope.size(); i++) { uri = new URI(uri, (String)literalSystemID.elementAt(i)); } return uri.getPath(); } // We need to find two consecutive elements in the scope stack, // such that the first is lower than 'depth' (or equal), and the // second is higher. private int scopeOf(int depth) { for (int i = baseURIScope.size() - 1; i >= 0; i--) { if (baseURIScope.elementAt(i) <= depth) return i; } // we should never get here, because 0 was put on the stack in startDocument() return -1; } /** * Search for a xml:base attribute, and if one is found, put the new base URI into * effect. */ protected void processXMLBaseAttributes(XMLAttributes attributes) { String baseURIValue = attributes.getValue(NamespaceContext.XML_URI, "base"); if (baseURIValue != null) { try { String expandedValue = XMLEntityManager.expandSystemId( baseURIValue, fCurrentBaseURI.getExpandedSystemId(), false); fCurrentBaseURI.setLiteralSystemId(baseURIValue); fCurrentBaseURI.setBaseSystemId( fCurrentBaseURI.getExpandedSystemId()); fCurrentBaseURI.setExpandedSystemId(expandedValue); // push the new values on the stack saveBaseURI(); } catch (MalformedURIException e) { // REVISIT: throw error here } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy