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 is the next generation of high performance, fully compliant XML parsers in the Apache Xerces family. This new version of Xerces introduces 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 is a fully conforming XML Schema 1.0 processor. A partial experimental implementation of the XML Schema 1.1 Structures and Datatypes Working Drafts (December 2009) and an experimental implementation of the XML Schema Definition Language (XSD): Component Designators (SCD) Candidate Recommendation (January 2010) are 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.

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.xerces.xinclude;

import java.io.CharConversionException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Stack;
import java.util.StringTokenizer;

import org.apache.xerces.impl.Constants;
import org.apache.xerces.impl.XMLEntityManager;
import org.apache.xerces.impl.XMLErrorReporter;
import org.apache.xerces.impl.io.MalformedByteSequenceException;
import org.apache.xerces.impl.msg.XMLMessageFormatter;
import org.apache.xerces.util.AugmentationsImpl;
import org.apache.xerces.util.HTTPInputSource;
import org.apache.xerces.util.IntStack;
import org.apache.xerces.util.ParserConfigurationSettings;
import org.apache.xerces.util.SecurityManager;
import org.apache.xerces.util.SymbolTable;
import org.apache.xerces.util.URI;
import org.apache.xerces.util.XMLAttributesImpl;
import org.apache.xerces.util.XMLChar;
import org.apache.xerces.util.XMLLocatorWrapper;
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;
import org.apache.xerces.xpointer.XPointerHandler;
import org.apache.xerces.xpointer.XPointerProcessor;

