0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: package org.apache.xerces.xinclude;
0019:
0020: import java.io.CharConversionException;
0021: import java.io.IOException;
0022: import java.util.ArrayList;
0023: import java.util.Enumeration;
0024: import java.util.Locale;
0025: import java.util.Stack;
0026: import java.util.StringTokenizer;
0027:
0028: import org.apache.xerces.impl.Constants;
0029: import org.apache.xerces.impl.XMLEntityManager;
0030: import org.apache.xerces.impl.XMLErrorReporter;
0031: import org.apache.xerces.impl.io.MalformedByteSequenceException;
0032: import org.apache.xerces.impl.msg.XMLMessageFormatter;
0033: import org.apache.xerces.util.AugmentationsImpl;
0034: import org.apache.xerces.util.HTTPInputSource;
0035: import org.apache.xerces.util.IntStack;
0036: import org.apache.xerces.util.ParserConfigurationSettings;
0037: import org.apache.xerces.util.SecurityManager;
0038: import org.apache.xerces.util.SymbolTable;
0039: import org.apache.xerces.util.URI;
0040: import org.apache.xerces.util.XMLAttributesImpl;
0041: import org.apache.xerces.util.XMLLocatorWrapper;
0042: import org.apache.xerces.util.XMLResourceIdentifierImpl;
0043: import org.apache.xerces.util.XMLChar;
0044: import org.apache.xerces.util.XMLSymbols;
0045: import org.apache.xerces.util.URI.MalformedURIException;
0046: import org.apache.xerces.xni.Augmentations;
0047: import org.apache.xerces.xni.NamespaceContext;
0048: import org.apache.xerces.xni.QName;
0049: import org.apache.xerces.xni.XMLAttributes;
0050: import org.apache.xerces.xni.XMLDTDHandler;
0051: import org.apache.xerces.xni.XMLDocumentHandler;
0052: import org.apache.xerces.xni.XMLLocator;
0053: import org.apache.xerces.xni.XMLResourceIdentifier;
0054: import org.apache.xerces.xni.XMLString;
0055: import org.apache.xerces.xni.XNIException;
0056: import org.apache.xerces.xni.parser.XMLComponent;
0057: import org.apache.xerces.xni.parser.XMLComponentManager;
0058: import org.apache.xerces.xni.parser.XMLConfigurationException;
0059: import org.apache.xerces.xni.parser.XMLDTDFilter;
0060: import org.apache.xerces.xni.parser.XMLDTDSource;
0061: import org.apache.xerces.xni.parser.XMLDocumentFilter;
0062: import org.apache.xerces.xni.parser.XMLDocumentSource;
0063: import org.apache.xerces.xni.parser.XMLEntityResolver;
0064: import org.apache.xerces.xni.parser.XMLInputSource;
0065: import org.apache.xerces.xni.parser.XMLParserConfiguration;
0066: import org.apache.xerces.xpointer.XPointerHandler;
0067: import org.apache.xerces.xpointer.XPointerProcessor;
0068:
0069: /**
0070: * <p>
0071: * This is a pipeline component which performs XInclude handling, according to the
0072: * W3C specification for XML Inclusions.
0073: * </p>
0074: * <p>
0075: * This component analyzes each event in the pipeline, looking for <include>
0076: * elements. An <include> element is one which has a namespace of
0077: * <code>http://www.w3.org/2001/XInclude</code> and a localname of <code>include</code>.
0078: * When it finds an <include> element, it attempts to include the file specified
0079: * in the <code>href</code> attribute of the element. If inclusion succeeds, all
0080: * children of the <include> element are ignored (with the exception of
0081: * checking for invalid children as outlined in the specification). If the inclusion
0082: * fails, the <fallback> child of the <include> element is processed.
0083: * </p>
0084: * <p>
0085: * See the <a href="http://www.w3.org/TR/xinclude/">XInclude specification</a> for
0086: * more information on how XInclude is to be used.
0087: * </p>
0088: * <p>
0089: * This component requires the following features and properties from the
0090: * component manager that uses it:
0091: * <ul>
0092: * <li>http://xml.org/sax/features/allow-dtd-events-after-endDTD</li>
0093: * <li>http://apache.org/xml/properties/internal/error-reporter</li>
0094: * <li>http://apache.org/xml/properties/internal/entity-resolver</li>
0095: * </ul>
0096: * Optional property:
0097: * <ul>
0098: * <li>http://apache.org/xml/properties/input-buffer-size</li>
0099: * </ul>
0100: *
0101: * Furthermore, the <code>NamespaceContext</code> used in the pipeline is required
0102: * to be an instance of <code>XIncludeNamespaceSupport</code>.
0103: * </p>
0104: * <p>
0105: * Currently, this implementation has only partial support for the XInclude specification.
0106: * Specifically, it is missing support for XPointer document fragments. Thus, only whole
0107: * documents can be included using this component in the pipeline.
0108: * </p>
0109: *
0110: * @author Peter McCracken, IBM
0111: * @author Michael Glavassevich, IBM
0112: *
0113: * @version $Id: XIncludeHandler.java 572045 2007-09-02 17:32:09Z mrglavas $
0114: *
0115: * @see XIncludeNamespaceSupport
0116: */
0117: public class XIncludeHandler implements XMLComponent,
0118: XMLDocumentFilter, XMLDTDFilter {
0119:
0120: public final static String XINCLUDE_DEFAULT_CONFIGURATION = "org.apache.xerces.parsers.XIncludeParserConfiguration";
0121: public final static String HTTP_ACCEPT = "Accept";
0122: public final static String HTTP_ACCEPT_LANGUAGE = "Accept-Language";
0123: public final static String XPOINTER = "xpointer";
0124:
0125: public final static String XINCLUDE_NS_URI = "http://www.w3.org/2001/XInclude"
0126: .intern();
0127: public final static String XINCLUDE_INCLUDE = "include".intern();
0128: public final static String XINCLUDE_FALLBACK = "fallback".intern();
0129:
0130: public final static String XINCLUDE_PARSE_XML = "xml".intern();
0131: public final static String XINCLUDE_PARSE_TEXT = "text".intern();
0132:
0133: public final static String XINCLUDE_ATTR_HREF = "href".intern();
0134: public final static String XINCLUDE_ATTR_PARSE = "parse".intern();
0135: public final static String XINCLUDE_ATTR_ENCODING = "encoding"
0136: .intern();
0137: public final static String XINCLUDE_ATTR_ACCEPT = "accept".intern();
0138: public final static String XINCLUDE_ATTR_ACCEPT_LANGUAGE = "accept-language"
0139: .intern();
0140:
0141: // Top Level Information Items have [included] property in infoset
0142: public final static String XINCLUDE_INCLUDED = "[included]"
0143: .intern();
0144:
0145: /** The identifier for the Augmentation that contains the current base URI */
0146: public final static String CURRENT_BASE_URI = "currentBaseURI";
0147:
0148: // used for adding [base URI] attributes
0149: private final static String XINCLUDE_BASE = "base".intern();
0150: private final static QName XML_BASE_QNAME = new QName(
0151: XMLSymbols.PREFIX_XML, XINCLUDE_BASE,
0152: (XMLSymbols.PREFIX_XML + ":" + XINCLUDE_BASE).intern(),
0153: NamespaceContext.XML_URI);
0154:
0155: // used for adding [language] attributes
0156: private final static String XINCLUDE_LANG = "lang".intern();
0157: private final static QName XML_LANG_QNAME = new QName(
0158: XMLSymbols.PREFIX_XML, XINCLUDE_LANG,
0159: (XMLSymbols.PREFIX_XML + ":" + XINCLUDE_LANG).intern(),
0160: NamespaceContext.XML_URI);
0161:
0162: private final static QName NEW_NS_ATTR_QNAME = new QName(
0163: XMLSymbols.PREFIX_XMLNS, "", XMLSymbols.PREFIX_XMLNS + ":",
0164: NamespaceContext.XMLNS_URI);
0165:
0166: // Processing States
0167: private final static int STATE_NORMAL_PROCESSING = 1;
0168: // we go into this state after a successful include (thus we ignore the children
0169: // of the include) or after a fallback
0170: private final static int STATE_IGNORE = 2;
0171: // we go into this state after a failed include. If we don't encounter a fallback
0172: // before we reach the end include tag, it's a fatal error
0173: private final static int STATE_EXPECT_FALLBACK = 3;
0174:
0175: // recognized features and properties
0176:
0177: /** Feature identifier: validation. */
0178: protected static final String VALIDATION = Constants.SAX_FEATURE_PREFIX
0179: + Constants.VALIDATION_FEATURE;
0180:
0181: /** Feature identifier: schema validation. */
0182: protected static final String SCHEMA_VALIDATION = Constants.XERCES_FEATURE_PREFIX
0183: + Constants.SCHEMA_VALIDATION_FEATURE;
0184:
0185: /** Feature identifier: dynamic validation. */
0186: protected static final String DYNAMIC_VALIDATION = Constants.XERCES_FEATURE_PREFIX
0187: + Constants.DYNAMIC_VALIDATION_FEATURE;
0188:
0189: /** Feature identifier: allow notation and unparsed entity events to be sent out of order. */
0190: protected static final String ALLOW_UE_AND_NOTATION_EVENTS = Constants.SAX_FEATURE_PREFIX
0191: + Constants.ALLOW_DTD_EVENTS_AFTER_ENDDTD_FEATURE;
0192:
0193: /** Feature identifier: fixup base URIs. */
0194: protected static final String XINCLUDE_FIXUP_BASE_URIS = Constants.XERCES_FEATURE_PREFIX
0195: + Constants.XINCLUDE_FIXUP_BASE_URIS_FEATURE;
0196:
0197: /** Feature identifier: fixup language. */
0198: protected static final String XINCLUDE_FIXUP_LANGUAGE = Constants.XERCES_FEATURE_PREFIX
0199: + Constants.XINCLUDE_FIXUP_LANGUAGE_FEATURE;
0200:
0201: /** Property identifier: JAXP schema language. */
0202: protected static final String JAXP_SCHEMA_LANGUAGE = Constants.JAXP_PROPERTY_PREFIX
0203: + Constants.SCHEMA_LANGUAGE;
0204:
0205: /** Property identifier: symbol table. */
0206: protected static final String SYMBOL_TABLE = Constants.XERCES_PROPERTY_PREFIX
0207: + Constants.SYMBOL_TABLE_PROPERTY;
0208:
0209: /** Property identifier: error reporter. */
0210: protected static final String ERROR_REPORTER = Constants.XERCES_PROPERTY_PREFIX
0211: + Constants.ERROR_REPORTER_PROPERTY;
0212:
0213: /** Property identifier: entity resolver. */
0214: protected static final String ENTITY_RESOLVER = Constants.XERCES_PROPERTY_PREFIX
0215: + Constants.ENTITY_RESOLVER_PROPERTY;
0216:
0217: /** property identifier: security manager. */
0218: protected static final String SECURITY_MANAGER = Constants.XERCES_PROPERTY_PREFIX
0219: + Constants.SECURITY_MANAGER_PROPERTY;
0220:
0221: /** property identifier: buffer size. */
0222: protected static final String BUFFER_SIZE = Constants.XERCES_PROPERTY_PREFIX
0223: + Constants.BUFFER_SIZE_PROPERTY;
0224:
0225: protected static final String PARSER_SETTINGS = Constants.XERCES_FEATURE_PREFIX
0226: + Constants.PARSER_SETTINGS;
0227:
0228: /** Recognized features. */
0229: private static final String[] RECOGNIZED_FEATURES = {
0230: ALLOW_UE_AND_NOTATION_EVENTS, XINCLUDE_FIXUP_BASE_URIS,
0231: XINCLUDE_FIXUP_LANGUAGE };
0232:
0233: /** Feature defaults. */
0234: private static final Boolean[] FEATURE_DEFAULTS = { Boolean.TRUE,
0235: Boolean.TRUE, Boolean.TRUE };
0236:
0237: /** Recognized properties. */
0238: private static final String[] RECOGNIZED_PROPERTIES = {
0239: ERROR_REPORTER, ENTITY_RESOLVER, SECURITY_MANAGER,
0240: BUFFER_SIZE };
0241:
0242: /** Property defaults. */
0243: private static final Object[] PROPERTY_DEFAULTS = { null, null,
0244: null, new Integer(XMLEntityManager.DEFAULT_BUFFER_SIZE) };
0245:
0246: // instance variables
0247:
0248: // for XMLDocumentFilter
0249: protected XMLDocumentHandler fDocumentHandler;
0250: protected XMLDocumentSource fDocumentSource;
0251:
0252: // for XMLDTDFilter
0253: protected XMLDTDHandler fDTDHandler;
0254: protected XMLDTDSource fDTDSource;
0255:
0256: // for XIncludeHandler
0257: protected XIncludeHandler fParentXIncludeHandler;
0258:
0259: // for buffer size in XIncludeTextReader
0260: protected int fBufferSize = XMLEntityManager.DEFAULT_BUFFER_SIZE;
0261:
0262: // It "feels wrong" to store this value here. However,
0263: // calculating it can be time consuming, so we cache it.
0264: // It's never going to change in the lifetime of this XIncludeHandler
0265: protected String fParentRelativeURI;
0266:
0267: // we cache the child parser configuration, so we don't have to re-create
0268: // the objects when the parser is re-used
0269: protected XMLParserConfiguration fChildConfig;
0270:
0271: // The cached child parser configuration, may contain a
0272: // XInclude or XPointer Handler. Cache both these
0273: protected XMLParserConfiguration fXIncludeChildConfig;
0274: protected XMLParserConfiguration fXPointerChildConfig;
0275:
0276: // The XPointerProcessor
0277: protected XPointerProcessor fXPtrProcessor = null;
0278:
0279: protected XMLLocator fDocLocation;
0280: protected XMLLocatorWrapper fXIncludeLocator = new XMLLocatorWrapper();
0281: protected XIncludeMessageFormatter fXIncludeMessageFormatter = new XIncludeMessageFormatter();
0282: protected XIncludeNamespaceSupport fNamespaceContext;
0283: protected SymbolTable fSymbolTable;
0284: protected XMLErrorReporter fErrorReporter;
0285: protected XMLEntityResolver fEntityResolver;
0286: protected SecurityManager fSecurityManager;
0287:
0288: // these are needed for text include processing
0289: protected XIncludeTextReader fXInclude10TextReader;
0290: protected XIncludeTextReader fXInclude11TextReader;
0291:
0292: // these are needed for XML Base processing
0293: protected final XMLResourceIdentifier fCurrentBaseURI;
0294: protected final IntStack fBaseURIScope;
0295: protected final Stack fBaseURI;
0296: protected final Stack fLiteralSystemID;
0297: protected final Stack fExpandedSystemID;
0298:
0299: // these are needed for Language Fixup
0300: protected final IntStack fLanguageScope;
0301: protected final Stack fLanguageStack;
0302: protected String fCurrentLanguage;
0303:
0304: protected String fHrefFromParent;
0305:
0306: // used for passing features on to child XIncludeHandler objects
0307: protected ParserConfigurationSettings fSettings;
0308:
0309: // The current element depth. We start at depth 0 (before we've reached any elements).
0310: // The first element is at depth 1.
0311: private int fDepth;
0312:
0313: // The current element depth of the result infoset.
0314: private int fResultDepth;
0315:
0316: // this value must be at least 1
0317: private static final int INITIAL_SIZE = 8;
0318:
0319: // Used to ensure that fallbacks are always children of include elements,
0320: // and that include elements are never children of other include elements.
0321: // An index contains true if the ancestor of the current element which resides
0322: // at that depth was an include element.
0323: private boolean[] fSawInclude = new boolean[INITIAL_SIZE];
0324:
0325: // Ensures that only one fallback element can be at a single depth.
0326: // An index contains true if we have seen any fallback elements at that depth,
0327: // and it is only reset to false when the end tag of the parent is encountered.
0328: private boolean[] fSawFallback = new boolean[INITIAL_SIZE];
0329:
0330: // The state of the processor at each given depth.
0331: private int[] fState = new int[INITIAL_SIZE];
0332:
0333: // buffering the necessary DTD events
0334: private final ArrayList fNotations;
0335: private final ArrayList fUnparsedEntities;
0336:
0337: // flags which control whether base URI or language fixup is performed.
0338: private boolean fFixupBaseURIs = true;
0339: private boolean fFixupLanguage = true;
0340:
0341: // for SAX compatibility.
0342: // Has the value of the ALLOW_UE_AND_NOTATION_EVENTS feature
0343: private boolean fSendUEAndNotationEvents;
0344:
0345: // track the version of the document being parsed
0346: private boolean fIsXML11;
0347:
0348: // track whether a DTD is being parsed
0349: private boolean fInDTD;
0350:
0351: // tracks whether content has been reported on the child pipeline
0352: boolean fHasIncludeReportedContent;
0353:
0354: // track whether the root element of the result infoset has been processed
0355: private boolean fSeenRootElement;
0356:
0357: // track whether the child config needs its features refreshed
0358: private boolean fNeedCopyFeatures = true;
0359:
0360: // Constructors
0361:
0362: public XIncludeHandler() {
0363: fDepth = 0;
0364:
0365: fSawFallback[fDepth] = false;
0366: fSawInclude[fDepth] = false;
0367: fState[fDepth] = STATE_NORMAL_PROCESSING;
0368: fNotations = new ArrayList();
0369: fUnparsedEntities = new ArrayList();
0370:
0371: fBaseURIScope = new IntStack();
0372: fBaseURI = new Stack();
0373: fLiteralSystemID = new Stack();
0374: fExpandedSystemID = new Stack();
0375: fCurrentBaseURI = new XMLResourceIdentifierImpl();
0376:
0377: fLanguageScope = new IntStack();
0378: fLanguageStack = new Stack();
0379: fCurrentLanguage = null;
0380: }
0381:
0382: // XMLComponent methods
0383:
0384: public void reset(XMLComponentManager componentManager)
0385: throws XNIException {
0386: fNamespaceContext = null;
0387: fDepth = 0;
0388: fResultDepth = isRootDocument() ? 0 : fParentXIncludeHandler
0389: .getResultDepth();
0390: fNotations.clear();
0391: fUnparsedEntities.clear();
0392: fParentRelativeURI = null;
0393: fIsXML11 = false;
0394: fInDTD = false;
0395: fSeenRootElement = false;
0396:
0397: fBaseURIScope.clear();
0398: fBaseURI.clear();
0399: fLiteralSystemID.clear();
0400: fExpandedSystemID.clear();
0401: fLanguageScope.clear();
0402: fLanguageStack.clear();
0403:
0404: // REVISIT: Find a better method for maintaining
0405: // the state of the XInclude processor. These arrays
0406: // can potentially grow quite large. Cleaning them
0407: // out on reset may be very time consuming. -- mrglavas
0408: //
0409: // clear the previous settings from the arrays
0410: for (int i = 0; i < fState.length; ++i) {
0411: fState[i] = STATE_NORMAL_PROCESSING;
0412: }
0413: for (int i = 0; i < fSawFallback.length; ++i) {
0414: fSawFallback[i] = false;
0415: }
0416: for (int i = 0; i < fSawInclude.length; ++i) {
0417: fSawInclude[i] = false;
0418: }
0419:
0420: try {
0421: if (!componentManager.getFeature(PARSER_SETTINGS)) {
0422: // if parser settings have not changed return.
0423: return;
0424: }
0425: } catch (XMLConfigurationException e) {
0426: }
0427:
0428: // parser settings changed. Need to refresh features on child config.
0429: fNeedCopyFeatures = true;
0430:
0431: try {
0432: fSendUEAndNotationEvents = componentManager
0433: .getFeature(ALLOW_UE_AND_NOTATION_EVENTS);
0434: if (fChildConfig != null) {
0435: fChildConfig.setFeature(ALLOW_UE_AND_NOTATION_EVENTS,
0436: fSendUEAndNotationEvents);
0437: }
0438: } catch (XMLConfigurationException e) {
0439: }
0440:
0441: try {
0442: fFixupBaseURIs = componentManager
0443: .getFeature(XINCLUDE_FIXUP_BASE_URIS);
0444: if (fChildConfig != null) {
0445: fChildConfig.setFeature(XINCLUDE_FIXUP_BASE_URIS,
0446: fFixupBaseURIs);
0447: }
0448: } catch (XMLConfigurationException e) {
0449: fFixupBaseURIs = true;
0450: }
0451:
0452: try {
0453: fFixupLanguage = componentManager
0454: .getFeature(XINCLUDE_FIXUP_LANGUAGE);
0455: if (fChildConfig != null) {
0456: fChildConfig.setFeature(XINCLUDE_FIXUP_LANGUAGE,
0457: fFixupLanguage);
0458: }
0459: } catch (XMLConfigurationException e) {
0460: fFixupLanguage = true;
0461: }
0462:
0463: // Get symbol table.
0464: try {
0465: SymbolTable value = (SymbolTable) componentManager
0466: .getProperty(SYMBOL_TABLE);
0467: if (value != null) {
0468: fSymbolTable = value;
0469: if (fChildConfig != null) {
0470: fChildConfig.setProperty(SYMBOL_TABLE, value);
0471: }
0472: }
0473: } catch (XMLConfigurationException e) {
0474: fSymbolTable = null;
0475: }
0476:
0477: // Get error reporter.
0478: try {
0479: XMLErrorReporter value = (XMLErrorReporter) componentManager
0480: .getProperty(ERROR_REPORTER);
0481: if (value != null) {
0482: setErrorReporter(value);
0483: if (fChildConfig != null) {
0484: fChildConfig.setProperty(ERROR_REPORTER, value);
0485: }
0486: }
0487: } catch (XMLConfigurationException e) {
0488: fErrorReporter = null;
0489: }
0490:
0491: // Get entity resolver.
0492: try {
0493: XMLEntityResolver value = (XMLEntityResolver) componentManager
0494: .getProperty(ENTITY_RESOLVER);
0495:
0496: if (value != null) {
0497: fEntityResolver = value;
0498: if (fChildConfig != null) {
0499: fChildConfig.setProperty(ENTITY_RESOLVER, value);
0500: }
0501: }
0502: } catch (XMLConfigurationException e) {
0503: fEntityResolver = null;
0504: }
0505:
0506: // Get security manager.
0507: try {
0508: SecurityManager value = (SecurityManager) componentManager
0509: .getProperty(SECURITY_MANAGER);
0510:
0511: if (value != null) {
0512: fSecurityManager = value;
0513: if (fChildConfig != null) {
0514: fChildConfig.setProperty(SECURITY_MANAGER, value);
0515: }
0516: }
0517: } catch (XMLConfigurationException e) {
0518: fSecurityManager = null;
0519: }
0520:
0521: // Get buffer size.
0522: try {
0523: Integer value = (Integer) componentManager
0524: .getProperty(BUFFER_SIZE);
0525:
0526: if (value != null && value.intValue() > 0) {
0527: fBufferSize = value.intValue();
0528: if (fChildConfig != null) {
0529: fChildConfig.setProperty(BUFFER_SIZE, value);
0530: }
0531: } else {
0532: fBufferSize = ((Integer) getPropertyDefault(BUFFER_SIZE))
0533: .intValue();
0534: }
0535: } catch (XMLConfigurationException e) {
0536: fBufferSize = ((Integer) getPropertyDefault(BUFFER_SIZE))
0537: .intValue();
0538: }
0539:
0540: // Reset XML 1.0 text reader.
0541: if (fXInclude10TextReader != null) {
0542: fXInclude10TextReader.setBufferSize(fBufferSize);
0543: }
0544: // Reset XML 1.1 text reader.
0545: if (fXInclude11TextReader != null) {
0546: fXInclude11TextReader.setBufferSize(fBufferSize);
0547: }
0548:
0549: fSettings = new ParserConfigurationSettings();
0550: copyFeatures(componentManager, fSettings);
0551:
0552: // We don't want a schema validator on the new pipeline,
0553: // so if it was enabled, we set the feature to false.
0554: try {
0555: if (componentManager.getFeature(SCHEMA_VALIDATION)) {
0556: fSettings.setFeature(SCHEMA_VALIDATION, false);
0557: // If the value of the JAXP 1.2 schema language property
0558: // is http://www.w3.org/2001/XMLSchema we're only validating
0559: // against XML schema so we disable validation on the new pipeline.
0560: if (Constants.NS_XMLSCHEMA.equals(componentManager
0561: .getProperty(JAXP_SCHEMA_LANGUAGE))) {
0562: fSettings.setFeature(VALIDATION, false);
0563: }
0564: // If the validation feature was also enabled we turn on
0565: // dynamic validation, so that DTD validation is performed
0566: // on the included documents only if they have a DOCTYPE.
0567: // This is consistent with the behaviour on the main pipeline.
0568: else if (componentManager.getFeature(VALIDATION)) {
0569: fSettings.setFeature(DYNAMIC_VALIDATION, true);
0570: }
0571: }
0572: } catch (XMLConfigurationException e) {
0573: }
0574:
0575: // Don't reset fChildConfig -- we don't want it to share the same components.
0576: // It will be reset when it is actually used to parse something.
0577: } // reset(XMLComponentManager)
0578:
0579: /**
0580: * Returns a list of feature identifiers that are recognized by
0581: * this component. This method may return null if no features
0582: * are recognized by this component.
0583: */
0584: public String[] getRecognizedFeatures() {
0585: return (String[]) (RECOGNIZED_FEATURES.clone());
0586: } // getRecognizedFeatures():String[]
0587:
0588: /**
0589: * Sets the state of a feature. This method is called by the component
0590: * manager any time after reset when a feature changes state.
0591: * <p>
0592: * <strong>Note:</strong> Components should silently ignore features
0593: * that do not affect the operation of the component.
0594: *
0595: * @param featureId The feature identifier.
0596: * @param state The state of the feature.
0597: *
0598: * @throws SAXNotRecognizedException The component should not throw
0599: * this exception.
0600: * @throws SAXNotSupportedException The component should not throw
0601: * this exception.
0602: */
0603: public void setFeature(String featureId, boolean state)
0604: throws XMLConfigurationException {
0605: if (featureId.equals(ALLOW_UE_AND_NOTATION_EVENTS)) {
0606: fSendUEAndNotationEvents = state;
0607: }
0608: if (fSettings != null) {
0609: fNeedCopyFeatures = true;
0610: fSettings.setFeature(featureId, state);
0611: }
0612: } // setFeature(String,boolean)
0613:
0614: /**
0615: * Returns a list of property identifiers that are recognized by
0616: * this component. This method may return null if no properties
0617: * are recognized by this component.
0618: */
0619: public String[] getRecognizedProperties() {
0620: return (String[]) (RECOGNIZED_PROPERTIES.clone());
0621: } // getRecognizedProperties():String[]
0622:
0623: /**
0624: * Sets the value of a property. This method is called by the component
0625: * manager any time after reset when a property changes value.
0626: * <p>
0627: * <strong>Note:</strong> Components should silently ignore properties
0628: * that do not affect the operation of the component.
0629: *
0630: * @param propertyId The property identifier.
0631: * @param value The value of the property.
0632: *
0633: * @throws SAXNotRecognizedException The component should not throw
0634: * this exception.
0635: * @throws SAXNotSupportedException The component should not throw
0636: * this exception.
0637: */
0638: public void setProperty(String propertyId, Object value)
0639: throws XMLConfigurationException {
0640: if (propertyId.equals(SYMBOL_TABLE)) {
0641: fSymbolTable = (SymbolTable) value;
0642: if (fChildConfig != null) {
0643: fChildConfig.setProperty(propertyId, value);
0644: }
0645: return;
0646: }
0647: if (propertyId.equals(ERROR_REPORTER)) {
0648: setErrorReporter((XMLErrorReporter) value);
0649: if (fChildConfig != null) {
0650: fChildConfig.setProperty(propertyId, value);
0651: }
0652: return;
0653: }
0654: if (propertyId.equals(ENTITY_RESOLVER)) {
0655: fEntityResolver = (XMLEntityResolver) value;
0656: if (fChildConfig != null) {
0657: fChildConfig.setProperty(propertyId, value);
0658: }
0659: return;
0660: }
0661: if (propertyId.equals(SECURITY_MANAGER)) {
0662: fSecurityManager = (SecurityManager) value;
0663: if (fChildConfig != null) {
0664: fChildConfig.setProperty(propertyId, value);
0665: }
0666: return;
0667: }
0668: if (propertyId.equals(BUFFER_SIZE)) {
0669: Integer bufferSize = (Integer) value;
0670: if (fChildConfig != null) {
0671: fChildConfig.setProperty(propertyId, value);
0672: }
0673: if (bufferSize != null && bufferSize.intValue() > 0) {
0674: fBufferSize = bufferSize.intValue();
0675: // Reset XML 1.0 text reader.
0676: if (fXInclude10TextReader != null) {
0677: fXInclude10TextReader.setBufferSize(fBufferSize);
0678: }
0679: // Reset XML 1.1 text reader.
0680: if (fXInclude11TextReader != null) {
0681: fXInclude11TextReader.setBufferSize(fBufferSize);
0682: }
0683: }
0684: return;
0685: }
0686:
0687: } // setProperty(String,Object)
0688:
0689: /**
0690: * Returns the default state for a feature, or null if this
0691: * component does not want to report a default value for this
0692: * feature.
0693: *
0694: * @param featureId The feature identifier.
0695: *
0696: * @since Xerces 2.2.0
0697: */
0698: public Boolean getFeatureDefault(String featureId) {
0699: for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
0700: if (RECOGNIZED_FEATURES[i].equals(featureId)) {
0701: return FEATURE_DEFAULTS[i];
0702: }
0703: }
0704: return null;
0705: } // getFeatureDefault(String):Boolean
0706:
0707: /**
0708: * Returns the default state for a property, or null if this
0709: * component does not want to report a default value for this
0710: * property.
0711: *
0712: * @param propertyId The property identifier.
0713: *
0714: * @since Xerces 2.2.0
0715: */
0716: public Object getPropertyDefault(String propertyId) {
0717: for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
0718: if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
0719: return PROPERTY_DEFAULTS[i];
0720: }
0721: }
0722: return null;
0723: } // getPropertyDefault(String):Object
0724:
0725: public void setDocumentHandler(XMLDocumentHandler handler) {
0726: if (fDocumentHandler != handler) {
0727: fDocumentHandler = handler;
0728: if (fXIncludeChildConfig != null) {
0729: fXIncludeChildConfig.setDocumentHandler(handler);
0730: }
0731: if (fXPointerChildConfig != null) {
0732: fXPointerChildConfig.setDocumentHandler(handler);
0733: }
0734: }
0735: }
0736:
0737: public XMLDocumentHandler getDocumentHandler() {
0738: return fDocumentHandler;
0739: }
0740:
0741: // XMLDocumentHandler methods
0742:
0743: /**
0744: * Event sent at the start of the document.
0745: *
0746: * A fatal error will occur here, if it is detected that this document has been processed
0747: * before.
0748: *
0749: * This event is only passed on to the document handler if this is the root document.
0750: */
0751: public void startDocument(XMLLocator locator, String encoding,
0752: NamespaceContext namespaceContext, Augmentations augs)
0753: throws XNIException {
0754:
0755: // we do this to ensure that the proper location is reported in errors
0756: // otherwise, the locator from the root document would always be used
0757: fErrorReporter.setDocumentLocator(locator);
0758:
0759: if (!(namespaceContext instanceof XIncludeNamespaceSupport)) {
0760: reportFatalError("IncompatibleNamespaceContext");
0761: }
0762: fNamespaceContext = (XIncludeNamespaceSupport) namespaceContext;
0763: fDocLocation = locator;
0764: fXIncludeLocator.setLocator(fDocLocation);
0765:
0766: // initialize the current base URI
0767: setupCurrentBaseURI(locator);
0768: saveBaseURI();
0769: if (augs == null) {
0770: augs = new AugmentationsImpl();
0771: }
0772: augs.putItem(CURRENT_BASE_URI, fCurrentBaseURI);
0773:
0774: // abort here if we detect a recursive include
0775: if (!isRootDocument()) {
0776: fParentXIncludeHandler.fHasIncludeReportedContent = true;
0777: if (fParentXIncludeHandler
0778: .searchForRecursiveIncludes(fCurrentBaseURI
0779: .getExpandedSystemId())) {
0780: reportFatalError("RecursiveInclude",
0781: new Object[] { fCurrentBaseURI
0782: .getExpandedSystemId() });
0783: }
0784: }
0785:
0786: // initialize the current language
0787: fCurrentLanguage = XMLSymbols.EMPTY_STRING;
0788: saveLanguage(fCurrentLanguage);
0789:
0790: if (isRootDocument() && fDocumentHandler != null) {
0791: fDocumentHandler.startDocument(fXIncludeLocator, encoding,
0792: namespaceContext, augs);
0793: }
0794: }
0795:
0796: public void xmlDecl(String version, String encoding,
0797: String standalone, Augmentations augs) throws XNIException {
0798: fIsXML11 = "1.1".equals(version);
0799: if (isRootDocument() && fDocumentHandler != null) {
0800: fDocumentHandler.xmlDecl(version, encoding, standalone,
0801: augs);
0802: }
0803: }
0804:
0805: public void doctypeDecl(String rootElement, String publicId,
0806: String systemId, Augmentations augs) throws XNIException {
0807: if (isRootDocument() && fDocumentHandler != null) {
0808: fDocumentHandler.doctypeDecl(rootElement, publicId,
0809: systemId, augs);
0810: }
0811: }
0812:
0813: public void comment(XMLString text, Augmentations augs)
0814: throws XNIException {
0815: if (!fInDTD) {
0816: if (fDocumentHandler != null
0817: && getState() == STATE_NORMAL_PROCESSING) {
0818: fDepth++;
0819: augs = modifyAugmentations(augs);
0820: fDocumentHandler.comment(text, augs);
0821: fDepth--;
0822: }
0823: } else if (fDTDHandler != null) {
0824: fDTDHandler.comment(text, augs);
0825: }
0826: }
0827:
0828: public void processingInstruction(String target, XMLString data,
0829: Augmentations augs) throws XNIException {
0830: if (!fInDTD) {
0831: if (fDocumentHandler != null
0832: && getState() == STATE_NORMAL_PROCESSING) {
0833: // we need to change the depth like this so that modifyAugmentations() works
0834: fDepth++;
0835: augs = modifyAugmentations(augs);
0836: fDocumentHandler.processingInstruction(target, data,
0837: augs);
0838: fDepth--;
0839: }
0840: } else if (fDTDHandler != null) {
0841: fDTDHandler.processingInstruction(target, data, augs);
0842: }
0843: }
0844:
0845: public void startElement(QName element, XMLAttributes attributes,
0846: Augmentations augs) throws XNIException {
0847: fDepth++;
0848: int lastState = getState(fDepth - 1);
0849: // If the last two states were fallback then this must be a descendant of an include
0850: // child which isn't a fallback. The specification says we should ignore such elements
0851: // and their children.
0852: if (lastState == STATE_EXPECT_FALLBACK
0853: && getState(fDepth - 2) == STATE_EXPECT_FALLBACK) {
0854: setState(STATE_IGNORE);
0855: } else {
0856: setState(lastState);
0857: }
0858:
0859: // we process the xml:base and xml:lang attributes regardless
0860: // of what type of element it is.
0861: processXMLBaseAttributes(attributes);
0862: if (fFixupLanguage) {
0863: processXMLLangAttributes(attributes);
0864: }
0865:
0866: if (isIncludeElement(element)) {
0867: boolean success = this .handleIncludeElement(attributes);
0868: if (success) {
0869: setState(STATE_IGNORE);
0870: } else {
0871: setState(STATE_EXPECT_FALLBACK);
0872: }
0873: } else if (isFallbackElement(element)) {
0874: this .handleFallbackElement();
0875: } else if (hasXIncludeNamespace(element)) {
0876: if (getSawInclude(fDepth - 1)) {
0877: reportFatalError("IncludeChild",
0878: new Object[] { element.rawname });
0879: }
0880: if (getSawFallback(fDepth - 1)) {
0881: reportFatalError("FallbackChild",
0882: new Object[] { element.rawname });
0883: }
0884: if (getState() == STATE_NORMAL_PROCESSING) {
0885: if (fResultDepth++ == 0) {
0886: checkMultipleRootElements();
0887: }
0888: if (fDocumentHandler != null) {
0889: augs = modifyAugmentations(augs);
0890: attributes = processAttributes(attributes);
0891: fDocumentHandler.startElement(element, attributes,
0892: augs);
0893: }
0894: }
0895: } else if (getState() == STATE_NORMAL_PROCESSING) {
0896: if (fResultDepth++ == 0) {
0897: checkMultipleRootElements();
0898: }
0899: if (fDocumentHandler != null) {
0900: augs = modifyAugmentations(augs);
0901: attributes = processAttributes(attributes);
0902: fDocumentHandler
0903: .startElement(element, attributes, augs);
0904: }
0905: }
0906: }
0907:
0908: public void emptyElement(QName element, XMLAttributes attributes,
0909: Augmentations augs) throws XNIException {
0910: fDepth++;
0911: int lastState = getState(fDepth - 1);
0912: // If the last two states were fallback then this must be a descendant of an include
0913: // child which isn't a fallback. The specification says we should ignore such elements
0914: // and their children.
0915: if (lastState == STATE_EXPECT_FALLBACK
0916: && getState(fDepth - 2) == STATE_EXPECT_FALLBACK) {
0917: setState(STATE_IGNORE);
0918: } else {
0919: setState(lastState);
0920: }
0921:
0922: // we process the xml:base and xml:lang attributes regardless
0923: // of what type of element it is.
0924: processXMLBaseAttributes(attributes);
0925: if (fFixupLanguage) {
0926: processXMLLangAttributes(attributes);
0927: }
0928:
0929: if (isIncludeElement(element)) {
0930: boolean success = this .handleIncludeElement(attributes);
0931: if (success) {
0932: setState(STATE_IGNORE);
0933: } else {
0934: reportFatalError("NoFallback");
0935: }
0936: } else if (isFallbackElement(element)) {
0937: this .handleFallbackElement();
0938: } else if (hasXIncludeNamespace(element)) {
0939: if (getSawInclude(fDepth - 1)) {
0940: reportFatalError("IncludeChild",
0941: new Object[] { element.rawname });
0942: }
0943: if (getSawFallback(fDepth - 1)) {
0944: reportFatalError("FallbackChild",
0945: new Object[] { element.rawname });
0946: }
0947: if (getState() == STATE_NORMAL_PROCESSING) {
0948: if (fResultDepth == 0) {
0949: checkMultipleRootElements();
0950: }
0951: if (fDocumentHandler != null) {
0952: augs = modifyAugmentations(augs);
0953: attributes = processAttributes(attributes);
0954: fDocumentHandler.emptyElement(element, attributes,
0955: augs);
0956: }
0957: }
0958: } else if (getState() == STATE_NORMAL_PROCESSING) {
0959: if (fResultDepth == 0) {
0960: checkMultipleRootElements();
0961: }
0962: if (fDocumentHandler != null) {
0963: augs = modifyAugmentations(augs);
0964: attributes = processAttributes(attributes);
0965: fDocumentHandler
0966: .emptyElement(element, attributes, augs);
0967: }
0968: }
0969: // reset the out of scope stack elements
0970: setSawFallback(fDepth + 1, false);
0971: setSawInclude(fDepth, false);
0972:
0973: // check if an xml:base has gone out of scope
0974: if (fBaseURIScope.size() > 0 && fDepth == fBaseURIScope.peek()) {
0975: // pop the values from the stack
0976: restoreBaseURI();
0977: }
0978: fDepth--;
0979: }
0980:
0981: public void endElement(QName element, Augmentations augs)
0982: throws XNIException {
0983:
0984: if (isIncludeElement(element)) {
0985: // if we're ending an include element, and we were expecting a fallback
0986: // we check to see if the children of this include element contained a fallback
0987: if (getState() == STATE_EXPECT_FALLBACK
0988: && !getSawFallback(fDepth + 1)) {
0989: reportFatalError("NoFallback");
0990: }
0991: }
0992: if (isFallbackElement(element)) {
0993: // the state would have been set to normal processing if we were expecting the fallback element
0994: // now that we're done processing it, we should ignore all the other children of the include element
0995: if (getState() == STATE_NORMAL_PROCESSING) {
0996: setState(STATE_IGNORE);
0997: }
0998: } else if (getState() == STATE_NORMAL_PROCESSING) {
0999: --fResultDepth;
1000: if (fDocumentHandler != null) {
1001: fDocumentHandler.endElement(element, augs);
1002: }
1003: }
1004:
1005: // reset the out of scope stack elements
1006: setSawFallback(fDepth + 1, false);
1007: setSawInclude(fDepth, false);
1008:
1009: // check if an xml:base has gone out of scope
1010: if (fBaseURIScope.size() > 0 && fDepth == fBaseURIScope.peek()) {
1011: // pop the values from the stack
1012: restoreBaseURI();
1013: }
1014:
1015: // check if an xml:lang has gone out of scope
1016: if (fLanguageScope.size() > 0
1017: && fDepth == fLanguageScope.peek()) {
1018: // pop the language from the stack
1019: fCurrentLanguage = restoreLanguage();
1020: }
1021:
1022: fDepth--;
1023: }
1024:
1025: public void startGeneralEntity(String name,
1026: XMLResourceIdentifier resId, String encoding,
1027: Augmentations augs) throws XNIException {
1028: if (getState() == STATE_NORMAL_PROCESSING) {
1029: if (fResultDepth == 0) {
1030: if (augs != null
1031: && Boolean.TRUE.equals(augs
1032: .getItem(Constants.ENTITY_SKIPPED))) {
1033: reportFatalError("UnexpandedEntityReferenceIllegal");
1034: }
1035: } else if (fDocumentHandler != null) {
1036: fDocumentHandler.startGeneralEntity(name, resId,
1037: encoding, augs);
1038: }
1039: }
1040: }
1041:
1042: public void textDecl(String version, String encoding,
1043: Augmentations augs) throws XNIException {
1044: if (fDocumentHandler != null
1045: && getState() == STATE_NORMAL_PROCESSING) {
1046: fDocumentHandler.textDecl(version, encoding, augs);
1047: }
1048: }
1049:
1050: public void endGeneralEntity(String name, Augmentations augs)
1051: throws XNIException {
1052: if (fDocumentHandler != null
1053: && getState() == STATE_NORMAL_PROCESSING
1054: && fResultDepth != 0) {
1055: fDocumentHandler.endGeneralEntity(name, augs);
1056: }
1057: }
1058:
1059: public void characters(XMLString text, Augmentations augs)
1060: throws XNIException {
1061: if (getState() == STATE_NORMAL_PROCESSING) {
1062: if (fResultDepth == 0) {
1063: checkWhitespace(text);
1064: } else if (fDocumentHandler != null) {
1065: // we need to change the depth like this so that modifyAugmentations() works
1066: fDepth++;
1067: augs = modifyAugmentations(augs);
1068: fDocumentHandler.characters(text, augs);
1069: fDepth--;
1070: }
1071: }
1072: }
1073:
1074: public void ignorableWhitespace(XMLString text, Augmentations augs)
1075: throws XNIException {
1076: if (fDocumentHandler != null
1077: && getState() == STATE_NORMAL_PROCESSING
1078: && fResultDepth != 0) {
1079: fDocumentHandler.ignorableWhitespace(text, augs);
1080: }
1081: }
1082:
1083: public void startCDATA(Augmentations augs) throws XNIException {
1084: if (fDocumentHandler != null
1085: && getState() == STATE_NORMAL_PROCESSING
1086: && fResultDepth != 0) {
1087: fDocumentHandler.startCDATA(augs);
1088: }
1089: }
1090:
1091: public void endCDATA(Augmentations augs) throws XNIException {
1092: if (fDocumentHandler != null
1093: && getState() == STATE_NORMAL_PROCESSING
1094: && fResultDepth != 0) {
1095: fDocumentHandler.endCDATA(augs);
1096: }
1097: }
1098:
1099: public void endDocument(Augmentations augs) throws XNIException {
1100: if (isRootDocument()) {
1101: if (!fSeenRootElement) {
1102: reportFatalError("RootElementRequired");
1103: }
1104: if (fDocumentHandler != null) {
1105: fDocumentHandler.endDocument(augs);
1106: }
1107: }
1108: }
1109:
1110: public void setDocumentSource(XMLDocumentSource source) {
1111: fDocumentSource = source;
1112: }
1113:
1114: public XMLDocumentSource getDocumentSource() {
1115: return fDocumentSource;
1116: }
1117:
1118: // DTDHandler methods
1119: // We are only interested in the notation and unparsed entity declarations,
1120: // the rest we just pass on
1121:
1122: /* (non-Javadoc)
1123: * @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)
1124: */
1125: public void attributeDecl(String elementName, String attributeName,
1126: String type, String[] enumeration, String defaultType,
1127: XMLString defaultValue,
1128: XMLString nonNormalizedDefaultValue,
1129: Augmentations augmentations) throws XNIException {
1130: if (fDTDHandler != null) {
1131: fDTDHandler.attributeDecl(elementName, attributeName, type,
1132: enumeration, defaultType, defaultValue,
1133: nonNormalizedDefaultValue, augmentations);
1134: }
1135: }
1136:
1137: /* (non-Javadoc)
1138: * @see org.apache.xerces.xni.XMLDTDHandler#elementDecl(java.lang.String, java.lang.String, org.apache.xerces.xni.Augmentations)
1139: */
1140: public void elementDecl(String name, String contentModel,
1141: Augmentations augmentations) throws XNIException {
1142: if (fDTDHandler != null) {
1143: fDTDHandler.elementDecl(name, contentModel, augmentations);
1144: }
1145: }
1146:
1147: /* (non-Javadoc)
1148: * @see org.apache.xerces.xni.XMLDTDHandler#endAttlist(org.apache.xerces.xni.Augmentations)
1149: */
1150: public void endAttlist(Augmentations augmentations)
1151: throws XNIException {
1152: if (fDTDHandler != null) {
1153: fDTDHandler.endAttlist(augmentations);
1154: }
1155: }
1156:
1157: /* (non-Javadoc)
1158: * @see org.apache.xerces.xni.XMLDTDHandler#endConditional(org.apache.xerces.xni.Augmentations)
1159: */
1160: public void endConditional(Augmentations augmentations)
1161: throws XNIException {
1162: if (fDTDHandler != null) {
1163: fDTDHandler.endConditional(augmentations);
1164: }
1165: }
1166:
1167: /* (non-Javadoc)
1168: * @see org.apache.xerces.xni.XMLDTDHandler#endDTD(org.apache.xerces.xni.Augmentations)
1169: */
1170: public void endDTD(Augmentations augmentations) throws XNIException {
1171: if (fDTDHandler != null) {
1172: fDTDHandler.endDTD(augmentations);
1173: }
1174: fInDTD = false;
1175: }
1176:
1177: /* (non-Javadoc)
1178: * @see org.apache.xerces.xni.XMLDTDHandler#endExternalSubset(org.apache.xerces.xni.Augmentations)
1179: */
1180: public void endExternalSubset(Augmentations augmentations)
1181: throws XNIException {
1182: if (fDTDHandler != null) {
1183: fDTDHandler.endExternalSubset(augmentations);
1184: }
1185: }
1186:
1187: /* (non-Javadoc)
1188: * @see org.apache.xerces.xni.XMLDTDHandler#endParameterEntity(java.lang.String, org.apache.xerces.xni.Augmentations)
1189: */
1190: public void endParameterEntity(String name,
1191: Augmentations augmentations) throws XNIException {
1192: if (fDTDHandler != null) {
1193: fDTDHandler.endParameterEntity(name, augmentations);
1194: }
1195: }
1196:
1197: /* (non-Javadoc)
1198: * @see org.apache.xerces.xni.XMLDTDHandler#externalEntityDecl(java.lang.String, org.apache.xerces.xni.XMLResourceIdentifier, org.apache.xerces.xni.Augmentations)
1199: */
1200: public void externalEntityDecl(String name,
1201: XMLResourceIdentifier identifier,
1202: Augmentations augmentations) throws XNIException {
1203: if (fDTDHandler != null) {
1204: fDTDHandler.externalEntityDecl(name, identifier,
1205: augmentations);
1206: }
1207: }
1208:
1209: /* (non-Javadoc)
1210: * @see org.apache.xerces.xni.XMLDTDHandler#getDTDSource()
1211: */
1212: public XMLDTDSource getDTDSource() {
1213: return fDTDSource;
1214: }
1215:
1216: /* (non-Javadoc)
1217: * @see org.apache.xerces.xni.XMLDTDHandler#ignoredCharacters(org.apache.xerces.xni.XMLString, org.apache.xerces.xni.Augmentations)
1218: */
1219: public void ignoredCharacters(XMLString text,
1220: Augmentations augmentations) throws XNIException {
1221: if (fDTDHandler != null) {
1222: fDTDHandler.ignoredCharacters(text, augmentations);
1223: }
1224: }
1225:
1226: /* (non-Javadoc)
1227: * @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)
1228: */
1229: public void internalEntityDecl(String name, XMLString text,
1230: XMLString nonNormalizedText, Augmentations augmentations)
1231: throws XNIException {
1232: if (fDTDHandler != null) {
1233: fDTDHandler.internalEntityDecl(name, text,
1234: nonNormalizedText, augmentations);
1235: }
1236: }
1237:
1238: /* (non-Javadoc)
1239: * @see org.apache.xerces.xni.XMLDTDHandler#notationDecl(java.lang.String, org.apache.xerces.xni.XMLResourceIdentifier, org.apache.xerces.xni.Augmentations)
1240: */
1241: public void notationDecl(String name,
1242: XMLResourceIdentifier identifier,
1243: Augmentations augmentations) throws XNIException {
1244: this .addNotation(name, identifier, augmentations);
1245: if (fDTDHandler != null) {
1246: fDTDHandler.notationDecl(name, identifier, augmentations);
1247: }
1248: }
1249:
1250: /* (non-Javadoc)
1251: * @see org.apache.xerces.xni.XMLDTDHandler#setDTDSource(org.apache.xerces.xni.parser.XMLDTDSource)
1252: */
1253: public void setDTDSource(XMLDTDSource source) {
1254: fDTDSource = source;
1255: }
1256:
1257: /* (non-Javadoc)
1258: * @see org.apache.xerces.xni.XMLDTDHandler#startAttlist(java.lang.String, org.apache.xerces.xni.Augmentations)
1259: */
1260: public void startAttlist(String elementName,
1261: Augmentations augmentations) throws XNIException {
1262: if (fDTDHandler != null) {
1263: fDTDHandler.startAttlist(elementName, augmentations);
1264: }
1265: }
1266:
1267: /* (non-Javadoc)
1268: * @see org.apache.xerces.xni.XMLDTDHandler#startConditional(short, org.apache.xerces.xni.Augmentations)
1269: */
1270: public void startConditional(short type, Augmentations augmentations)
1271: throws XNIException {
1272: if (fDTDHandler != null) {
1273: fDTDHandler.startConditional(type, augmentations);
1274: }
1275: }
1276:
1277: /* (non-Javadoc)
1278: * @see org.apache.xerces.xni.XMLDTDHandler#startDTD(org.apache.xerces.xni.XMLLocator, org.apache.xerces.xni.Augmentations)
1279: */
1280: public void startDTD(XMLLocator locator, Augmentations augmentations)
1281: throws XNIException {
1282: fInDTD = true;
1283: if (fDTDHandler != null) {
1284: fDTDHandler.startDTD(locator, augmentations);
1285: }
1286: }
1287:
1288: /* (non-Javadoc)
1289: * @see org.apache.xerces.xni.XMLDTDHandler#startExternalSubset(org.apache.xerces.xni.XMLResourceIdentifier, org.apache.xerces.xni.Augmentations)
1290: */
1291: public void startExternalSubset(XMLResourceIdentifier identifier,
1292: Augmentations augmentations) throws XNIException {
1293: if (fDTDHandler != null) {
1294: fDTDHandler.startExternalSubset(identifier, augmentations);
1295: }
1296: }
1297:
1298: /* (non-Javadoc)
1299: * @see org.apache.xerces.xni.XMLDTDHandler#startParameterEntity(java.lang.String, org.apache.xerces.xni.XMLResourceIdentifier, java.lang.String, org.apache.xerces.xni.Augmentations)
1300: */
1301: public void startParameterEntity(String name,
1302: XMLResourceIdentifier identifier, String encoding,
1303: Augmentations augmentations) throws XNIException {
1304: if (fDTDHandler != null) {
1305: fDTDHandler.startParameterEntity(name, identifier,
1306: encoding, augmentations);
1307: }
1308: }
1309:
1310: /* (non-Javadoc)
1311: * @see org.apache.xerces.xni.XMLDTDHandler#unparsedEntityDecl(java.lang.String, org.apache.xerces.xni.XMLResourceIdentifier, java.lang.String, org.apache.xerces.xni.Augmentations)
1312: */
1313: public void unparsedEntityDecl(String name,
1314: XMLResourceIdentifier identifier, String notation,
1315: Augmentations augmentations) throws XNIException {
1316: this .addUnparsedEntity(name, identifier, notation,
1317: augmentations);
1318: if (fDTDHandler != null) {
1319: fDTDHandler.unparsedEntityDecl(name, identifier, notation,
1320: augmentations);
1321: }
1322: }
1323:
1324: /* (non-Javadoc)
1325: * @see org.apache.xerces.xni.parser.XMLDTDSource#getDTDHandler()
1326: */
1327: public XMLDTDHandler getDTDHandler() {
1328: return fDTDHandler;
1329: }
1330:
1331: /* (non-Javadoc)
1332: * @see org.apache.xerces.xni.parser.XMLDTDSource#setDTDHandler(org.apache.xerces.xni.XMLDTDHandler)
1333: */
1334: public void setDTDHandler(XMLDTDHandler handler) {
1335: fDTDHandler = handler;
1336: }
1337:
1338: // XIncludeHandler methods
1339:
1340: private void setErrorReporter(XMLErrorReporter reporter) {
1341: fErrorReporter = reporter;
1342: if (fErrorReporter != null) {
1343: fErrorReporter.putMessageFormatter(
1344: XIncludeMessageFormatter.XINCLUDE_DOMAIN,
1345: fXIncludeMessageFormatter);
1346: // this ensures the proper location is displayed in error messages
1347: if (fDocLocation != null) {
1348: fErrorReporter.setDocumentLocator(fDocLocation);
1349: }
1350: }
1351: }
1352:
1353: protected void handleFallbackElement() {
1354: if (!getSawInclude(fDepth - 1)) {
1355: if (getState() == STATE_IGNORE) {
1356: return;
1357: }
1358: reportFatalError("FallbackParent");
1359: }
1360:
1361: setSawInclude(fDepth, false);
1362: fNamespaceContext.setContextInvalid();
1363:
1364: if (getSawFallback(fDepth)) {
1365: reportFatalError("MultipleFallbacks");
1366: } else {
1367: setSawFallback(fDepth, true);
1368: }
1369:
1370: // Either the state is STATE_EXPECT_FALLBACK or it's STATE_IGNORE.
1371: // If we're ignoring, we want to stay ignoring. But if we're expecting this fallback element,
1372: // we want to signal that we should process the children.
1373: if (getState() == STATE_EXPECT_FALLBACK) {
1374: setState(STATE_NORMAL_PROCESSING);
1375: }
1376: }
1377:
1378: protected boolean handleIncludeElement(XMLAttributes attributes)
1379: throws XNIException {
1380: if (getSawInclude(fDepth - 1)) {
1381: reportFatalError("IncludeChild",
1382: new Object[] { XINCLUDE_INCLUDE });
1383: }
1384: if (getState() == STATE_IGNORE) {
1385: return true;
1386: }
1387: setSawInclude(fDepth, true);
1388: fNamespaceContext.setContextInvalid();
1389:
1390: // TODO: does Java use IURIs by default?
1391: // [Definition: An internationalized URI reference, or IURI, is a URI reference that directly uses [Unicode] characters.]
1392: // TODO: figure out what section 4.1.1 of the XInclude spec is talking about
1393: // has to do with disallowed ASCII character escaping
1394: // this ties in with the above IURI section, but I suspect Java already does it
1395:
1396: String href = attributes.getValue(XINCLUDE_ATTR_HREF);
1397: String parse = attributes.getValue(XINCLUDE_ATTR_PARSE);
1398: String xpointer = attributes.getValue(XPOINTER);
1399: String accept = attributes.getValue(XINCLUDE_ATTR_ACCEPT);
1400: String acceptLanguage = attributes
1401: .getValue(XINCLUDE_ATTR_ACCEPT_LANGUAGE);
1402:
1403: if (parse == null) {
1404: parse = XINCLUDE_PARSE_XML;
1405: }
1406: if (href == null) {
1407: href = XMLSymbols.EMPTY_STRING;
1408: }
1409: if (href.length() == 0 && XINCLUDE_PARSE_XML.equals(parse)) {
1410: if (xpointer == null) {
1411: reportFatalError("XpointerMissing");
1412: } else {
1413: // When parse="xml" and an xpointer is specified treat
1414: // all absences of the href attribute as a resource error.
1415: Locale locale = (fErrorReporter != null) ? fErrorReporter
1416: .getLocale()
1417: : null;
1418: String reason = fXIncludeMessageFormatter
1419: .formatMessage(locale, "XPointerStreamability",
1420: null);
1421: reportResourceError("XMLResourceError", new Object[] {
1422: href, reason });
1423: return false;
1424: }
1425: }
1426:
1427: URI hrefURI = null;
1428:
1429: // Check whether href is correct and perform escaping as per section 4.1.1 of the XInclude spec.
1430: // Report fatal error if the href value contains a fragment identifier or if the value after
1431: // escaping is a syntactically invalid URI or IRI.
1432: try {
1433: hrefURI = new URI(href, true);
1434: if (hrefURI.getFragment() != null) {
1435: reportFatalError("HrefFragmentIdentifierIllegal",
1436: new Object[] { href });
1437: }
1438: } catch (URI.MalformedURIException exc) {
1439: String newHref = escapeHref(href);
1440: if (href != newHref) {
1441: href = newHref;
1442: try {
1443: hrefURI = new URI(href, true);
1444: if (hrefURI.getFragment() != null) {
1445: reportFatalError(
1446: "HrefFragmentIdentifierIllegal",
1447: new Object[] { href });
1448: }
1449: } catch (URI.MalformedURIException exc2) {
1450: reportFatalError("HrefSyntacticallyInvalid",
1451: new Object[] { href });
1452: }
1453: } else {
1454: reportFatalError("HrefSyntacticallyInvalid",
1455: new Object[] { href });
1456: }
1457: }
1458:
1459: // Verify that if an accept and/or an accept-language attribute exist
1460: // that the value(s) don't contain disallowed characters.
1461: if (accept != null && !isValidInHTTPHeader(accept)) {
1462: reportFatalError("AcceptMalformed", null);
1463: accept = null;
1464: }
1465: if (acceptLanguage != null
1466: && !isValidInHTTPHeader(acceptLanguage)) {
1467: reportFatalError("AcceptLanguageMalformed", null);
1468: acceptLanguage = null;
1469: }
1470:
1471: XMLInputSource includedSource = null;
1472: if (fEntityResolver != null) {
1473: try {
1474: XMLResourceIdentifier resourceIdentifier = new XMLResourceIdentifierImpl(
1475: null, href, fCurrentBaseURI
1476: .getExpandedSystemId(),
1477: XMLEntityManager.expandSystemId(href,
1478: fCurrentBaseURI.getExpandedSystemId(),
1479: false));
1480:
1481: includedSource = fEntityResolver
1482: .resolveEntity(resourceIdentifier);
1483:
1484: if (includedSource != null
1485: && !(includedSource instanceof HTTPInputSource)
1486: && (accept != null || acceptLanguage != null)
1487: && includedSource.getCharacterStream() == null
1488: && includedSource.getByteStream() == null) {
1489:
1490: includedSource = createInputSource(includedSource
1491: .getPublicId(), includedSource
1492: .getSystemId(), includedSource
1493: .getBaseSystemId(), accept, acceptLanguage);
1494: }
1495: } catch (IOException e) {
1496: reportResourceError("XMLResourceError", new Object[] {
1497: href, e.getMessage() });
1498: return false;
1499: }
1500: }
1501:
1502: if (includedSource == null) {
1503: // setup an HTTPInputSource if either of the content negotation attributes were specified.
1504: if (accept != null || acceptLanguage != null) {
1505: includedSource = createInputSource(null, href,
1506: fCurrentBaseURI.getExpandedSystemId(), accept,
1507: acceptLanguage);
1508: } else {
1509: includedSource = new XMLInputSource(null, href,
1510: fCurrentBaseURI.getExpandedSystemId());
1511: }
1512: }
1513:
1514: if (parse.equals(XINCLUDE_PARSE_XML)) {
1515: // Instead of always creating a new configuration, the first one can be reused
1516: if ((xpointer != null && fXPointerChildConfig == null)
1517: || (xpointer == null && fXIncludeChildConfig == null)) {
1518:
1519: String parserName = XINCLUDE_DEFAULT_CONFIGURATION;
1520: if (xpointer != null)
1521: parserName = "org.apache.xerces.parsers.XPointerParserConfiguration";
1522:
1523: fChildConfig = (XMLParserConfiguration) ObjectFactory
1524: .newInstance(parserName, ObjectFactory
1525: .findClassLoader(), true);
1526:
1527: // use the same symbol table, error reporter, entity resolver, security manager and buffer size.
1528: if (fSymbolTable != null)
1529: fChildConfig
1530: .setProperty(SYMBOL_TABLE, fSymbolTable);
1531: if (fErrorReporter != null)
1532: fChildConfig.setProperty(ERROR_REPORTER,
1533: fErrorReporter);
1534: if (fEntityResolver != null)
1535: fChildConfig.setProperty(ENTITY_RESOLVER,
1536: fEntityResolver);
1537: fChildConfig.setProperty(SECURITY_MANAGER,
1538: fSecurityManager);
1539: fChildConfig.setProperty(BUFFER_SIZE, new Integer(
1540: fBufferSize));
1541:
1542: // features must be copied to child configuration
1543: fNeedCopyFeatures = true;
1544:
1545: // use the same namespace context
1546: fChildConfig.setProperty(
1547: Constants.XERCES_PROPERTY_PREFIX
1548: + Constants.NAMESPACE_CONTEXT_PROPERTY,
1549: fNamespaceContext);
1550:
1551: fChildConfig.setFeature(XINCLUDE_FIXUP_BASE_URIS,
1552: fFixupBaseURIs);
1553:
1554: fChildConfig.setFeature(XINCLUDE_FIXUP_LANGUAGE,
1555: fFixupLanguage);
1556:
1557: // If the xpointer attribute is present
1558: if (xpointer != null) {
1559:
1560: XPointerHandler newHandler = (XPointerHandler) fChildConfig
1561: .getProperty(Constants.XERCES_PROPERTY_PREFIX
1562: + Constants.XPOINTER_HANDLER_PROPERTY);
1563:
1564: fXPtrProcessor = newHandler;
1565:
1566: // ???
1567: ((XPointerHandler) fXPtrProcessor)
1568: .setProperty(
1569: Constants.XERCES_PROPERTY_PREFIX
1570: + Constants.NAMESPACE_CONTEXT_PROPERTY,
1571: fNamespaceContext);
1572:
1573: ((XPointerHandler) fXPtrProcessor).setProperty(
1574: XINCLUDE_FIXUP_BASE_URIS,
1575: fFixupBaseURIs ? Boolean.TRUE
1576: : Boolean.FALSE);
1577:
1578: ((XPointerHandler) fXPtrProcessor).setProperty(
1579: XINCLUDE_FIXUP_LANGUAGE,
1580: fFixupLanguage ? Boolean.TRUE
1581: : Boolean.FALSE);
1582:
1583: if (fErrorReporter != null)
1584: ((XPointerHandler) fXPtrProcessor).setProperty(
1585: ERROR_REPORTER, fErrorReporter);
1586: // ???
1587:
1588: newHandler.setParent(this );
1589: newHandler.setHref(href);
1590: newHandler.setXIncludeLocator(fXIncludeLocator);
1591: newHandler.setDocumentHandler(this
1592: .getDocumentHandler());
1593: fXPointerChildConfig = fChildConfig;
1594: } else {
1595: XIncludeHandler newHandler = (XIncludeHandler) fChildConfig
1596: .getProperty(Constants.XERCES_PROPERTY_PREFIX
1597: + Constants.XINCLUDE_HANDLER_PROPERTY);
1598:
1599: newHandler.setParent(this );
1600: newHandler.setHref(href);
1601: newHandler.setXIncludeLocator(fXIncludeLocator);
1602: newHandler.setDocumentHandler(this
1603: .getDocumentHandler());
1604: fXIncludeChildConfig = fChildConfig;
1605: }
1606: }
1607:
1608: // If an xpointer attribute is present
1609: if (xpointer != null) {
1610: fChildConfig = fXPointerChildConfig;
1611:
1612: // Parse the XPointer expression
1613: try {
1614: ((XPointerProcessor) fXPtrProcessor)
1615: .parseXPointer(xpointer);
1616:
1617: } catch (XNIException ex) {
1618: // report the XPointer error as a resource error
1619: reportResourceError("XMLResourceError",
1620: new Object[] { href, ex.getMessage() });
1621: return false;
1622: }
1623: } else {
1624: fChildConfig = fXIncludeChildConfig;
1625: }
1626:
1627: // set all features on parserConfig to match this parser configuration
1628: if (fNeedCopyFeatures) {
1629: copyFeatures(fSettings, fChildConfig);
1630: }
1631: fNeedCopyFeatures = false;
1632:
1633: try {
1634: fHasIncludeReportedContent = false;
1635: fNamespaceContext.pushScope();
1636:
1637: fChildConfig.parse(includedSource);
1638: // necessary to make sure proper location is reported to the application and in errors
1639: fXIncludeLocator.setLocator(fDocLocation);
1640: if (fErrorReporter != null) {
1641: fErrorReporter.setDocumentLocator(fDocLocation);
1642: }
1643:
1644: // If the xpointer attribute is present
1645: if (xpointer != null) {
1646: // and it was not resolved
1647: if (!((XPointerProcessor) fXPtrProcessor)
1648: .isXPointerResolved()) {
1649: Locale locale = (fErrorReporter != null) ? fErrorReporter
1650: .getLocale()
1651: : null;
1652: String reason = fXIncludeMessageFormatter
1653: .formatMessage(
1654: locale,
1655: "XPointerResolutionUnsuccessful",
1656: null);
1657: reportResourceError("XMLResourceError",
1658: new Object[] { href, reason });
1659: // use the fallback
1660: return false;
1661: }
1662: }
1663: } catch (XNIException e) {
1664: // necessary to make sure proper location is reported to the application and in errors
1665: fXIncludeLocator.setLocator(fDocLocation);
1666: if (fErrorReporter != null) {
1667: fErrorReporter.setDocumentLocator(fDocLocation);
1668: }
1669: reportFatalError("XMLParseError", new Object[] { href });
1670: } catch (IOException e) {
1671: // necessary to make sure proper location is reported to the application and in errors
1672: fXIncludeLocator.setLocator(fDocLocation);
1673: if (fErrorReporter != null) {
1674: fErrorReporter.setDocumentLocator(fDocLocation);
1675: }
1676: // If the start document event has been seen on the child pipeline it
1677: // means the resource was successfully opened and we started reporting
1678: // document events. If an IOException is thrown after the start document
1679: // event we had a failure midstream and cannot recover.
1680: if (fHasIncludeReportedContent) {
1681: throw new XNIException(e);
1682: }
1683: // In other circumstances an IOException indicates that we had trouble
1684: // accessing or opening the file, not that it was an invalid XML file. So we
1685: // send a resource error, not a fatal error.
1686: reportResourceError("XMLResourceError", new Object[] {
1687: href, e.getMessage() });
1688: return false;
1689: } finally {
1690: fNamespaceContext.popScope();
1691: }
1692: } else if (parse.equals(XINCLUDE_PARSE_TEXT)) {
1693: // we only care about encoding for parse="text"
1694: String encoding = attributes
1695: .getValue(XINCLUDE_ATTR_ENCODING);
1696: includedSource.setEncoding(encoding);
1697: XIncludeTextReader textReader = null;
1698:
1699: try {
1700: fHasIncludeReportedContent = false;
1701:
1702: // Setup the appropriate text reader.
1703: if (!fIsXML11) {
1704: if (fXInclude10TextReader == null) {
1705: fXInclude10TextReader = new XIncludeTextReader(
1706: includedSource, this , fBufferSize);
1707: } else {
1708: fXInclude10TextReader
1709: .setInputSource(includedSource);
1710: }
1711: textReader = fXInclude10TextReader;
1712: } else {
1713: if (fXInclude11TextReader == null) {
1714: fXInclude11TextReader = new XInclude11TextReader(
1715: includedSource, this , fBufferSize);
1716: } else {
1717: fXInclude11TextReader
1718: .setInputSource(includedSource);
1719: }
1720: textReader = fXInclude11TextReader;
1721: }
1722: textReader.setErrorReporter(fErrorReporter);
1723: textReader.parse();
1724: }
1725: // encoding errors
1726: catch (MalformedByteSequenceException ex) {
1727: fErrorReporter.reportError(ex.getDomain(), ex.getKey(),
1728: ex.getArguments(),
1729: XMLErrorReporter.SEVERITY_FATAL_ERROR, ex);
1730: } catch (CharConversionException e) {
1731: fErrorReporter.reportError(
1732: XMLMessageFormatter.XML_DOMAIN,
1733: "CharConversionFailure", null,
1734: XMLErrorReporter.SEVERITY_FATAL_ERROR, e);
1735: } catch (IOException e) {
1736: // If a characters event has already been sent down the pipeline it
1737: // means the resource was successfully opened and that this IOException
1738: // is from a failure midstream from which we cannot recover.
1739: if (fHasIncludeReportedContent) {
1740: throw new XNIException(e);
1741: }
1742: reportResourceError("TextResourceError", new Object[] {
1743: href, e.getMessage() });
1744: return false;
1745: } finally {
1746: if (textReader != null) {
1747: try {
1748: textReader.close();
1749: } catch (IOException e) {
1750: reportResourceError("TextResourceError",
1751: new Object[] { href, e.getMessage() });
1752: return false;
1753: }
1754: }
1755: }
1756: } else {
1757: reportFatalError("InvalidParseValue",
1758: new Object[] { parse });
1759: }
1760: return true;
1761: }
1762:
1763: /**
1764: * Returns true if the element has the namespace "http://www.w3.org/2001/XInclude"
1765: * @param element the element to check
1766: * @return true if the element has the namespace "http://www.w3.org/2001/XInclude"
1767: */
1768: protected boolean hasXIncludeNamespace(QName element) {
1769: // REVISIT: The namespace of this element should be bound
1770: // already. Why are we looking it up from the namespace
1771: // context? -- mrglavas
1772: return element.uri == XINCLUDE_NS_URI
1773: || fNamespaceContext.getURI(element.prefix) == XINCLUDE_NS_URI;
1774: }
1775:
1776: /**
1777: * Checks if the element is an <include> element. The element must have
1778: * the XInclude namespace, and a local name of "include".
1779: *
1780: * @param element the element to check
1781: * @return true if the element is an <include> element
1782: * @see #hasXIncludeNamespace(QName)
1783: */
1784: protected boolean isIncludeElement(QName element) {
1785: return element.localpart.equals(XINCLUDE_INCLUDE)
1786: && hasXIncludeNamespace(element);
1787: }
1788:
1789: /**
1790: * Checks if the element is an <fallback> element. The element must have
1791: * the XInclude namespace, and a local name of "fallback".
1792: *
1793: * @param element the element to check
1794: * @return true if the element is an <fallback; element
1795: * @see #hasXIncludeNamespace(QName)
1796: */
1797: protected boolean isFallbackElement(QName element) {
1798: return element.localpart.equals(XINCLUDE_FALLBACK)
1799: && hasXIncludeNamespace(element);
1800: }
1801:
1802: /**
1803: * Returns true if the current [base URI] is the same as the [base URI] that
1804: * was in effect on the include parent. This method should <em>only</em> be called
1805: * when the current element is a top level included element, i.e. the direct child
1806: * of a fallback element, or the root elements in an included document.
1807: * The "include parent" is the element which, in the result infoset, will be the
1808: * direct parent of the current element.
1809: * @return true if the [base URIs] are the same string
1810: */
1811: protected boolean sameBaseURIAsIncludeParent() {
1812: String parentBaseURI = getIncludeParentBaseURI();
1813: String baseURI = fCurrentBaseURI.getExpandedSystemId();
1814: // REVISIT: should we use File#sameFile() ?
1815: // I think the benefit of using it is that it resolves host names
1816: // instead of just doing a string comparison.
1817: // TODO: [base URI] is still an open issue with the working group.
1818: // They're deciding if xml:base should be added if the [base URI] is different in terms
1819: // of resolving relative references, or if it should be added if they are different at all.
1820: // Revisit this after a final decision has been made.
1821: // The decision also affects whether we output the file name of the URI, or just the path.
1822: return parentBaseURI != null && parentBaseURI.equals(baseURI);
1823: }
1824:
1825: /**
1826: * Returns true if the current [language] is equivalent to the [language] that
1827: * was in effect on the include parent, taking case-insensitivity into account
1828: * as per [RFC 3066]. This method should <em>only</em> be called when the
1829: * current element is a top level included element, i.e. the direct child
1830: * of a fallback element, or the root elements in an included document.
1831: * The "include parent" is the element which, in the result infoset, will be the
1832: * direct parent of the current element.
1833: *
1834: * @return true if the [language] properties have the same value
1835: * taking case-insensitivity into account as per [RFC 3066].
1836: */
1837: protected boolean sameLanguageAsIncludeParent() {
1838: String parentLanguage = getIncludeParentLanguage();
1839: return parentLanguage != null
1840: && parentLanguage.equalsIgnoreCase(fCurrentLanguage);
1841: }
1842:
1843: protected void setupCurrentBaseURI(XMLLocator locator) {
1844: fCurrentBaseURI.setBaseSystemId(locator.getBaseSystemId());
1845:
1846: if (locator.getLiteralSystemId() != null) {
1847: fCurrentBaseURI.setLiteralSystemId(locator
1848: .getLiteralSystemId());
1849: } else {
1850: fCurrentBaseURI.setLiteralSystemId(fHrefFromParent);
1851: }
1852:
1853: String expandedSystemId = locator.getExpandedSystemId();
1854: if (expandedSystemId == null) {
1855: // attempt to expand it ourselves
1856: try {
1857: expandedSystemId = XMLEntityManager.expandSystemId(
1858: fCurrentBaseURI.getLiteralSystemId(),
1859: fCurrentBaseURI.getBaseSystemId(), false);
1860: if (expandedSystemId == null) {
1861: expandedSystemId = fCurrentBaseURI
1862: .getLiteralSystemId();
1863: }
1864: } catch (MalformedURIException e) {
1865: reportFatalError("ExpandedSystemId");
1866: }
1867: }
1868: fCurrentBaseURI.setExpandedSystemId(expandedSystemId);
1869: }
1870:
1871: /**
1872: * Checks if the file indicated by the given system id has already been
1873: * included in the current stack.
1874: * @param includedSysId the system id to check for inclusion
1875: * @return true if the source has already been included
1876: */
1877: protected boolean searchForRecursiveIncludes(String includedSysId) {
1878: if (includedSysId.equals(fCurrentBaseURI.getExpandedSystemId())) {
1879: return true;
1880: } else if (fParentXIncludeHandler == null) {
1881: return false;
1882: } else {
1883: return fParentXIncludeHandler
1884: .searchForRecursiveIncludes(includedSysId);
1885: }
1886: }
1887:
1888: /**
1889: * Returns true if the current element is a top level included item. This means
1890: * it's either the child of a fallback element, or the top level item in an
1891: * included document
1892: * @return true if the current element is a top level included item
1893: */
1894: protected boolean isTopLevelIncludedItem() {
1895: return isTopLevelIncludedItemViaInclude()
1896: || isTopLevelIncludedItemViaFallback();
1897: }
1898:
1899: protected boolean isTopLevelIncludedItemViaInclude() {
1900: return fDepth == 1 && !isRootDocument();
1901: }
1902:
1903: protected boolean isTopLevelIncludedItemViaFallback() {
1904: // Technically, this doesn't check if the parent was a fallback, it also
1905: // would return true if any of the parent's sibling elements were fallbacks.
1906: // However, this doesn't matter, since we will always be ignoring elements
1907: // whose parent's siblings were fallbacks.
1908: return getSawFallback(fDepth - 1);
1909: }
1910:
1911: /**
1912: * Processes the XMLAttributes object of startElement() calls. Performs the following tasks:
1913: * <ul>
1914: * <li> If the element is a top level included item whose [base URI] is different from the
1915: * [base URI] of the include parent, then an xml:base attribute is added to specify the
1916: * true [base URI]
1917: * <li> For all namespace prefixes which are in-scope in an included item, but not in scope
1918: * in the include parent, a xmlns:prefix attribute is added
1919: * <li> For all attributes with a type of ENTITY, ENTITIES or NOTATIONS, the notations and
1920: * unparsed entities are processed as described in the spec, sections 4.5.1 and 4.5.2
1921: * </ul>
1922: * @param attributes
1923: * @return the processed XMLAttributes
1924: */
1925: protected XMLAttributes processAttributes(XMLAttributes attributes) {
1926: if (isTopLevelIncludedItem()) {
1927: // Modify attributes to fix the base URI (spec 4.5.5).
1928: // We only do it to top level included elements, which have a different
1929: // base URI than their include parent.
1930: if (fFixupBaseURIs && !sameBaseURIAsIncludeParent()) {
1931: if (attributes == null) {
1932: attributes = new XMLAttributesImpl();
1933: }
1934:
1935: // This causes errors with schema validation, if the schema doesn't
1936: // specify that these elements can have an xml:base attribute
1937: String uri = null;
1938: try {
1939: uri = this .getRelativeBaseURI();
1940: } catch (MalformedURIException e) {
1941: // this shouldn't ever happen, since by definition, we had to traverse
1942: // the same URIs to even get to this place
1943: uri = fCurrentBaseURI.getExpandedSystemId();
1944: }
1945: int index = attributes.addAttribute(XML_BASE_QNAME,
1946: XMLSymbols.fCDATASymbol, uri);
1947: attributes.setSpecified(index, true);
1948: }
1949:
1950: // Modify attributes to perform language-fixup (spec 4.5.6).
1951: // We only do it to top level included elements, which have a different
1952: // [language] than their include parent.
1953: if (fFixupLanguage && !sameLanguageAsIncludeParent()) {
1954: if (attributes == null) {
1955: attributes = new XMLAttributesImpl();
1956: }
1957: int index = attributes.addAttribute(XML_LANG_QNAME,
1958: XMLSymbols.fCDATASymbol, fCurrentLanguage);
1959: attributes.setSpecified(index, true);
1960: }
1961:
1962: // Modify attributes of included items to do namespace-fixup. (spec 4.5.4)
1963: Enumeration inscopeNS = fNamespaceContext.getAllPrefixes();
1964: while (inscopeNS.hasMoreElements()) {
1965: String prefix = (String) inscopeNS.nextElement();
1966: String parentURI = fNamespaceContext
1967: .getURIFromIncludeParent(prefix);
1968: String uri = fNamespaceContext.getURI(prefix);
1969: if (parentURI != uri && attributes != null) {
1970: if (prefix == XMLSymbols.EMPTY_STRING) {
1971: if (attributes.getValue(
1972: NamespaceContext.XMLNS_URI,
1973: XMLSymbols.PREFIX_XMLNS) == null) {
1974: if (attributes == null) {
1975: attributes = new XMLAttributesImpl();
1976: }
1977:
1978: QName ns = (QName) NEW_NS_ATTR_QNAME
1979: .clone();
1980: ns.prefix = null;
1981: ns.localpart = XMLSymbols.PREFIX_XMLNS;
1982: ns.rawname = XMLSymbols.PREFIX_XMLNS;
1983: int index = attributes.addAttribute(ns,
1984: XMLSymbols.fCDATASymbol,
1985: uri != null ? uri
1986: : XMLSymbols.EMPTY_STRING);
1987: attributes.setSpecified(index, true);
1988: // Need to re-declare this prefix in the current context
1989: // in order for the SAX parser to report the appropriate
1990: // start and end prefix mapping events. -- mrglavas
1991: fNamespaceContext
1992: .declarePrefix(prefix, uri);
1993: }
1994: } else if (attributes.getValue(
1995: NamespaceContext.XMLNS_URI, prefix) == null) {
1996: if (attributes == null) {
1997: attributes = new XMLAttributesImpl();
1998: }
1999:
2000: QName ns = (QName) NEW_NS_ATTR_QNAME.clone();
2001: ns.localpart = prefix;
2002: ns.rawname += prefix;
2003: ns.rawname = (fSymbolTable != null) ? fSymbolTable
2004: .addSymbol(ns.rawname)
2005: : ns.rawname.intern();
2006: int index = attributes.addAttribute(ns,
2007: XMLSymbols.fCDATASymbol,
2008: uri != null ? uri
2009: : XMLSymbols.EMPTY_STRING);
2010: attributes.setSpecified(index, true);
2011: // Need to re-declare this prefix in the current context
2012: // in order for the SAX parser to report the appropriate
2013: // start and end prefix mapping events. -- mrglavas
2014: fNamespaceContext.declarePrefix(prefix, uri);
2015: }
2016: }
2017: }
2018: }
2019:
2020: if (attributes != null) {
2021: int length = attributes.getLength();
2022: for (int i = 0; i < length; i++) {
2023: String type = attributes.getType(i);
2024: String value = attributes.getValue(i);
2025: if (type == XMLSymbols.fENTITYSymbol) {
2026: this .checkUnparsedEntity(value);
2027: }
2028: if (type == XMLSymbols.fENTITIESSymbol) {
2029: // 4.5.1 - Unparsed Entities
2030: StringTokenizer st = new StringTokenizer(value);
2031: while (st.hasMoreTokens()) {
2032: String entName = st.nextToken();
2033: this .checkUnparsedEntity(entName);
2034: }
2035: } else if (type == XMLSymbols.fNOTATIONSymbol) {
2036: // 4.5.2 - Notations
2037: this .checkNotation(value);
2038: }
2039: /* We actually don't need to do anything for 4.5.3, because at this stage the
2040: * value of the attribute is just a string. It will be taken care of later
2041: * in the pipeline, when the IDREFs are actually resolved against IDs.
2042: *
2043: * if (type == XMLSymbols.fIDREFSymbol || type == XMLSymbols.fIDREFSSymbol) { }
2044: */
2045: }
2046: }
2047:
2048: return attributes;
2049: }
2050:
2051: /**
2052: * Returns a URI, relative to the include parent's base URI, of the current
2053: * [base URI]. For instance, if the current [base URI] was "dir1/dir2/file.xml"
2054: * and the include parent's [base URI] was "dir/", this would return "dir2/file.xml".
2055: * @return the relative URI
2056: */
2057: protected String getRelativeBaseURI() throws MalformedURIException {
2058: int includeParentDepth = getIncludeParentDepth();
2059: String relativeURI = this .getRelativeURI(includeParentDepth);
2060: if (isRootDocument()) {
2061: return relativeURI;
2062: } else {
2063: if (relativeURI.equals("")) {
2064: relativeURI = fCurrentBaseURI.getLiteralSystemId();
2065: }
2066:
2067: if (includeParentDepth == 0) {
2068: if (fParentRelativeURI == null) {
2069: fParentRelativeURI = fParentXIncludeHandler
2070: .getRelativeBaseURI();
2071: }
2072: if (fParentRelativeURI.equals("")) {
2073: return relativeURI;
2074: }
2075:
2076: URI base = new URI(fParentRelativeURI, true);
2077: URI uri = new URI(base, relativeURI);
2078:
2079: /** Check whether the scheme components are equal. */
2080: final String baseScheme = base.getScheme();
2081: final String literalScheme = uri.getScheme();
2082: if (!isEqual(baseScheme, literalScheme)) {
2083: return relativeURI;
2084: }
2085:
2086: /** Check whether the authority components are equal. */
2087: final String baseAuthority = base.getAuthority();
2088: final String literalAuthority = uri.getAuthority();
2089: if (!isEqual(baseAuthority, literalAuthority)) {
2090: return uri.getSchemeSpecificPart();
2091: }
2092:
2093: /**
2094: * The scheme and authority components are equal,
2095: * return the path and the possible query and/or
2096: * fragment which follow.
2097: */
2098: final String literalPath = uri.getPath();
2099: final String literalQuery = uri.getQueryString();
2100: final String literalFragment = uri.getFragment();
2101: if (literalQuery != null || literalFragment != null) {
2102: StringBuffer buffer = new StringBuffer();
2103: if (literalPath != null) {
2104: buffer.append(literalPath);
2105: }
2106: if (literalQuery != null) {
2107: buffer.append('?');
2108: buffer.append(literalQuery);
2109: }
2110: if (literalFragment != null) {
2111: buffer.append('#');
2112: buffer.append(literalFragment);
2113: }
2114: return buffer.toString();
2115: }
2116: return literalPath;
2117: } else {
2118: return relativeURI;
2119: }
2120: }
2121: }
2122:
2123: /**
2124: * Returns the [base URI] of the include parent.
2125: * @return the base URI of the include parent.
2126: */
2127: private String getIncludeParentBaseURI() {
2128: int depth = getIncludeParentDepth();
2129: if (!isRootDocument() && depth == 0) {
2130: return fParentXIncludeHandler.getIncludeParentBaseURI();
2131: } else {
2132: return this .getBaseURI(depth);
2133: }
2134: }
2135:
2136: /**
2137: * Returns the [language] of the include parent.
2138: *
2139: * @return the language property of the include parent.
2140: */
2141: private String getIncludeParentLanguage() {
2142: int depth = getIncludeParentDepth();
2143: if (!isRootDocument() && depth == 0) {
2144: return fParentXIncludeHandler.getIncludeParentLanguage();
2145: } else {
2146: return getLanguage(depth);
2147: }
2148: }
2149:
2150: /**
2151: * Returns the depth of the include parent. Here, the include parent is
2152: * calculated as the last non-include or non-fallback element. It is assumed
2153: * this method is called when the current element is a top level included item.
2154: * Returning 0 indicates that the top level element in this document
2155: * was an include element.
2156: * @return the depth of the top level include element
2157: */
2158: private int getIncludeParentDepth() {
2159: // We don't start at fDepth, since it is either the top level included item,
2160: // or an include element, when this method is called.
2161: for (int i = fDepth - 1; i >= 0; i--) {
2162: // This technically might not always return the first non-include/fallback
2163: // element that it comes to, since sawFallback() returns true if a fallback
2164: // was ever encountered at that depth. However, if a fallback was encountered
2165: // at that depth, and it wasn't the direct descendant of the current element
2166: // then we can't be in a situation where we're calling this method (because
2167: // we'll always be in STATE_IGNORE)
2168: if (!getSawInclude(i) && !getSawFallback(i)) {
2169: return i;
2170: }
2171: }
2172: // shouldn't get here, since depth 0 should never have an include element or
2173: // a fallback element
2174: return 0;
2175: }
2176:
2177: /**
2178: * Returns the current element depth of the result infoset.
2179: */
2180: private int getResultDepth() {
2181: return fResultDepth;
2182: }
2183:
2184: /**
2185: * Modify the augmentations. Add an [included] infoset item, if the current
2186: * element is a top level included item.
2187: * @param augs the Augmentations to modify.
2188: * @return the modified Augmentations
2189: */
2190: protected Augmentations modifyAugmentations(Augmentations augs) {
2191: return modifyAugmentations(augs, false);
2192: }
2193:
2194: /**
2195: * Modify the augmentations. Add an [included] infoset item, if <code>force</code>
2196: * is true, or if the current element is a top level included item.
2197: * @param augs the Augmentations to modify.
2198: * @param force whether to force modification
2199: * @return the modified Augmentations
2200: */
2201: protected Augmentations modifyAugmentations(Augmentations augs,
2202: boolean force) {
2203: if (force || isTopLevelIncludedItem()) {
2204: if (augs == null) {
2205: augs = new AugmentationsImpl();
2206: }
2207: augs.putItem(XINCLUDE_INCLUDED, Boolean.TRUE);
2208: }
2209: return augs;
2210: }
2211:
2212: protected int getState(int depth) {
2213: return fState[depth];
2214: }
2215:
2216: protected int getState() {
2217: return fState[fDepth];
2218: }
2219:
2220: protected void setState(int state) {
2221: if (fDepth >= fState.length) {
2222: int[] newarray = new int[fDepth * 2];
2223: System.arraycopy(fState, 0, newarray, 0, fState.length);
2224: fState = newarray;
2225: }
2226: fState[fDepth] = state;
2227: }
2228:
2229: /**
2230: * Records that an <fallback> was encountered at the specified depth,
2231: * as an ancestor of the current element, or as a sibling of an ancestor of the
2232: * current element.
2233: *
2234: * @param depth
2235: * @param val
2236: */
2237: protected void setSawFallback(int depth, boolean val) {
2238: if (depth >= fSawFallback.length) {
2239: boolean[] newarray = new boolean[depth * 2];
2240: System.arraycopy(fSawFallback, 0, newarray, 0,
2241: fSawFallback.length);
2242: fSawFallback = newarray;
2243: }
2244: fSawFallback[depth] = val;
2245: }
2246:
2247: /**
2248: * Returns whether an <fallback> was encountered at the specified depth,
2249: * as an ancestor of the current element, or as a sibling of an ancestor of the
2250: * current element.
2251: *
2252: * @param depth
2253: */
2254: protected boolean getSawFallback(int depth) {
2255: if (depth >= fSawFallback.length) {
2256: return false;
2257: }
2258: return fSawFallback[depth];
2259: }
2260:
2261: /**
2262: * Records that an <include> was encountered at the specified depth,
2263: * as an ancestor of the current item.
2264: *
2265: * @param depth
2266: * @param val
2267: */
2268: protected void setSawInclude(int depth, boolean val) {
2269: if (depth >= fSawInclude.length) {
2270: boolean[] newarray = new boolean[depth * 2];
2271: System.arraycopy(fSawInclude, 0, newarray, 0,
2272: fSawInclude.length);
2273: fSawInclude = newarray;
2274: }
2275: fSawInclude[depth] = val;
2276: }
2277:
2278: /**
2279: * Return whether an <include> was encountered at the specified depth,
2280: * as an ancestor of the current item.
2281: *
2282: * @param depth
2283: * @return true if an include was seen at the given depth, false otherwise
2284: */
2285: protected boolean getSawInclude(int depth) {
2286: if (depth >= fSawInclude.length) {
2287: return false;
2288: }
2289: return fSawInclude[depth];
2290: }
2291:
2292: protected void reportResourceError(String key) {
2293: this .reportFatalError(key, null);
2294: }
2295:
2296: protected void reportResourceError(String key, Object[] args) {
2297: this .reportError(key, args, XMLErrorReporter.SEVERITY_WARNING);
2298: }
2299:
2300: protected void reportFatalError(String key) {
2301: this .reportFatalError(key, null);
2302: }
2303:
2304: protected void reportFatalError(String key, Object[] args) {
2305: this .reportError(key, args,
2306: XMLErrorReporter.SEVERITY_FATAL_ERROR);
2307: }
2308:
2309: private void reportError(String key, Object[] args, short severity) {
2310: if (fErrorReporter != null) {
2311: fErrorReporter.reportError(
2312: XIncludeMessageFormatter.XINCLUDE_DOMAIN, key,
2313: args, severity);
2314: }
2315: // we won't worry about when error reporter is null, since there should always be
2316: // at least the default error reporter
2317: }
2318:
2319: /**
2320: * Set the parent of this XIncludeHandler in the tree
2321: * @param parent
2322: */
2323: protected void setParent(XIncludeHandler parent) {
2324: fParentXIncludeHandler = parent;
2325: }
2326:
2327: protected void setHref(String href) {
2328: fHrefFromParent = href;
2329: }
2330:
2331: protected void setXIncludeLocator(XMLLocatorWrapper locator) {
2332: fXIncludeLocator = locator;
2333: }
2334:
2335: // used to know whether to pass declarations to the document handler
2336: protected boolean isRootDocument() {
2337: return fParentXIncludeHandler == null;
2338: }
2339:
2340: /**
2341: * Caches an unparsed entity.
2342: * @param name the name of the unparsed entity
2343: * @param identifier the location of the unparsed entity
2344: * @param augmentations any Augmentations that were on the original unparsed entity declaration
2345: */
2346: protected void addUnparsedEntity(String name,
2347: XMLResourceIdentifier identifier, String notation,
2348: Augmentations augmentations) {
2349: UnparsedEntity ent = new UnparsedEntity();
2350: ent.name = name;
2351: ent.systemId = identifier.getLiteralSystemId();
2352: ent.publicId = identifier.getPublicId();
2353: ent.baseURI = identifier.getBaseSystemId();
2354: ent.expandedSystemId = identifier.getExpandedSystemId();
2355: ent.notation = notation;
2356: ent.augmentations = augmentations;
2357: fUnparsedEntities.add(ent);
2358: }
2359:
2360: /**
2361: * Caches a notation.
2362: * @param name the name of the notation
2363: * @param identifier the location of the notation
2364: * @param augmentations any Augmentations that were on the original notation declaration
2365: */
2366: protected void addNotation(String name,
2367: XMLResourceIdentifier identifier,
2368: Augmentations augmentations) {
2369: Notation not = new Notation();
2370: not.name = name;
2371: not.systemId = identifier.getLiteralSystemId();
2372: not.publicId = identifier.getPublicId();
2373: not.baseURI = identifier.getBaseSystemId();
2374: not.expandedSystemId = identifier.getExpandedSystemId();
2375: not.augmentations = augmentations;
2376: fNotations.add(not);
2377: }
2378:
2379: /**
2380: * Checks if an UnparsedEntity with the given name was declared in the DTD of the document
2381: * for the current pipeline. If so, then the notation for the UnparsedEntity is checked.
2382: * If that turns out okay, then the UnparsedEntity is passed to the root pipeline to
2383: * be checked for conflicts, and sent to the root DTDHandler.
2384: *
2385: * @param entName the name of the UnparsedEntity to check
2386: */
2387: protected void checkUnparsedEntity(String entName) {
2388: UnparsedEntity ent = new UnparsedEntity();
2389: ent.name = entName;
2390: int index = fUnparsedEntities.indexOf(ent);
2391: if (index != -1) {
2392: ent = (UnparsedEntity) fUnparsedEntities.get(index);
2393: // first check the notation of the unparsed entity
2394: checkNotation(ent.notation);
2395: checkAndSendUnparsedEntity(ent);
2396: }
2397: }
2398:
2399: /**
2400: * Checks if a Notation with the given name was declared in the DTD of the document
2401: * for the current pipeline. If so, that Notation is passed to the root pipeline to
2402: * be checked for conflicts, and sent to the root DTDHandler
2403: *
2404: * @param notName the name of the Notation to check
2405: */
2406: protected void checkNotation(String notName) {
2407: Notation not = new Notation();
2408: not.name = notName;
2409: int index = fNotations.indexOf(not);
2410: if (index != -1) {
2411: not = (Notation) fNotations.get(index);
2412: checkAndSendNotation(not);
2413: }
2414: }
2415:
2416: /**
2417: * The purpose of this method is to check if an UnparsedEntity conflicts with a previously
2418: * declared entity in the current pipeline stack. If there is no conflict, the
2419: * UnparsedEntity is sent by the root pipeline.
2420: *
2421: * @param ent the UnparsedEntity to check for conflicts
2422: */
2423: protected void checkAndSendUnparsedEntity(UnparsedEntity ent) {
2424: if (isRootDocument()) {
2425: int index = fUnparsedEntities.indexOf(ent);
2426: if (index == -1) {
2427: // There is no unparsed entity with the same name that we have sent.
2428: // Calling unparsedEntityDecl() will add the entity to our local store,
2429: // and also send the unparsed entity to the DTDHandler
2430: XMLResourceIdentifier id = new XMLResourceIdentifierImpl(
2431: ent.publicId, ent.systemId, ent.baseURI,
2432: ent.expandedSystemId);
2433: addUnparsedEntity(ent.name, id, ent.notation,
2434: ent.augmentations);
2435: if (fSendUEAndNotationEvents && fDTDHandler != null) {
2436: fDTDHandler.unparsedEntityDecl(ent.name, id,
2437: ent.notation, ent.augmentations);
2438: }
2439: } else {
2440: UnparsedEntity localEntity = (UnparsedEntity) fUnparsedEntities
2441: .get(index);
2442: if (!ent.isDuplicate(localEntity)) {
2443: reportFatalError("NonDuplicateUnparsedEntity",
2444: new Object[] { ent.name });
2445: }
2446: }
2447: } else {
2448: fParentXIncludeHandler.checkAndSendUnparsedEntity(ent);
2449: }
2450: }
2451:
2452: /**
2453: * The purpose of this method is to check if a Notation conflicts with a previously
2454: * declared notation in the current pipeline stack. If there is no conflict, the
2455: * Notation is sent by the root pipeline.
2456: *
2457: * @param not the Notation to check for conflicts
2458: */
2459: protected void checkAndSendNotation(Notation not) {
2460: if (isRootDocument()) {
2461: int index = fNotations.indexOf(not);
2462: if (index == -1) {
2463: // There is no notation with the same name that we have sent.
2464: XMLResourceIdentifier id = new XMLResourceIdentifierImpl(
2465: not.publicId, not.systemId, not.baseURI,
2466: not.expandedSystemId);
2467: addNotation(not.name, id, not.augmentations);
2468: if (fSendUEAndNotationEvents && fDTDHandler != null) {
2469: fDTDHandler.notationDecl(not.name, id,
2470: not.augmentations);
2471: }
2472: } else {
2473: Notation localNotation = (Notation) fNotations
2474: .get(index);
2475: if (!not.isDuplicate(localNotation)) {
2476: reportFatalError("NonDuplicateNotation",
2477: new Object[] { not.name });
2478: }
2479: }
2480: } else {
2481: fParentXIncludeHandler.checkAndSendNotation(not);
2482: }
2483: }
2484:
2485: /**
2486: * Checks whether the string only contains white space characters.
2487: *
2488: * @param value the text to check
2489: */
2490: private void checkWhitespace(XMLString value) {
2491: int end = value.offset + value.length;
2492: for (int i = value.offset; i < end; ++i) {
2493: if (!XMLChar.isSpace(value.ch[i])) {
2494: reportFatalError("ContentIllegalAtTopLevel");
2495: return;
2496: }
2497: }
2498: }
2499:
2500: /**
2501: * Checks whether the root element has already been processed.
2502: */
2503: private void checkMultipleRootElements() {
2504: if (getRootElementProcessed()) {
2505: reportFatalError("MultipleRootElements");
2506: }
2507: setRootElementProcessed(true);
2508: }
2509:
2510: /**
2511: * Sets whether the root element has been processed.
2512: */
2513: private void setRootElementProcessed(boolean seenRoot) {
2514: if (isRootDocument()) {
2515: fSeenRootElement = seenRoot;
2516: return;
2517: }
2518: fParentXIncludeHandler.setRootElementProcessed(seenRoot);
2519: }
2520:
2521: /**
2522: * Returns whether the root element has been processed.
2523: */
2524: private boolean getRootElementProcessed() {
2525: return isRootDocument() ? fSeenRootElement
2526: : fParentXIncludeHandler.getRootElementProcessed();
2527: }
2528:
2529: // It would be nice if we didn't have to repeat code like this, but there's no interface that has
2530: // setFeature() and addRecognizedFeatures() that the objects have in common.
2531: protected void copyFeatures(XMLComponentManager from,
2532: ParserConfigurationSettings to) {
2533: Enumeration features = Constants.getXercesFeatures();
2534: copyFeatures1(features, Constants.XERCES_FEATURE_PREFIX, from,
2535: to);
2536: features = Constants.getSAXFeatures();
2537: copyFeatures1(features, Constants.SAX_FEATURE_PREFIX, from, to);
2538: }
2539:
2540: protected void copyFeatures(XMLComponentManager from,
2541: XMLParserConfiguration to) {
2542: Enumeration features = Constants.getXercesFeatures();
2543: copyFeatures1(features, Constants.XERCES_FEATURE_PREFIX, from,
2544: to);
2545: features = Constants.getSAXFeatures();
2546: copyFeatures1(features, Constants.SAX_FEATURE_PREFIX, from, to);
2547: }
2548:
2549: private void copyFeatures1(Enumeration features,
2550: String featurePrefix, XMLComponentManager from,
2551: ParserConfigurationSettings to) {
2552: while (features.hasMoreElements()) {
2553: String featureId = featurePrefix
2554: + (String) features.nextElement();
2555:
2556: to.addRecognizedFeatures(new String[] { featureId });
2557:
2558: try {
2559: to.setFeature(featureId, from.getFeature(featureId));
2560: } catch (XMLConfigurationException e) {
2561: // componentManager doesn't support this feature,
2562: // so we won't worry about it
2563: }
2564: }
2565: }
2566:
2567: private void copyFeatures1(Enumeration features,
2568: String featurePrefix, XMLComponentManager from,
2569: XMLParserConfiguration to) {
2570: while (features.hasMoreElements()) {
2571: String featureId = featurePrefix
2572: + (String) features.nextElement();
2573: boolean value = from.getFeature(featureId);
2574:
2575: try {
2576: to.setFeature(featureId, value);
2577: } catch (XMLConfigurationException e) {
2578: // componentManager doesn't support this feature,
2579: // so we won't worry about it
2580: }
2581: }
2582: }
2583:
2584: // This is a storage class to hold information about the notations.
2585: // We're not using XMLNotationDecl because we don't want to lose the augmentations.
2586: protected static class Notation {
2587: public String name;
2588: public String systemId;
2589: public String baseURI;
2590: public String publicId;
2591: public String expandedSystemId;
2592: public Augmentations augmentations;
2593:
2594: // equals() returns true if two Notations have the same name.
2595: // Useful for searching Vectors for notations with the same name
2596: public boolean equals(Object obj) {
2597: if (obj == null) {
2598: return false;
2599: }
2600: if (obj instanceof Notation) {
2601: Notation other = (Notation) obj;
2602: return name.equals(other.name);
2603: }
2604: return false;
2605: }
2606:
2607: // from 4.5.2
2608: // Notation items with the same [name], [system identifier],
2609: // [public identifier], and [declaration base URI] are considered
2610: // to be duplicate. An application may also be able to detect that
2611: // notations are duplicate through other means. For instance, the URI
2612: // resulting from combining the system identifier and the declaration
2613: // base URI is the same.
2614: public boolean isDuplicate(Object obj) {
2615: if (obj != null && obj instanceof Notation) {
2616: Notation other = (Notation) obj;
2617: return name.equals(other.name)
2618: && isEqual(publicId, other.publicId)
2619: && isEqual(expandedSystemId,
2620: other.expandedSystemId);
2621: }
2622: return false;
2623: }
2624:
2625: private boolean isEqual(String one, String two) {
2626: return (one == two || (one != null && one.equals(two)));
2627: }
2628: }
2629:
2630: // This is a storage class to hold information about the unparsed entities.
2631: // We're not using XMLEntityDecl because we don't want to lose the augmentations.
2632: protected static class UnparsedEntity {
2633: public String name;
2634: public String systemId;
2635: public String baseURI;
2636: public String publicId;
2637: public String expandedSystemId;
2638: public String notation;
2639: public Augmentations augmentations;
2640:
2641: // equals() returns true if two UnparsedEntities have the same name.
2642: // Useful for searching Vectors for entities with the same name
2643: public boolean equals(Object obj) {
2644: if (obj == null) {
2645: return false;
2646: }
2647: if (obj instanceof UnparsedEntity) {
2648: UnparsedEntity other = (UnparsedEntity) obj;
2649: return name.equals(other.name);
2650: }
2651: return false;
2652: }
2653:
2654: // from 4.5.1:
2655: // Unparsed entity items with the same [name], [system identifier],
2656: // [public identifier], [declaration base URI], [notation name], and
2657: // [notation] are considered to be duplicate. An application may also
2658: // be able to detect that unparsed entities are duplicate through other
2659: // means. For instance, the URI resulting from combining the system
2660: // identifier and the declaration base URI is the same.
2661: public boolean isDuplicate(Object obj) {
2662: if (obj != null && obj instanceof UnparsedEntity) {
2663: UnparsedEntity other = (UnparsedEntity) obj;
2664: return name.equals(other.name)
2665: && isEqual(publicId, other.publicId)
2666: && isEqual(expandedSystemId,
2667: other.expandedSystemId)
2668: && isEqual(notation, other.notation);
2669: }
2670: return false;
2671: }
2672:
2673: private boolean isEqual(String one, String two) {
2674: return (one == two || (one != null && one.equals(two)));
2675: }
2676: }
2677:
2678: // The following methods are used for XML Base processing
2679:
2680: /**
2681: * Saves the current base URI to the top of the stack.
2682: */
2683: protected void saveBaseURI() {
2684: fBaseURIScope.push(fDepth);
2685: fBaseURI.push(fCurrentBaseURI.getBaseSystemId());
2686: fLiteralSystemID.push(fCurrentBaseURI.getLiteralSystemId());
2687: fExpandedSystemID.push(fCurrentBaseURI.getExpandedSystemId());
2688: }
2689:
2690: /**
2691: * Discards the URIs at the top of the stack, and restores the ones beneath it.
2692: */
2693: protected void restoreBaseURI() {
2694: fBaseURI.pop();
2695: fLiteralSystemID.pop();
2696: fExpandedSystemID.pop();
2697: fBaseURIScope.pop();
2698: fCurrentBaseURI.setBaseSystemId((String) fBaseURI.peek());
2699: fCurrentBaseURI.setLiteralSystemId((String) fLiteralSystemID
2700: .peek());
2701: fCurrentBaseURI.setExpandedSystemId((String) fExpandedSystemID
2702: .peek());
2703: }
2704:
2705: // The following methods are used for language processing
2706:
2707: /**
2708: * Saves the given language on the top of the stack.
2709: *
2710: * @param language the language to push onto the stack.
2711: */
2712: protected void saveLanguage(String language) {
2713: fLanguageScope.push(fDepth);
2714: fLanguageStack.push(language);
2715: }
2716:
2717: /**
2718: * Discards the language at the top of the stack, and returns the one beneath it.
2719: */
2720: public String restoreLanguage() {
2721: fLanguageStack.pop();
2722: fLanguageScope.pop();
2723: return (String) fLanguageStack.peek();
2724: }
2725:
2726: /**
2727: * Gets the base URI that was in use at that depth
2728: * @param depth
2729: * @return the base URI
2730: */
2731: public String getBaseURI(int depth) {
2732: int scope = scopeOfBaseURI(depth);
2733: return (String) fExpandedSystemID.elementAt(scope);
2734: }
2735:
2736: /**
2737: * Gets the language that was in use at that depth.
2738: * @param depth
2739: * @return the language
2740: */
2741: public String getLanguage(int depth) {
2742: int scope = scopeOfLanguage(depth);
2743: return (String) fLanguageStack.elementAt(scope);
2744: }
2745:
2746: /**
2747: * Returns a relative URI, which when resolved against the base URI at the
2748: * specified depth, will create the current base URI.
2749: * This is accomplished by merged the literal system IDs.
2750: * @param depth the depth at which to start creating the relative URI
2751: * @return a relative URI to convert the base URI at the given depth to the current
2752: * base URI
2753: */
2754: public String getRelativeURI(int depth)
2755: throws MalformedURIException {
2756: // The literal system id at the location given by "start" is *in focus* at
2757: // the given depth. So we need to adjust it to the next scope, so that we
2758: // only process out of focus literal system ids
2759: int start = scopeOfBaseURI(depth) + 1;
2760: if (start == fBaseURIScope.size()) {
2761: // If that is the last system id, then we don't need a relative URI
2762: return "";
2763: }
2764: URI uri = new URI("file", (String) fLiteralSystemID
2765: .elementAt(start));
2766: for (int i = start + 1; i < fBaseURIScope.size(); i++) {
2767: uri = new URI(uri, (String) fLiteralSystemID.elementAt(i));
2768: }
2769: return uri.getPath();
2770: }
2771:
2772: // We need to find two consecutive elements in the scope stack,
2773: // such that the first is lower than 'depth' (or equal), and the
2774: // second is higher.
2775: private int scopeOfBaseURI(int depth) {
2776: for (int i = fBaseURIScope.size() - 1; i >= 0; i--) {
2777: if (fBaseURIScope.elementAt(i) <= depth)
2778: return i;
2779: }
2780: // we should never get here, because 0 was put on the stack in startDocument()
2781: return -1;
2782: }
2783:
2784: private int scopeOfLanguage(int depth) {
2785: for (int i = fLanguageScope.size() - 1; i >= 0; i--) {
2786: if (fLanguageScope.elementAt(i) <= depth)
2787: return i;
2788: }
2789: // we should never get here, because 0 was put on the stack in startDocument()
2790: return -1;
2791: }
2792:
2793: /**
2794: * Search for a xml:base attribute, and if one is found, put the new base URI into
2795: * effect.
2796: */
2797: protected void processXMLBaseAttributes(XMLAttributes attributes) {
2798: String baseURIValue = attributes.getValue(
2799: NamespaceContext.XML_URI, "base");
2800: if (baseURIValue != null) {
2801: try {
2802: String expandedValue = XMLEntityManager.expandSystemId(
2803: baseURIValue, fCurrentBaseURI
2804: .getExpandedSystemId(), false);
2805: fCurrentBaseURI.setLiteralSystemId(baseURIValue);
2806: fCurrentBaseURI.setBaseSystemId(fCurrentBaseURI
2807: .getExpandedSystemId());
2808: fCurrentBaseURI.setExpandedSystemId(expandedValue);
2809:
2810: // push the new values on the stack
2811: saveBaseURI();
2812: } catch (MalformedURIException e) {
2813: // REVISIT: throw error here
2814: }
2815: }
2816: }
2817:
2818: /**
2819: * Search for a xml:lang attribute, and if one is found, put the new
2820: * [language] into effect.
2821: */
2822: protected void processXMLLangAttributes(XMLAttributes attributes) {
2823: String language = attributes.getValue(NamespaceContext.XML_URI,
2824: "lang");
2825: if (language != null) {
2826: fCurrentLanguage = language;
2827: saveLanguage(fCurrentLanguage);
2828: }
2829: }
2830:
2831: /**
2832: * Returns <code>true</code> if the given string
2833: * would be valid in an HTTP header.
2834: *
2835: * @param value string to check
2836: * @return <code>true</code> if the given string
2837: * would be valid in an HTTP header
2838: */
2839: private boolean isValidInHTTPHeader(String value) {
2840: char ch;
2841: for (int i = value.length() - 1; i >= 0; --i) {
2842: ch = value.charAt(i);
2843: if (ch < 0x20 || ch > 0x7E) {
2844: return false;
2845: }
2846: }
2847: return true;
2848: }
2849:
2850: /**
2851: * Returns a new <code>XMLInputSource</code> from the given parameters.
2852: */
2853: private XMLInputSource createInputSource(String publicId,
2854: String systemId, String baseSystemId, String accept,
2855: String acceptLanguage) {
2856:
2857: HTTPInputSource httpSource = new HTTPInputSource(publicId,
2858: systemId, baseSystemId);
2859: if (accept != null && accept.length() > 0) {
2860: httpSource.setHTTPRequestProperty(
2861: XIncludeHandler.HTTP_ACCEPT, accept);
2862: }
2863: if (acceptLanguage != null && acceptLanguage.length() > 0) {
2864: httpSource.setHTTPRequestProperty(
2865: XIncludeHandler.HTTP_ACCEPT_LANGUAGE,
2866: acceptLanguage);
2867: }
2868: return httpSource;
2869: }
2870:
2871: private boolean isEqual(String one, String two) {
2872: return (one == two || (one != null && one.equals(two)));
2873: }
2874:
2875: // which ASCII characters need to be escaped
2876: private static final boolean gNeedEscaping[] = new boolean[128];
2877: // the first hex character if a character needs to be escaped
2878: private static final char gAfterEscaping1[] = new char[128];
2879: // the second hex character if a character needs to be escaped
2880: private static final char gAfterEscaping2[] = new char[128];
2881: private static final char[] gHexChs = { '0', '1', '2', '3', '4',
2882: '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
2883: // initialize the above 3 arrays
2884: static {
2885: char[] escChs = { ' ', '<', '>', '"', '{', '}', '|', '\\', '^',
2886: '`' };
2887: int len = escChs.length;
2888: char ch;
2889: for (int i = 0; i < len; i++) {
2890: ch = escChs[i];
2891: gNeedEscaping[ch] = true;
2892: gAfterEscaping1[ch] = gHexChs[ch >> 4];
2893: gAfterEscaping2[ch] = gHexChs[ch & 0xf];
2894: }
2895: }
2896:
2897: //
2898: // Escape an href value according to (4.1.1):
2899: //
2900: // To convert the value of the href attribute to an IRI reference, the following characters must be escaped:
2901: // space #x20
2902: // the delimiters < #x3C, > #x3E and " #x22
2903: // the unwise characters { #x7B, } #x7D, | #x7C, \ #x5C, ^ #x5E and ` #x60
2904: //
2905: // To convert an IRI reference to a URI reference, the following characters must also be escaped:
2906: // the Unicode plane 0 characters #xA0 - #xD7FF, #xF900-#xFDCF, #xFDF0-#xFFEF
2907: // the Unicode plane 1-14 characters #x10000-#x1FFFD ... #xE0000-#xEFFFD
2908: //
2909: private String escapeHref(String href) {
2910: int len = href.length();
2911: int ch;
2912: StringBuffer buffer = new StringBuffer(len * 3);
2913:
2914: // for each character in the href
2915: int i = 0;
2916: for (; i < len; i++) {
2917: ch = href.charAt(i);
2918: // if it's not an ASCII character (excluding 0x7F), break here, and use UTF-8 encoding
2919: if (ch > 0x7E) {
2920: break;
2921: }
2922: // abort: href does not allow this character
2923: if (ch < 0x20) {
2924: return href;
2925: }
2926: if (gNeedEscaping[ch]) {
2927: buffer.append('%');
2928: buffer.append(gAfterEscaping1[ch]);
2929: buffer.append(gAfterEscaping2[ch]);
2930: } else {
2931: buffer.append((char) ch);
2932: }
2933: }
2934:
2935: // we saw some non-ascii character
2936: if (i < len) {
2937: // check if remainder of href contains any illegal characters before proceeding
2938: for (int j = i; j < len; ++j) {
2939: ch = href.charAt(j);
2940: if ((ch >= 0x20 && ch <= 0x7E)
2941: || (ch >= 0xA0 && ch <= 0xD7FF)
2942: || (ch >= 0xF900 && ch <= 0xFDCF)
2943: || (ch >= 0xFDF0 && ch <= 0xFFEF)) {
2944: continue;
2945: }
2946: if (XMLChar.isHighSurrogate(ch) && ++j < len) {
2947: int ch2 = href.charAt(j);
2948: if (XMLChar.isLowSurrogate(ch2)) {
2949: ch2 = XMLChar.supplemental((char) ch,
2950: (char) ch2);
2951: if (ch2 < 0xF0000 && (ch2 & 0xFFFF) <= 0xFFFD) {
2952: continue;
2953: }
2954: }
2955: }
2956: // abort: href does not allow this character
2957: return href;
2958: }
2959:
2960: // get UTF-8 bytes for the remaining sub-string
2961: byte[] bytes = null;
2962: byte b;
2963: try {
2964: bytes = href.substring(i).getBytes("UTF-8");
2965: } catch (java.io.UnsupportedEncodingException e) {
2966: // should never happen
2967: return href;
2968: }
2969: len = bytes.length;
2970:
2971: // for each byte
2972: for (i = 0; i < len; i++) {
2973: b = bytes[i];
2974: // for non-ASCII character: make it positive, then escape
2975: if (b < 0) {
2976: ch = b + 256;
2977: buffer.append('%');
2978: buffer.append(gHexChs[ch >> 4]);
2979: buffer.append(gHexChs[ch & 0xf]);
2980: } else if (gNeedEscaping[b]) {
2981: buffer.append('%');
2982: buffer.append(gAfterEscaping1[b]);
2983: buffer.append(gAfterEscaping2[b]);
2984: } else {
2985: buffer.append((char) b);
2986: }
2987: }
2988: }
2989:
2990: // If escaping happened, create a new string;
2991: // otherwise, return the original one.
2992: if (buffer.length() != len) {
2993: return buffer.toString();
2994: } else {
2995: return href;
2996: }
2997: }
2998: }
|