/**
 * 

* 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/2001/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
  • *
* Optional property: *
    *
  • http://apache.org/xml/properties/input-buffer-size
  • *
* * 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 * @author Michael Glavassevich, IBM * * @version $Id: XIncludeHandler.java 778245 2009-05-24 22:27:32Z mrglavas $ * * @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 XPOINTER = "xpointer"; public final static String XINCLUDE_NS_URI = "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(); // 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 private final static String XINCLUDE_BASE = "base".intern(); private final static QName XML_BASE_QNAME = new QName( XMLSymbols.PREFIX_XML, XINCLUDE_BASE, (XMLSymbols.PREFIX_XML + ":" + XINCLUDE_BASE).intern(), NamespaceContext.XML_URI); // used for adding [language] attributes private final static String XINCLUDE_LANG = "lang".intern(); private final static QName XML_LANG_QNAME = new QName( XMLSymbols.PREFIX_XML, XINCLUDE_LANG, (XMLSymbols.PREFIX_XML + ":" + XINCLUDE_LANG).intern(), NamespaceContext.XML_URI); private 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: validation. */ protected static final String VALIDATION = Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE; /** Feature identifier: schema validation. */ protected static final String SCHEMA_VALIDATION = Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_VALIDATION_FEATURE; /** Feature identifier: dynamic validation. */ protected static final String DYNAMIC_VALIDATION = Constants.XERCES_FEATURE_PREFIX + Constants.DYNAMIC_VALIDATION_FEATURE; /** 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; /** Feature identifier: fixup base URIs. */ protected static final String XINCLUDE_FIXUP_BASE_URIS = Constants.XERCES_FEATURE_PREFIX + Constants.XINCLUDE_FIXUP_BASE_URIS_FEATURE; /** Feature identifier: fixup language. */ protected static final String XINCLUDE_FIXUP_LANGUAGE = Constants.XERCES_FEATURE_PREFIX + Constants.XINCLUDE_FIXUP_LANGUAGE_FEATURE; /** Property identifier: JAXP schema language. */ protected static final String JAXP_SCHEMA_LANGUAGE = Constants.JAXP_PROPERTY_PREFIX + Constants.SCHEMA_LANGUAGE; /** Property identifier: symbol table. */ protected static final String SYMBOL_TABLE = Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY; /** 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; /** property identifier: buffer size. */ protected static final String BUFFER_SIZE = Constants.XERCES_PROPERTY_PREFIX + Constants.BUFFER_SIZE_PROPERTY; protected static final String PARSER_SETTINGS = Constants.XERCES_FEATURE_PREFIX + Constants.PARSER_SETTINGS; /** Recognized features. */ private static final String[] RECOGNIZED_FEATURES = { ALLOW_UE_AND_NOTATION_EVENTS, XINCLUDE_FIXUP_BASE_URIS, XINCLUDE_FIXUP_LANGUAGE }; /** Feature defaults. */ private static final Boolean[] FEATURE_DEFAULTS = { Boolean.TRUE, Boolean.TRUE, Boolean.TRUE }; /** Recognized properties. */ private static final String[] RECOGNIZED_PROPERTIES = { ERROR_REPORTER, ENTITY_RESOLVER, SECURITY_MANAGER, BUFFER_SIZE }; /** Property defaults. */ private static final Object[] PROPERTY_DEFAULTS = { null, null, null, new Integer(XMLEntityManager.DEFAULT_BUFFER_SIZE) }; // instance variables // for XMLDocumentFilter protected XMLDocumentHandler fDocumentHandler; protected XMLDocumentSource fDocumentSource; // for XMLDTDFilter protected XMLDTDHandler fDTDHandler; protected XMLDTDSource fDTDSource; // for XIncludeHandler protected XIncludeHandler fParentXIncludeHandler; // for buffer size in XIncludeTextReader protected int fBufferSize = XMLEntityManager.DEFAULT_BUFFER_SIZE; // It "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; // The cached child parser configuration, may contain a // XInclude or XPointer Handler. Cache both these protected XMLParserConfiguration fXIncludeChildConfig; protected XMLParserConfiguration fXPointerChildConfig; // The XPointerProcessor protected XPointerProcessor fXPtrProcessor = null; protected XMLLocator fDocLocation; protected XMLLocatorWrapper fXIncludeLocator = new XMLLocatorWrapper(); protected XIncludeMessageFormatter fXIncludeMessageFormatter = new XIncludeMessageFormatter(); protected XIncludeNamespaceSupport fNamespaceContext; protected SymbolTable fSymbolTable; protected XMLErrorReporter fErrorReporter; protected XMLEntityResolver fEntityResolver; protected SecurityManager fSecurityManager; // these are needed for text include processing protected XIncludeTextReader fXInclude10TextReader; protected XIncludeTextReader fXInclude11TextReader; // these are needed for XML Base processing protected final XMLResourceIdentifier fCurrentBaseURI; protected final IntStack fBaseURIScope; protected final Stack fBaseURI; protected final Stack fLiteralSystemID; protected final Stack fExpandedSystemID; // these are needed for Language Fixup protected final IntStack fLanguageScope; protected final Stack fLanguageStack; protected String fCurrentLanguage; protected String fHrefFromParent; // 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; // The current element depth of the result infoset. private int fResultDepth; // 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 final ArrayList fNotations; private final ArrayList fUnparsedEntities; // flags which control whether base URI or language fixup is performed. private boolean fFixupBaseURIs = true; private boolean fFixupLanguage = true; // 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; // tracks whether content has been reported on the child pipeline boolean fHasIncludeReportedContent; // track whether the root element of the result infoset has been processed private boolean fSeenRootElement; // track whether the child config needs its features refreshed private boolean fNeedCopyFeatures = true; // Constructors public XIncludeHandler() { fDepth = 0; fSawFallback[fDepth] = false; fSawInclude[fDepth] = false; fState[fDepth] = STATE_NORMAL_PROCESSING; fNotations = new ArrayList(); fUnparsedEntities = new ArrayList(); fBaseURIScope = new IntStack(); fBaseURI = new Stack(); fLiteralSystemID = new Stack(); fExpandedSystemID = new Stack(); fCurrentBaseURI = new XMLResourceIdentifierImpl(); fLanguageScope = new IntStack(); fLanguageStack = new Stack(); fCurrentLanguage = null; } // XMLComponent methods public void reset(XMLComponentManager componentManager) throws XNIException { fNamespaceContext = null; fDepth = 0; fResultDepth = isRootDocument() ? 0 : fParentXIncludeHandler.getResultDepth(); fNotations.clear(); fUnparsedEntities.clear(); fParentRelativeURI = null; fIsXML11 = false; fInDTD = false; fSeenRootElement = false; fBaseURIScope.clear(); fBaseURI.clear(); fLiteralSystemID.clear(); fExpandedSystemID.clear(); fLanguageScope.clear(); fLanguageStack.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 { if (!componentManager.getFeature(PARSER_SETTINGS)) { // if parser settings have not changed return. return; } } catch (XMLConfigurationException e) {} // parser settings changed. Need to refresh features on child config. fNeedCopyFeatures = true; try { fSendUEAndNotationEvents = componentManager.getFeature(ALLOW_UE_AND_NOTATION_EVENTS); if (fChildConfig != null) { fChildConfig.setFeature( ALLOW_UE_AND_NOTATION_EVENTS, fSendUEAndNotationEvents); } } catch (XMLConfigurationException e) { } try { fFixupBaseURIs = componentManager.getFeature(XINCLUDE_FIXUP_BASE_URIS); if (fChildConfig != null) { fChildConfig.setFeature( XINCLUDE_FIXUP_BASE_URIS, fFixupBaseURIs); } } catch (XMLConfigurationException e) { fFixupBaseURIs = true; } try { fFixupLanguage = componentManager.getFeature(XINCLUDE_FIXUP_LANGUAGE); if (fChildConfig != null) { fChildConfig.setFeature( XINCLUDE_FIXUP_LANGUAGE, fFixupLanguage); } } catch (XMLConfigurationException e) { fFixupLanguage = true; } // Get symbol table. try { SymbolTable value = (SymbolTable)componentManager.getProperty(SYMBOL_TABLE); if (value != null) { fSymbolTable = value; if (fChildConfig != null) { fChildConfig.setProperty(SYMBOL_TABLE, value); } } } catch (XMLConfigurationException e) { fSymbolTable = null; } // 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; } // Get buffer size. try { Integer value = (Integer)componentManager.getProperty( BUFFER_SIZE); if (value != null && value.intValue() > 0) { fBufferSize = value.intValue(); if (fChildConfig != null) { fChildConfig.setProperty(BUFFER_SIZE, value); } } else { fBufferSize = ((Integer)getPropertyDefault(BUFFER_SIZE)).intValue(); } } catch (XMLConfigurationException e) { fBufferSize = ((Integer)getPropertyDefault(BUFFER_SIZE)).intValue(); } // Reset XML 1.0 text reader. if (fXInclude10TextReader != null) { fXInclude10TextReader.setBufferSize(fBufferSize); } // Reset XML 1.1 text reader. if (fXInclude11TextReader != null) { fXInclude11TextReader.setBufferSize(fBufferSize); } fSettings = new ParserConfigurationSettings(); copyFeatures(componentManager, fSettings); // We don't want a schema validator on the new pipeline, // so if it was enabled, we set the feature to false. try { if (componentManager.getFeature(SCHEMA_VALIDATION)) { fSettings.setFeature(SCHEMA_VALIDATION, false); // If the value of the JAXP 1.2 schema language property // is http://www.w3.org/2001/XMLSchema we're only validating // against XML schema so we disable validation on the new pipeline. if (Constants.NS_XMLSCHEMA.equals(componentManager.getProperty(JAXP_SCHEMA_LANGUAGE))) { fSettings.setFeature(VALIDATION, false); } // If the validation feature was also enabled we turn on // dynamic validation, so that DTD validation is performed // on the included documents only if they have a DOCTYPE. // This is consistent with the behaviour on the main pipeline. else if (componentManager.getFeature(VALIDATION)) { fSettings.setFeature(DYNAMIC_VALIDATION, true); } } } catch (XMLConfigurationException e) {} // 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 (String[])(RECOGNIZED_FEATURES.clone()); } // 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) { fNeedCopyFeatures = true; 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 (String[])(RECOGNIZED_PROPERTIES.clone()); } // 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(SYMBOL_TABLE)) { fSymbolTable = (SymbolTable)value; if (fChildConfig != null) { fChildConfig.setProperty(propertyId, value); } return; } if (propertyId.equals(ERROR_REPORTER)) { setErrorReporter((XMLErrorReporter)value); if (fChildConfig != null) { fChildConfig.setProperty(propertyId, value); } return; } if (propertyId.equals(ENTITY_RESOLVER)) { fEntityResolver = (XMLEntityResolver)value; if (fChildConfig != null) { fChildConfig.setProperty(propertyId, value); } return; } if (propertyId.equals(SECURITY_MANAGER)) { fSecurityManager = (SecurityManager)value; if (fChildConfig != null) { fChildConfig.setProperty(propertyId, value); } return; } if (propertyId.equals(BUFFER_SIZE)) { Integer bufferSize = (Integer) value; if (fChildConfig != null) { fChildConfig.setProperty(propertyId, value); } if (bufferSize != null && bufferSize.intValue() > 0) { fBufferSize = bufferSize.intValue(); // Reset XML 1.0 text reader. if (fXInclude10TextReader != null) { fXInclude10TextReader.setBufferSize(fBufferSize); } // Reset XML 1.1 text reader. if (fXInclude11TextReader != null) { fXInclude11TextReader.setBufferSize(fBufferSize); } } 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 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) { if (fDocumentHandler != handler) { fDocumentHandler = handler; if (fXIncludeChildConfig != null) { fXIncludeChildConfig.setDocumentHandler(handler); } if (fXPointerChildConfig != null) { fXPointerChildConfig.setDocumentHandler(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 (!(namespaceContext instanceof XIncludeNamespaceSupport)) { reportFatalError("IncompatibleNamespaceContext"); } fNamespaceContext = (XIncludeNamespaceSupport)namespaceContext; fDocLocation = locator; fXIncludeLocator.setLocator(fDocLocation); // initialize the current base URI setupCurrentBaseURI(locator); saveBaseURI(); if (augs == null) { augs = new AugmentationsImpl(); } augs.putItem(CURRENT_BASE_URI, fCurrentBaseURI); // abort here if we detect a recursive include if (!isRootDocument()) { fParentXIncludeHandler.fHasIncludeReportedContent = true; if (fParentXIncludeHandler.searchForRecursiveIncludes( fCurrentBaseURI.getExpandedSystemId())) { reportFatalError( "RecursiveInclude", new Object[] { fCurrentBaseURI.getExpandedSystemId()}); } } // initialize the current language fCurrentLanguage = XMLSymbols.EMPTY_STRING; saveLanguage(fCurrentLanguage); if (isRootDocument() && fDocumentHandler != null) { fDocumentHandler.startDocument( fXIncludeLocator, 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++; int lastState = getState(fDepth - 1); // If the last two states were fallback then this must be a descendant of an include // child which isn't a fallback. The specification says we should ignore such elements // and their children. if (lastState == STATE_EXPECT_FALLBACK && getState(fDepth - 2) == STATE_EXPECT_FALLBACK) { setState(STATE_IGNORE); } else { setState(lastState); } // we process the xml:base and xml:lang attributes regardless // of what type of element it is. processXMLBaseAttributes(attributes); if (fFixupLanguage) { processXMLLangAttributes(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 (hasXIncludeNamespace(element)) { if (getSawInclude(fDepth - 1)) { reportFatalError( "IncludeChild", new Object[] { element.rawname }); } if (getSawFallback(fDepth - 1)) { reportFatalError( "FallbackChild", new Object[] { element.rawname }); } if (getState() == STATE_NORMAL_PROCESSING) { if (fResultDepth++ == 0) { checkMultipleRootElements(); } if (fDocumentHandler != null) { augs = modifyAugmentations(augs); attributes = processAttributes(attributes); fDocumentHandler.startElement(element, attributes, augs); } } } else if (getState() == STATE_NORMAL_PROCESSING) { if (fResultDepth++ == 0) { checkMultipleRootElements(); } if (fDocumentHandler != null) { augs = modifyAugmentations(augs); attributes = processAttributes(attributes); fDocumentHandler.startElement(element, attributes, augs); } } } public void emptyElement( QName element, XMLAttributes attributes, Augmentations augs) throws XNIException { fDepth++; int lastState = getState(fDepth - 1); // If the last two states were fallback then this must be a descendant of an include // child which isn't a fallback. The specification says we should ignore such elements // and their children. if (lastState == STATE_EXPECT_FALLBACK && getState(fDepth - 2) == STATE_EXPECT_FALLBACK) { setState(STATE_IGNORE); } else { setState(lastState); } // we process the xml:base and xml:lang attributes regardless // of what type of element it is. processXMLBaseAttributes(attributes); if (fFixupLanguage) { processXMLLangAttributes(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 }); } if (getSawFallback(fDepth - 1)) { reportFatalError( "FallbackChild", new Object[] { element.rawname }); } if (getState() == STATE_NORMAL_PROCESSING) { if (fResultDepth == 0) { checkMultipleRootElements(); } if (fDocumentHandler != null) { augs = modifyAugmentations(augs); attributes = processAttributes(attributes); fDocumentHandler.emptyElement(element, attributes, augs); } } } else if (getState() == STATE_NORMAL_PROCESSING) { if (fResultDepth == 0) { checkMultipleRootElements(); } if (fDocumentHandler != null) { augs = modifyAugmentations(augs); attributes = processAttributes(attributes); fDocumentHandler.emptyElement(element, attributes, augs); } } // reset the out of scope stack elements setSawFallback(fDepth + 1, false); setSawInclude(fDepth, false); // check if an xml:base has gone out of scope if (fBaseURIScope.size() > 0 && fDepth == fBaseURIScope.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 (getState() == STATE_NORMAL_PROCESSING) { --fResultDepth; if (fDocumentHandler != null) { fDocumentHandler.endElement(element, augs); } } // reset the out of scope stack elements setSawFallback(fDepth + 1, false); setSawInclude(fDepth, false); // check if an xml:base has gone out of scope if (fBaseURIScope.size() > 0 && fDepth == fBaseURIScope.peek()) { // pop the values from the stack restoreBaseURI(); } // check if an xml:lang has gone out of scope if (fLanguageScope.size() > 0 && fDepth == fLanguageScope.peek()) { // pop the language from the stack fCurrentLanguage = restoreLanguage(); } fDepth--; } public void startGeneralEntity( String name, XMLResourceIdentifier resId, String encoding, Augmentations augs) throws XNIException { if (getState() == STATE_NORMAL_PROCESSING) { if (fResultDepth == 0) { if (augs != null && Boolean.TRUE.equals(augs.getItem(Constants.ENTITY_SKIPPED))) { reportFatalError("UnexpandedEntityReferenceIllegal"); } } else if (fDocumentHandler != null) { 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 && fResultDepth != 0) { fDocumentHandler.endGeneralEntity(name, augs); } } public void characters(XMLString text, Augmentations augs) throws XNIException { if (getState() == STATE_NORMAL_PROCESSING) { if (fResultDepth == 0) { checkWhitespace(text); } else if (fDocumentHandler != null) { // 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 && fResultDepth != 0) { fDocumentHandler.ignorableWhitespace(text, augs); } } public void startCDATA(Augmentations augs) throws XNIException { if (fDocumentHandler != null && getState() == STATE_NORMAL_PROCESSING && fResultDepth != 0) { fDocumentHandler.startCDATA(augs); } } public void endCDATA(Augmentations augs) throws XNIException { if (fDocumentHandler != null && getState() == STATE_NORMAL_PROCESSING && fResultDepth != 0) { fDocumentHandler.endCDATA(augs); } } public void endDocument(Augmentations augs) throws XNIException { if (isRootDocument()) { if (!fSeenRootElement) { reportFatalError("RootElementRequired"); } if (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, fXIncludeMessageFormatter); // this ensures the proper location is displayed in error messages if (fDocLocation != null) { fErrorReporter.setDocumentLocator(fDocLocation); } } } protected void handleFallbackElement() { if (!getSawInclude(fDepth - 1)) { if (getState() == STATE_IGNORE) { return; } reportFatalError("FallbackParent"); } setSawInclude(fDepth, false); fNamespaceContext.setContextInvalid(); 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 { if (getSawInclude(fDepth - 1)) { reportFatalError("IncludeChild", new Object[] { XINCLUDE_INCLUDE }); } if (getState() == STATE_IGNORE) { return true; } setSawInclude(fDepth, true); fNamespaceContext.setContextInvalid(); // 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); String accept = attributes.getValue(XINCLUDE_ATTR_ACCEPT); String acceptLanguage = attributes.getValue(XINCLUDE_ATTR_ACCEPT_LANGUAGE); if (parse == null) { parse = XINCLUDE_PARSE_XML; } if (href == null) { href = XMLSymbols.EMPTY_STRING; } if (href.length() == 0 && XINCLUDE_PARSE_XML.equals(parse)) { if (xpointer == null) { reportFatalError("XpointerMissing"); } else { // When parse="xml" and an xpointer is specified treat // all absences of the href attribute as a resource error. Locale locale = (fErrorReporter != null) ? fErrorReporter.getLocale() : null; String reason = fXIncludeMessageFormatter.formatMessage(locale, "XPointerStreamability", null); reportResourceError("XMLResourceError", new Object[] { href, reason }); return false; } } URI hrefURI = null; // Check whether href is correct and perform escaping as per section 4.1.1 of the XInclude spec. // Report fatal error if the href value contains a fragment identifier or if the value after // escaping is a syntactically invalid URI or IRI. try { hrefURI = new URI(href, true); if (hrefURI.getFragment() != null) { reportFatalError("HrefFragmentIdentifierIllegal", new Object[] {href}); } } catch (URI.MalformedURIException exc) { String newHref = escapeHref(href); if (href != newHref) { href = newHref; try { hrefURI = new URI(href, true); if (hrefURI.getFragment() != null) { reportFatalError("HrefFragmentIdentifierIllegal", new Object[] {href}); } } catch (URI.MalformedURIException exc2) { reportFatalError("HrefSyntacticallyInvalid", new Object[] {href}); } } else { reportFatalError("HrefSyntacticallyInvalid", new Object[] {href}); } } // Verify that if an accept and/or an accept-language attribute exist // that the value(s) don't contain disallowed characters. if (accept != null && !isValidInHTTPHeader(accept)) { reportFatalError("AcceptMalformed", null); accept = null; } if (acceptLanguage != null && !isValidInHTTPHeader(acceptLanguage)) { reportFatalError("AcceptLanguageMalformed", null); acceptLanguage = null; } 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); if (includedSource != null && !(includedSource instanceof HTTPInputSource) && (accept != null || acceptLanguage != null) && includedSource.getCharacterStream() == null && includedSource.getByteStream() == null) { includedSource = createInputSource(includedSource.getPublicId(), includedSource.getSystemId(), includedSource.getBaseSystemId(), accept, acceptLanguage); } } catch (IOException e) { reportResourceError( "XMLResourceError", new Object[] { href, e.getMessage()}, e); return false; } } if (includedSource == null) { // setup an HTTPInputSource if either of the content negotation attributes were specified. if (accept != null || acceptLanguage != null) { includedSource = createInputSource(null, href, fCurrentBaseURI.getExpandedSystemId(), accept, acceptLanguage); } else { 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 ((xpointer != null && fXPointerChildConfig == null) || (xpointer == null && fXIncludeChildConfig == null) ) { String parserName = XINCLUDE_DEFAULT_CONFIGURATION; if (xpointer != null) parserName = "org.apache.xerces.parsers.XPointerParserConfiguration"; fChildConfig = (XMLParserConfiguration)ObjectFactory.newInstance( parserName, ObjectFactory.findClassLoader(), true); // use the same symbol table, error reporter, entity resolver, security manager and buffer size. if (fSymbolTable != null) fChildConfig.setProperty(SYMBOL_TABLE, fSymbolTable); if (fErrorReporter != null) fChildConfig.setProperty(ERROR_REPORTER, fErrorReporter); if (fEntityResolver != null) fChildConfig.setProperty(ENTITY_RESOLVER, fEntityResolver); fChildConfig.setProperty(SECURITY_MANAGER, fSecurityManager); fChildConfig.setProperty(BUFFER_SIZE, new Integer(fBufferSize)); // features must be copied to child configuration fNeedCopyFeatures = true; // use the same namespace context fChildConfig.setProperty( Constants.XERCES_PROPERTY_PREFIX + Constants.NAMESPACE_CONTEXT_PROPERTY, fNamespaceContext); fChildConfig.setFeature( XINCLUDE_FIXUP_BASE_URIS, fFixupBaseURIs); fChildConfig.setFeature( XINCLUDE_FIXUP_LANGUAGE, fFixupLanguage); // If the xpointer attribute is present if (xpointer != null ) { XPointerHandler newHandler = (XPointerHandler)fChildConfig.getProperty( Constants.XERCES_PROPERTY_PREFIX + Constants.XPOINTER_HANDLER_PROPERTY); fXPtrProcessor = newHandler; // ??? ((XPointerHandler)fXPtrProcessor).setProperty( Constants.XERCES_PROPERTY_PREFIX + Constants.NAMESPACE_CONTEXT_PROPERTY, fNamespaceContext); ((XPointerHandler)fXPtrProcessor).setProperty(XINCLUDE_FIXUP_BASE_URIS, fFixupBaseURIs ? Boolean.TRUE : Boolean.FALSE); ((XPointerHandler)fXPtrProcessor).setProperty( XINCLUDE_FIXUP_LANGUAGE, fFixupLanguage ? Boolean.TRUE : Boolean.FALSE); if (fErrorReporter != null) ((XPointerHandler)fXPtrProcessor).setProperty(ERROR_REPORTER, fErrorReporter); // ??? newHandler.setParent(this); newHandler.setHref(href); newHandler.setXIncludeLocator(fXIncludeLocator); newHandler.setDocumentHandler(this.getDocumentHandler()); fXPointerChildConfig = fChildConfig; } else { XIncludeHandler newHandler = (XIncludeHandler)fChildConfig.getProperty( Constants.XERCES_PROPERTY_PREFIX + Constants.XINCLUDE_HANDLER_PROPERTY); newHandler.setParent(this); newHandler.setHref(href); newHandler.setXIncludeLocator(fXIncludeLocator); newHandler.setDocumentHandler(this.getDocumentHandler()); fXIncludeChildConfig = fChildConfig; } } // If an xpointer attribute is present if (xpointer != null ) { fChildConfig = fXPointerChildConfig; // Parse the XPointer expression try { ((XPointerProcessor)fXPtrProcessor).parseXPointer(xpointer); } catch (XNIException ex) { // report the XPointer error as a resource error reportResourceError( "XMLResourceError", new Object[] { href, ex.getMessage()}); return false; } } else { fChildConfig = fXIncludeChildConfig; } // set all features on parserConfig to match this parser configuration if (fNeedCopyFeatures) { copyFeatures(fSettings, fChildConfig); } fNeedCopyFeatures = false; try { fHasIncludeReportedContent = false; fNamespaceContext.pushScope(); fChildConfig.parse(includedSource); // necessary to make sure proper location is reported to the application and in errors fXIncludeLocator.setLocator(fDocLocation); if (fErrorReporter != null) { fErrorReporter.setDocumentLocator(fDocLocation); } // If the xpointer attribute is present if (xpointer != null ) { // and it was not resolved if (!((XPointerProcessor)fXPtrProcessor).isXPointerResolved()) { Locale locale = (fErrorReporter != null) ? fErrorReporter.getLocale() : null; String reason = fXIncludeMessageFormatter.formatMessage(locale, "XPointerResolutionUnsuccessful", null); reportResourceError("XMLResourceError", new Object[] {href, reason}); // use the fallback return false; } } } catch (XNIException e) { // necessary to make sure proper location is reported to the application and in errors fXIncludeLocator.setLocator(fDocLocation); if (fErrorReporter != null) { fErrorReporter.setDocumentLocator(fDocLocation); } reportFatalError("XMLParseError", new Object[] { href }); } catch (IOException e) { // necessary to make sure proper location is reported to the application and in errors fXIncludeLocator.setLocator(fDocLocation); if (fErrorReporter != null) { fErrorReporter.setDocumentLocator(fDocLocation); } // If the start document event has been seen on the child pipeline it // means the resource was successfully opened and we started reporting // document events. If an IOException is thrown after the start document // event we had a failure midstream and cannot recover. if (fHasIncludeReportedContent) { throw new XNIException(e); } // In other circumstances an IOException indicates that we had trouble // accessing or opening 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()}, e); 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 textReader = null; try { fHasIncludeReportedContent = false; // Setup the appropriate text reader. if (!fIsXML11) { if (fXInclude10TextReader == null) { fXInclude10TextReader = new XIncludeTextReader(includedSource, this, fBufferSize); } else { fXInclude10TextReader.setInputSource(includedSource); } textReader = fXInclude10TextReader; } else { if (fXInclude11TextReader == null) { fXInclude11TextReader = new XInclude11TextReader(includedSource, this, fBufferSize); } else { fXInclude11TextReader.setInputSource(includedSource); } textReader = fXInclude11TextReader; } textReader.setErrorReporter(fErrorReporter); textReader.parse(); } // encoding errors catch (MalformedByteSequenceException ex) { fErrorReporter.reportError(ex.getDomain(), ex.getKey(), ex.getArguments(), XMLErrorReporter.SEVERITY_FATAL_ERROR, ex); } catch (CharConversionException e) { fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, "CharConversionFailure", null, XMLErrorReporter.SEVERITY_FATAL_ERROR, e); } catch (IOException e) { // If a characters event has already been sent down the pipeline it // means the resource was successfully opened and that this IOException // is from a failure midstream from which we cannot recover. if (fHasIncludeReportedContent) { throw new XNIException(e); } reportResourceError( "TextResourceError", new Object[] { href, e.getMessage()}, e); return false; } finally { if (textReader != null) { try { textReader.close(); } catch (IOException e) { reportResourceError( "TextResourceError", new Object[] { href, e.getMessage()}, e); return false; } } } } else { reportFatalError("InvalidParseValue", new Object[] { parse }); } return true; } /** * 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 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; } /** * Checks if the element is an <include> element. The element must have * the XInclude namespace, and a local name of "include". * * @param element the element to check * @return true if the element is an <include> element * @see #hasXIncludeNamespace(QName) */ protected boolean isIncludeElement(QName element) { return element.localpart.equals(XINCLUDE_INCLUDE) && hasXIncludeNamespace(element); } /** * Checks if the element is an <fallback> element. The element must have * the XInclude namespace, and a local name of "fallback". * * @param element the element to check * @return true if the element is an <fallback; element * @see #hasXIncludeNamespace(QName) */ protected boolean isFallbackElement(QName element) { return element.localpart.equals(XINCLUDE_FALLBACK) && hasXIncludeNamespace(element); } /** * 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); } /** * Returns true if the current [language] is equivalent to the [language] that * was in effect on the include parent, taking case-insensitivity into account * as per [RFC 3066]. 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 [language] properties have the same value * taking case-insensitivity into account as per [RFC 3066]. */ protected boolean sameLanguageAsIncludeParent() { String parentLanguage = getIncludeParentLanguage(); return parentLanguage != null && parentLanguage.equalsIgnoreCase(fCurrentLanguage); } protected void setupCurrentBaseURI(XMLLocator locator) { fCurrentBaseURI.setBaseSystemId(locator.getBaseSystemId()); if (locator.getLiteralSystemId() != null) { fCurrentBaseURI.setLiteralSystemId(locator.getLiteralSystemId()); } else { fCurrentBaseURI.setLiteralSystemId(fHrefFromParent); } String expandedSystemId = locator.getExpandedSystemId(); if (expandedSystemId == null) { // attempt to expand it ourselves try { expandedSystemId = XMLEntityManager.expandSystemId( fCurrentBaseURI.getLiteralSystemId(), fCurrentBaseURI.getBaseSystemId(), false); if (expandedSystemId == null) { expandedSystemId = fCurrentBaseURI.getLiteralSystemId(); } } catch (MalformedURIException e) { reportFatalError("ExpandedSystemId"); } } fCurrentBaseURI.setExpandedSystemId(expandedSystemId); } /** * Checks if the file indicated by the given system id has already been * included in the current stack. * @param includedSysId the system id to check for inclusion * @return true if the source has already been included */ protected boolean searchForRecursiveIncludes(String includedSysId) { if (includedSysId.equals(fCurrentBaseURI.getExpandedSystemId())) { return true; } else if (fParentXIncludeHandler == null) { return false; } else { return fParentXIncludeHandler.searchForRecursiveIncludes(includedSysId); } } /** * 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 the processed XMLAttributes */ 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 (fFixupBaseURIs && !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 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 to perform language-fixup (spec 4.5.6). // We only do it to top level included elements, which have a different // [language] than their include parent. if (fFixupLanguage && !sameLanguageAsIncludeParent()) { if (attributes == null) { attributes = new XMLAttributesImpl(); } int index = attributes.addAttribute( XML_LANG_QNAME, XMLSymbols.fCDATASymbol, fCurrentLanguage); 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.prefix = null; ns.localpart = XMLSymbols.PREFIX_XMLNS; ns.rawname = XMLSymbols.PREFIX_XMLNS; int index = attributes.addAttribute( ns, XMLSymbols.fCDATASymbol, uri != null ? uri : XMLSymbols.EMPTY_STRING); attributes.setSpecified(index, true); // Need to re-declare this prefix in the current context // in order for the SAX parser to report the appropriate // start and end prefix mapping events. -- mrglavas fNamespaceContext.declarePrefix(prefix, 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; ns.rawname = (fSymbolTable != null) ? fSymbolTable.addSymbol(ns.rawname) : ns.rawname.intern(); int index = attributes.addAttribute( ns, XMLSymbols.fCDATASymbol, uri != null ? uri : XMLSymbols.EMPTY_STRING); attributes.setSpecified(index, true); // Need to re-declare this prefix in the current context // in order for the SAX parser to report the appropriate // start and end prefix mapping events. -- mrglavas fNamespaceContext.declarePrefix(prefix, 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.length() == 0) { relativeURI = fCurrentBaseURI.getLiteralSystemId(); } if (includeParentDepth == 0) { if (fParentRelativeURI == null) { fParentRelativeURI = fParentXIncludeHandler.getRelativeBaseURI(); } if (fParentRelativeURI.length() == 0) { return relativeURI; } URI base = new URI(fParentRelativeURI, true); URI uri = new URI(base, relativeURI); /** Check whether the scheme components are equal. */ final String baseScheme = base.getScheme(); final String literalScheme = uri.getScheme(); if (!isEqual(baseScheme, literalScheme)) { return relativeURI; } /** Check whether the authority components are equal. */ final String baseAuthority = base.getAuthority(); final String literalAuthority = uri.getAuthority(); if (!isEqual(baseAuthority, literalAuthority)) { return uri.getSchemeSpecificPart(); } /** * The scheme and authority components are equal, * return the path and the possible query and/or * fragment which follow. */ final String literalPath = uri.getPath(); final String literalQuery = uri.getQueryString(); final String literalFragment = uri.getFragment(); if (literalQuery != null || literalFragment != null) { StringBuffer buffer = new StringBuffer(); if (literalPath != null) { buffer.append(literalPath); } if (literalQuery != null) { buffer.append('?'); buffer.append(literalQuery); } if (literalFragment != null) { buffer.append('#'); buffer.append(literalFragment); } return buffer.toString(); } return literalPath; } 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 [language] of the include parent. * * @return the language property of the include parent. */ private String getIncludeParentLanguage() { int depth = getIncludeParentDepth(); if (!isRootDocument() && depth == 0) { return fParentXIncludeHandler.getIncludeParentLanguage(); } else { return getLanguage(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; } /** * Returns the current element depth of the result infoset. */ private int getResultDepth() { return fResultDepth; } /** * 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 true if an include was seen at the given depth, false otherwise */ protected boolean getSawInclude(int depth) { if (depth >= fSawInclude.length) { return false; } return fSawInclude[depth]; } protected void reportResourceError(String key) { this.reportResourceError(key, null); } protected void reportResourceError(String key, Object[] args) { this.reportResourceError(key, args, null); } protected void reportResourceError(String key, Object[] args, Exception exception) { this.reportError(key, args, XMLErrorReporter.SEVERITY_WARNING, exception); } protected void reportFatalError(String key) { this.reportFatalError(key, null); } protected void reportFatalError(String key, Object[] args) { this.reportFatalError(key, args, null); } protected void reportFatalError(String key, Object[] args, Exception exception) { this.reportError(key, args, XMLErrorReporter.SEVERITY_FATAL_ERROR, exception); } private void reportError(String key, Object[] args, short severity, Exception exception) { if (fErrorReporter != null) { fErrorReporter.reportError( XIncludeMessageFormatter.XINCLUDE_DOMAIN, key, args, severity, exception); } // 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; } protected void setHref(String href) { fHrefFromParent = href; } protected void setXIncludeLocator(XMLLocatorWrapper locator) { fXIncludeLocator = locator; } // 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.expandedSystemId = identifier.getExpandedSystemId(); 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.expandedSystemId = identifier.getExpandedSystemId(); 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, ent.expandedSystemId); 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, not.expandedSystemId); 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); } } /** * Checks whether the string only contains white space characters. * * @param value the text to check */ private void checkWhitespace(XMLString value) { int end = value.offset + value.length; for (int i = value.offset; i < end; ++i) { if (!XMLChar.isSpace(value.ch[i])) { reportFatalError("ContentIllegalAtTopLevel"); return; } } } /** * Checks whether the root element has already been processed. */ private void checkMultipleRootElements() { if (getRootElementProcessed()) { reportFatalError("MultipleRootElements"); } setRootElementProcessed(true); } /** * Sets whether the root element has been processed. */ private void setRootElementProcessed(boolean seenRoot) { if (isRootDocument()) { fSeenRootElement = seenRoot; return; } fParentXIncludeHandler.setRootElementProcessed(seenRoot); } /** * Returns whether the root element has been processed. */ private boolean getRootElementProcessed() { return isRootDocument() ? fSeenRootElement : fParentXIncludeHandler.getRootElementProcessed(); } // 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 static class Notation { public String name; public String systemId; public String baseURI; public String publicId; public String expandedSystemId; 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. An application may also be able to detect that // notations are duplicate through other means. For instance, the URI // resulting from combining the system identifier and the declaration // base URI is the same. public boolean isDuplicate(Object obj) { if (obj != null && obj instanceof Notation) { Notation other = (Notation)obj; return name.equals(other.name) && isEqual(publicId, other.publicId) && isEqual(expandedSystemId, other.expandedSystemId); } return false; } private boolean isEqual(String one, String two) { return (one == two || (one != null && one.equals(two))); } } // 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 static class UnparsedEntity { public String name; public String systemId; public String baseURI; public String publicId; public String expandedSystemId; 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. An application may also // be able to detect that unparsed entities are duplicate through other // means. For instance, the URI resulting from combining the system // identifier and the declaration base URI is the same. public boolean isDuplicate(Object obj) { if (obj != null && obj instanceof UnparsedEntity) { UnparsedEntity other = (UnparsedEntity)obj; return name.equals(other.name) && isEqual(publicId, other.publicId) && isEqual(expandedSystemId, other.expandedSystemId) && isEqual(notation, other.notation); } return false; } private boolean isEqual(String one, String two) { return (one == two || (one != null && one.equals(two))); } } // The following methods are used for XML Base processing /** * Saves the current base URI to the top of the stack. */ protected void saveBaseURI() { fBaseURIScope.push(fDepth); fBaseURI.push(fCurrentBaseURI.getBaseSystemId()); fLiteralSystemID.push(fCurrentBaseURI.getLiteralSystemId()); fExpandedSystemID.push(fCurrentBaseURI.getExpandedSystemId()); } /** * Discards the URIs at the top of the stack, and restores the ones beneath it. */ protected void restoreBaseURI() { fBaseURI.pop(); fLiteralSystemID.pop(); fExpandedSystemID.pop(); fBaseURIScope.pop(); fCurrentBaseURI.setBaseSystemId((String)fBaseURI.peek()); fCurrentBaseURI.setLiteralSystemId((String)fLiteralSystemID.peek()); fCurrentBaseURI.setExpandedSystemId((String)fExpandedSystemID.peek()); } // The following methods are used for language processing /** * Saves the given language on the top of the stack. * * @param language the language to push onto the stack. */ protected void saveLanguage(String language) { fLanguageScope.push(fDepth); fLanguageStack.push(language); } /** * Discards the language at the top of the stack, and returns the one beneath it. */ public String restoreLanguage() { fLanguageStack.pop(); fLanguageScope.pop(); return (String) fLanguageStack.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 = scopeOfBaseURI(depth); return (String)fExpandedSystemID.elementAt(scope); } /** * Gets the language that was in use at that depth. * @param depth * @return the language */ public String getLanguage(int depth) { int scope = scopeOfLanguage(depth); return (String)fLanguageStack.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 = scopeOfBaseURI(depth) + 1; if (start == fBaseURIScope.size()) { // If that is the last system id, then we don't need a relative URI return ""; } URI uri = new URI("file", (String)fLiteralSystemID.elementAt(start)); for (int i = start + 1; i < fBaseURIScope.size(); i++) { uri = new URI(uri, (String)fLiteralSystemID.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 scopeOfBaseURI(int depth) { for (int i = fBaseURIScope.size() - 1; i >= 0; i--) { if (fBaseURIScope.elementAt(i) <= depth) return i; } // we should never get here, because 0 was put on the stack in startDocument() return -1; } private int scopeOfLanguage(int depth) { for (int i = fLanguageScope.size() - 1; i >= 0; i--) { if (fLanguageScope.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 } } } /** * Search for a xml:lang attribute, and if one is found, put the new * [language] into effect. */ protected void processXMLLangAttributes(XMLAttributes attributes) { String language = attributes.getValue(NamespaceContext.XML_URI, "lang"); if (language != null) { fCurrentLanguage = language; saveLanguage(fCurrentLanguage); } } /** * Returns true if the given string * would be valid in an HTTP header. * * @param value string to check * @return true if the given string * would be valid in an HTTP header */ private boolean isValidInHTTPHeader (String value) { char ch; for (int i = value.length() - 1; i >= 0; --i) { ch = value.charAt(i); if (ch < 0x20 || ch > 0x7E) { return false; } } return true; } /** * Returns a new XMLInputSource from the given parameters. */ private XMLInputSource createInputSource(String publicId, String systemId, String baseSystemId, String accept, String acceptLanguage) { HTTPInputSource httpSource = new HTTPInputSource(publicId, systemId, baseSystemId); if (accept != null && accept.length() > 0) { httpSource.setHTTPRequestProperty(XIncludeHandler.HTTP_ACCEPT, accept); } if (acceptLanguage != null && acceptLanguage.length() > 0) { httpSource.setHTTPRequestProperty(XIncludeHandler.HTTP_ACCEPT_LANGUAGE, acceptLanguage); } return httpSource; } private boolean isEqual(String one, String two) { return (one == two || (one != null && one.equals(two))); } // which ASCII characters need to be escaped private static final boolean gNeedEscaping[] = new boolean[128]; // the first hex character if a character needs to be escaped private static final char gAfterEscaping1[] = new char[128]; // the second hex character if a character needs to be escaped private static final char gAfterEscaping2[] = new char[128]; private static final char[] gHexChs = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; // initialize the above 3 arrays static { char[] escChs = {' ', '<', '>', '"', '{', '}', '|', '\\', '^', '`'}; int len = escChs.length; char ch; for (int i = 0; i < len; i++) { ch = escChs[i]; gNeedEscaping[ch] = true; gAfterEscaping1[ch] = gHexChs[ch >> 4]; gAfterEscaping2[ch] = gHexChs[ch & 0xf]; } } // // Escape an href value according to (4.1.1): // // To convert the value of the href attribute to an IRI reference, the following characters must be escaped: // space #x20 // the delimiters < #x3C, > #x3E and " #x22 // the unwise characters { #x7B, } #x7D, | #x7C, \ #x5C, ^ #x5E and ` #x60 // // To convert an IRI reference to a URI reference, the following characters must also be escaped: // the Unicode plane 0 characters #xA0 - #xD7FF, #xF900-#xFDCF, #xFDF0-#xFFEF // the Unicode plane 1-14 characters #x10000-#x1FFFD ... #xE0000-#xEFFFD // private String escapeHref(String href) { int len = href.length(); int ch; StringBuffer buffer = new StringBuffer(len*3); // for each character in the href int i = 0; for (; i < len; i++) { ch = href.charAt(i); // if it's not an ASCII character (excluding 0x7F), break here, and use UTF-8 encoding if (ch > 0x7E) { break; } // abort: href does not allow this character if (ch < 0x20) { return href; } if (gNeedEscaping[ch]) { buffer.append('%'); buffer.append(gAfterEscaping1[ch]); buffer.append(gAfterEscaping2[ch]); } else { buffer.append((char)ch); } } // we saw some non-ascii character if (i < len) { // check if remainder of href contains any illegal characters before proceeding for (int j = i; j < len; ++j) { ch = href.charAt(j); if ((ch >= 0x20 && ch <= 0x7E) || (ch >= 0xA0 && ch <= 0xD7FF) || (ch >= 0xF900 && ch <= 0xFDCF) || (ch >= 0xFDF0 && ch <= 0xFFEF)) { continue; } if (XMLChar.isHighSurrogate(ch) && ++j < len) { int ch2 = href.charAt(j); if (XMLChar.isLowSurrogate(ch2)) { ch2 = XMLChar.supplemental((char)ch, (char)ch2); if (ch2 < 0xF0000 && (ch2 & 0xFFFF) <= 0xFFFD) { continue; } } } // abort: href does not allow this character return href; } // get UTF-8 bytes for the remaining sub-string byte[] bytes = null; byte b; try { bytes = href.substring(i).getBytes("UTF-8"); } catch (java.io.UnsupportedEncodingException e) { // should never happen return href; } len = bytes.length; // for each byte for (i = 0; i < len; i++) { b = bytes[i]; // for non-ASCII character: make it positive, then escape if (b < 0) { ch = b + 256; buffer.append('%'); buffer.append(gHexChs[ch >> 4]); buffer.append(gHexChs[ch & 0xf]); } else if (gNeedEscaping[b]) { buffer.append('%'); buffer.append(gAfterEscaping1[b]); buffer.append(gAfterEscaping2[b]); } else { buffer.append((char)b); } } } // If escaping happened, create a new string; // otherwise, return the original one. if (buffer.length() != len) { return buffer.toString(); } else { return href; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy