You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xerces.apache.org by mr...@apache.org on 2003/06/25 02:49:21 UTC

cvs commit: xml-xerces/java/src/org/apache/xerces/xinclude XIncludeHandler.java

mrglavas    2003/06/24 17:49:21

  Added:       java/src/org/apache/xerces/xinclude XIncludeHandler.java
  Log:
  Preliminary support for XInclude submitted by Peter McCracken. It supports: regular/text includes, fallbacks, namespace fix-up, etc...
  
  Revision  Changes    Path
  1.1                  xml-xerces/java/src/org/apache/xerces/xinclude/XIncludeHandler.java
  
  Index: XIncludeHandler.java
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   *
   * Copyright (c) 2001-2003 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Xerces" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation and was
   * originally based on software copyright (c) 1999, International
   * Business Machines, Inc., http://www.apache.org.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  package org.apache.xerces.xinclude;
  
  import java.io.File;
  import java.io.IOException;
  import java.io.InputStreamReader;
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.util.Enumeration;
  import java.util.Vector;
  
  import org.apache.xerces.impl.Constants;
  import org.apache.xerces.impl.XMLEntityManager;
  import org.apache.xerces.impl.XMLErrorReporter;
  import org.apache.xerces.impl.dtd.DTDGrammar;
  import org.apache.xerces.impl.dtd.XMLDTDDescription;
  import org.apache.xerces.util.AugmentationsImpl;
  import org.apache.xerces.util.ObjectFactory;
  import org.apache.xerces.util.XMLAttributesImpl;
  import org.apache.xerces.util.XMLStringBuffer;
  import org.apache.xerces.util.XMLSymbols;
  import org.apache.xerces.util.URI.MalformedURIException;
  import org.apache.xerces.xni.Augmentations;
  import org.apache.xerces.xni.NamespaceContext;
  import org.apache.xerces.xni.QName;
  import org.apache.xerces.xni.XMLAttributes;
  import org.apache.xerces.xni.XMLDocumentHandler;
  import org.apache.xerces.xni.XMLLocator;
  import org.apache.xerces.xni.XMLResourceIdentifier;
  import org.apache.xerces.xni.XMLString;
  import org.apache.xerces.xni.XNIException;
  import org.apache.xerces.xni.grammars.XMLGrammarDescription;
  import org.apache.xerces.xni.grammars.XMLGrammarPool;
  import org.apache.xerces.xni.parser.XMLComponent;
  import org.apache.xerces.xni.parser.XMLComponentManager;
  import org.apache.xerces.xni.parser.XMLConfigurationException;
  import org.apache.xerces.xni.parser.XMLDocumentFilter;
  import org.apache.xerces.xni.parser.XMLDocumentSource;
  import org.apache.xerces.xni.parser.XMLInputSource;
  import org.apache.xerces.xni.parser.XMLParserConfiguration;
  
  /**
   * @author Peter McCracken, IBM
   */
  public class XIncludeHandler implements XMLComponent, XMLDocumentFilter {
  
      public final static String XINCLUDE_DEFAULT_CONFIGURATION =
          "org.apache.xerces.parsers.XIncludeParserConfiguration";
  
      public final static String XINCLUDE_NS_URI =
          "http://www.w3.org/2001/XInclude".intern();
      public final static String XINCLUDE_INCLUDE = "include".intern();
      public final static String XINCLUDE_FALLBACK = "fallback".intern();
  
      public final static String XINCLUDE_PARSE_XML = "xml".intern();
      public final static String XINCLUDE_PARSE_TEXT = "text".intern();
  
      public final static String XINCLUDE_ATTR_HREF = "href".intern();
      public final static String XINCLUDE_ATTR_PARSE = "parse".intern();
      public final static String XINCLUDE_ATTR_ENCODING = "encoding".intern();
  
      // Top Level Information Items have [included] property in infoset
      public final static String XINCLUDE_INCLUDED = "[included]".intern();
  
      // used for adding [base URI] attributes
      public final static String XINCLUDE_BASE = "base";
      public final static QName XML_BASE_QNAME =
          new QName(
              XMLSymbols.PREFIX_XML,
              XINCLUDE_BASE,
              XMLSymbols.PREFIX_XML + ":" + XINCLUDE_BASE,
              NamespaceContext.XML_URI);
  
      public final static QName NEW_NS_ATTR_QNAME =
          new QName(
              XMLSymbols.PREFIX_XMLNS,
              "",
              XMLSymbols.PREFIX_XMLNS + ":",
              NamespaceContext.XMLNS_URI);
  
      // Processing States
      private final static Integer STATE_NORMAL_PROCESSING = new Integer(1);
      private final static Integer STATE_IGNORE = new Integer(2);
      private final static Integer STATE_EXPECT_FALLBACK = new Integer(3);
  
      // recognized features and properties
  
      /** Property identifier: error handler. */
      protected static final String ERROR_REPORTER =
          Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;
  
      /** Property identifier: grammar pool . */
      protected static final String GRAMMAR_POOL =
          Constants.XERCES_PROPERTY_PREFIX + Constants.XMLGRAMMAR_POOL_PROPERTY;
  
      /** Recognized features. */
      private static final String[] RECOGNIZED_FEATURES = {
      };
  
      /** Feature defaults. */
      private static final Boolean[] FEATURE_DEFAULTS = {
      };
  
      /** Recognized properties. */
  
      // TODO: make DEFAULT_XINCLUDE_PIPELINE a property?
      private static final String[] RECOGNIZED_PROPERTIES =
          { ERROR_REPORTER, GRAMMAR_POOL };
  
      /** Property defaults. */
      private static final Object[] PROPERTY_DEFAULTS = { null, null, null, };
  
      // Data
  
      protected XMLDocumentHandler fDocumentHandler;
      protected XMLDocumentSource fDocumentSource;
  
      protected XIncludeHandler fParentXIncludeHandler;
  
      protected XMLLocator fDocLocation;
      protected XIncludeNamespaceSupport fNamespaceContext;
      protected XMLErrorReporter fErrorReporter;
      protected XMLGrammarPool fGrammarPool;
      protected XMLGrammarDescription fGrammarDesc;
      protected DTDGrammar fDTDGrammar;
  
      // The current element depth.
      // This is used to access the appropriate level of the following stacks.
      private int fDepth;
  
      // The depth of the first element to actually be part of the result infoset.
      // This will normally be 1, but it could be larger when the top-level item
      // is an include, and processing goes to the fallback.
      private int fRootDepth;
  
      // TODO: for performance, change these to expanding arrays
  
      // Used to ensure that fallbacks are always children of include elements,
      // and that include elements are never children of other include elements.
      // An index contains true if the ancestor of the current element which resides
      // at that depth was an include element.
      private Vector fSawInclude;
  
      // Ensures that only one fallback element can be at a single depth.
      // An index contains true if we have seen any fallback elements at that depth,
      // and it is only reset to false when the end tag of the parent is encountered.
      private Vector fSawFallback;
  
      // The state of the processor at each given depth.
      private Vector fState;
  
      // Constructors
  
      public XIncludeHandler() {
          this(null);
      }
  
      private XIncludeHandler(XIncludeHandler parent) {
          fParentXIncludeHandler = parent;
  
          fDepth = 0;
          fRootDepth = 0;
  
          fSawFallback = new Vector();
          fSawFallback.add(Boolean.FALSE);
          fSawInclude = new Vector();
          fSawInclude.add(Boolean.FALSE);
          fState = new Vector();
          fState.add(STATE_NORMAL_PROCESSING);
      }
  
      // XMLComponent methods
  
      public void reset(XMLComponentManager componentManager)
          throws XNIException {
          fNamespaceContext = null;
          fDepth = 0;
          fRootDepth = 0;
  
          try {
              setErrorReporter(
                  (XMLErrorReporter)componentManager.getProperty(ERROR_REPORTER));
          }
          catch (XMLConfigurationException e) {
              fErrorReporter = null;
          }
          try {
              fGrammarPool =
                  (XMLGrammarPool)componentManager.getProperty(GRAMMAR_POOL);
          }
          catch (XMLConfigurationException e) {
              fGrammarPool = null;
          }
      } // reset(XMLComponentManager)
  
      /**
       * Returns a list of feature identifiers that are recognized by
       * this component. This method may return null if no features
       * are recognized by this component.
       */
      public String[] getRecognizedFeatures() {
          return RECOGNIZED_FEATURES;
      } // getRecognizedFeatures():String[]
  
      /**
       * Sets the state of a feature. This method is called by the component
       * manager any time after reset when a feature changes state.
       * <p>
       * <strong>Note:</strong> Components should silently ignore features
       * that do not affect the operation of the component.
       *
       * @param featureId The feature identifier.
       * @param state     The state of the feature.
       *
       * @throws SAXNotRecognizedException The component should not throw
       *                                   this exception.
       * @throws SAXNotSupportedException The component should not throw
       *                                  this exception.
       */
      public void setFeature(String featureId, boolean state)
          throws XMLConfigurationException {
      } // setFeature(String,boolean)
  
      /**
       * Returns a list of property identifiers that are recognized by
       * this component. This method may return null if no properties
       * are recognized by this component.
       */
      public String[] getRecognizedProperties() {
          return RECOGNIZED_PROPERTIES;
      } // getRecognizedProperties():String[]
  
      /**
       * Sets the value of a property. This method is called by the component
       * manager any time after reset when a property changes value.
       * <p>
       * <strong>Note:</strong> Components should silently ignore properties
       * that do not affect the operation of the component.
       *
       * @param propertyId The property identifier.
       * @param value      The value of the property.
       *
       * @throws SAXNotRecognizedException The component should not throw
       *                                   this exception.
       * @throws SAXNotSupportedException The component should not throw
       *                                  this exception.
       */
      public void setProperty(String propertyId, Object value)
          throws XMLConfigurationException {
          if (propertyId.equals(ERROR_REPORTER)) {
              setErrorReporter((XMLErrorReporter)value);
          }
          if (propertyId.equals(GRAMMAR_POOL)) {
              fGrammarPool = (XMLGrammarPool)value;
          }
      } // setProperty(String,Object)
  
      /** 
       * Returns the default state for a feature, or null if this
       * component does not want to report a default value for this
       * feature.
       *
       * @param featureId The feature identifier.
       *
       * @since Xerces 2.2.0
       */
      public Boolean getFeatureDefault(String featureId) {
          for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
              if (RECOGNIZED_FEATURES[i].equals(featureId)) {
                  return FEATURE_DEFAULTS[i];
              }
          }
          return null;
      } // getFeatureDefault(String):Boolean
  
      /** 
       * Returns the default state for a property, or null if this
       * component does not want to report a default value for this
       * property. 
       *
       * @param propertyId The property identifier.
       *
       * @since Xerces 2.2.0
       */
      public Object getPropertyDefault(String propertyId) {
          for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
              if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
                  return PROPERTY_DEFAULTS[i];
              }
          }
          return null;
      } // getPropertyDefault(String):Object
  
      private void setErrorReporter(XMLErrorReporter reporter) {
          fErrorReporter = reporter;
          if (fErrorReporter != null) {
              fErrorReporter.putMessageFormatter(
                  XIncludeMessageFormatter.XINCLUDE_DOMAIN,
                  new XIncludeMessageFormatter());
          }
      }
  
      public void setDocumentHandler(XMLDocumentHandler handler) {
          fDocumentHandler = handler;
      }
  
      public XMLDocumentHandler getDocumentHandler() {
          return fDocumentHandler;
      }
  
      // XMLDocumentHandler methods
  
      public void startDocument(
          XMLLocator locator,
          String encoding,
          NamespaceContext namespaceContext,
          Augmentations augs)
          throws XNIException {
  
          if (!isRootDocument()
              && fParentXIncludeHandler.searchForRecursiveIncludes(locator)) {
              throw new XIncludeFatalError("RecursiveInclude", null);
          }
  
          if (!(namespaceContext instanceof XIncludeNamespaceSupport)) {
              throw new XIncludeFatalError("IncompatibleNamespaceContext", null);
          }
          fNamespaceContext = (XIncludeNamespaceSupport)namespaceContext;
          fDocLocation = locator;
  
          if (isRootDocument() && fDocumentHandler != null) {
              fDocumentHandler.startDocument(
                  locator,
                  encoding,
                  namespaceContext,
                  augs);
          }
      }
  
      public void xmlDecl(
          String version,
          String encoding,
          String standalone,
          Augmentations augs)
          throws XNIException {
          if (isRootDocument() && fDocumentHandler != null) {
              fDocumentHandler.xmlDecl(version, encoding, standalone, augs);
          }
      }
  
      public void doctypeDecl(
          String rootElement,
          String publicId,
          String systemId,
          Augmentations augs)
          throws XNIException {
  
          String eid = null;
          try {
              eid =
                  XMLEntityManager.expandSystemId(
                      systemId,
                      fDocLocation.getExpandedSystemId(),
                      false);
          }
          catch (java.io.IOException e) {
          }
  
          fGrammarDesc =
              new XMLDTDDescription(
                  publicId,
                  systemId,
                  fDocLocation.getExpandedSystemId(),
                  eid,
                  rootElement);
  
          if (isRootDocument() && fDocumentHandler != null) {
              fDocumentHandler.doctypeDecl(rootElement, publicId, systemId, augs);
          }
      }
  
      public void comment(XMLString text, Augmentations augs)
          throws XNIException {
          if (fDocumentHandler != null
              && getState() == STATE_NORMAL_PROCESSING) {
              fDepth++;
              augs = modifyAugmentations(augs);
              fDocumentHandler.comment(text, augs);
              fDepth--;
          }
      }
  
      public void processingInstruction(
          String target,
          XMLString data,
          Augmentations augs)
          throws XNIException {
          if (fDocumentHandler != null
              && getState() == STATE_NORMAL_PROCESSING) {
              // we need to change the depth like this so that modifyAugmentations() works
              fDepth++;
              augs = modifyAugmentations(augs);
              fDocumentHandler.processingInstruction(target, data, augs);
              fDepth--;
          }
      }
  
      public void startElement(
          QName element,
          XMLAttributes attributes,
          Augmentations augs)
          throws XNIException {
          fDepth++;
          setState(getState(fDepth - 1));
          if (isIncludeElement(element)) {
              try {
                  this.handleIncludeElement(attributes);
                  // since the above call returned, we've completed the include
                  // ignore any fallback elements, or other children, of the include element
                  setState(STATE_IGNORE);
              }
              catch (XIncludeResourceError e) {
                  setState(STATE_EXPECT_FALLBACK);
              }
          }
          else if (isFallbackElement(element)) {
              this.handleFallbackElement();
          }
          else if (
              fDocumentHandler != null
                  && getState() == STATE_NORMAL_PROCESSING) {
              if (fRootDepth < 1) {
                  fRootDepth = fDepth;
              }
              augs = modifyAugmentations(augs);
              attributes = processAttributes(attributes);
              fDocumentHandler.startElement(element, attributes, augs);
          }
      }
  
      public void emptyElement(
          QName element,
          XMLAttributes attributes,
          Augmentations augs)
          throws XNIException {
          fDepth++;
          setState(getState(fDepth - 1));
          if (isIncludeElement(element)) {
              try {
                  this.handleIncludeElement(attributes);
                  setState(STATE_IGNORE);
              }
              catch (XIncludeResourceError e) {
                  reportFatalError("NoFallback");
              }
          }
          else if (isFallbackElement(element)) {
              this.handleFallbackElement();
          }
          else if (
              fDocumentHandler != null
                  && getState() == STATE_NORMAL_PROCESSING) {
              if (fRootDepth < 1) {
                  fRootDepth = fDepth;
              }
              augs = modifyAugmentations(augs);
              attributes = processAttributes(attributes);
              fDocumentHandler.emptyElement(element, attributes, augs);
              if (fRootDepth == fDepth) {
                  fRootDepth = 0;
              }
          }
          fDepth--;
      }
  
      protected void handleFallbackElement() {
          setSawInclude(fDepth, false);
          fNamespaceContext.setContextInvalid();
          if (!getSawInclude(fDepth - 1)) {
              reportFatalError("FallbackParent");
          }
  
          if (getSawFallback(fDepth)) {
              reportFatalError("MultipleFallbacks");
          }
          else {
              setSawFallback(fDepth, true);
          }
  
          // either the state is STATE_EXPECT_FALLBACK or it's STATE_IGNORE
          // if we're ignoring, we want to stay ignoring. But if we're expecting this fallback element,
          // we want to signal that we should process the children
          if (getState() == STATE_EXPECT_FALLBACK) {
              setState(STATE_NORMAL_PROCESSING);
          }
      }
  
      protected void handleIncludeElement(XMLAttributes attributes)
          throws XNIException {
          setSawInclude(fDepth, true);
          fNamespaceContext.setContextInvalid();
          if (getSawInclude(fDepth - 1)) {
              reportFatalError("IncludeParent");
          }
          if (getState() == STATE_IGNORE)
              return;
  
          String href = attributes.getValue(XINCLUDE_ATTR_HREF);
  
          XMLInputSource includedSource = null;
          if (href == null) {
              reportFatalError("HrefMissing");
          }
  
          includedSource =
              new XMLInputSource(null, href, fDocLocation.getBaseSystemId());
  
          String parse = attributes.getValue(XINCLUDE_ATTR_PARSE);
          if (parse == null) {
              parse = XINCLUDE_PARSE_XML;
          }
  
          if (parse.equals(XINCLUDE_PARSE_XML)) {
              // create pipeline with no schema validator
              // TODO: implement DEFAULT_XINCLUDE_PROPERTY?
              String parserName = XINCLUDE_DEFAULT_CONFIGURATION;
              XMLParserConfiguration parserConfig =
                  (XMLParserConfiguration)ObjectFactory.newInstance(
                      parserName,
                      ObjectFactory.findClassLoader(),
                      true);
  
              // TODO: set all features on parserConfig to match this parser configuration
  
              // we don't want a schema validator on the new pipeline
              parserConfig.setFeature(
                  Constants.XERCES_FEATURE_PREFIX
                      + Constants.SCHEMA_VALIDATION_FEATURE,
                  false);
  
              // use the same error reporter
              parserConfig.setProperty(ERROR_REPORTER, fErrorReporter);
              // use the same namespace context
              parserConfig.setProperty(
                  Constants.XERCES_PROPERTY_PREFIX
                      + Constants.NAMESPACE_CONTEXT_PROPERTY,
                  fNamespaceContext);
  
              XIncludeHandler newHandler =
                  (XIncludeHandler)parserConfig.getProperty(
                      Constants.XERCES_PROPERTY_PREFIX
                          + Constants.XINCLUDE_HANDLER_PROPERTY);
              newHandler.setParent(this);
              newHandler.setDocumentHandler(this.getDocumentHandler());
  
              try {
                  fNamespaceContext.pushScope();
  
                  parserConfig.parse(includedSource);
              }
              catch (XIncludeFatalError e) {
                  reportFatalError(e.getKey(), e.getArgs());
              }
              catch (XNIException e) {
                  reportFatalError("XMLParseError");
              }
              catch (IOException e) {
                  throw new XIncludeResourceError(
                      "XMLResourceError",
                      new Object[] { e.getMessage()});
              }
              finally {
                  fNamespaceContext.popScope();
              }
          }
          // TODO: fix parsing as text.
          // Suggest writing a ParserConfiguration with an appropriate Scanner,
          // so that it will be parallel to how we treat parsing as XML
          else if (parse.equals(XINCLUDE_PARSE_TEXT)) {
              // TODO: This is what the spec says on encoding. Make sure it is being done.
              //     The encoding of such a resource is determined by:
              //         * external encoding information, if available, otherwise
              //         * if the media type of the resource is text/xml, application/xml, or matches the conventions text/*+xml or application/*+xml as described in XML Media Types [IETF RFC 3023], the encoding is recognized as specified in XML 1.0, otherwise
              //         * the value of the encoding attribute if one exists, otherwise
              //         * UTF-8.
              //     Byte sequences outside the range allowed by the encoding are a fatal error. Characters that are not permitted in XML documents also are a fatal error.
  
              // encoding only matters when parse="text"
              String encoding = attributes.getValue(XINCLUDE_ATTR_ENCODING);
              if (encoding != null) {
                  includedSource.setEncoding(encoding);
              }
  
              String systemId = includedSource.getSystemId();
              InputStreamReader reader = null;
              try {
                  URL url = this.createURL(includedSource);
                  if (encoding != null) {
                      reader = new InputStreamReader(url.openStream(), encoding);
                  }
                  else {
                      reader = new InputStreamReader(url.openStream(), "UTF-8");
                  }
  
                  XMLStringBuffer buffer = new XMLStringBuffer();
                  while (reader.ready()) {
                      buffer.append((char)reader.read());
                  }
                  if (fDocumentHandler != null) {
                      fDocumentHandler.characters(
                          buffer,
                          modifyAugmentations(null, true));
                  }
                  reader.close();
              }
              catch (IOException e) {
                  throw new XIncludeResourceError(
                      "TextResourceError",
                      new Object[] { e.getMessage()});
              }
          }
          else {
              reportFatalError("InvalidParseValue", new Object[] { parse });
          }
      }
  
      public void endElement(QName element, Augmentations augs)
          throws XNIException {
  
          if (isIncludeElement(element)) {
              // if we're ending an include element, and we were expecting a fallback
              // we check to see if the children of this include element contained a fallback
              if (getState() == STATE_EXPECT_FALLBACK
                  && !getSawFallback(fDepth + 1)) {
                  reportFatalError("NoFallback");
              }
          }
          if (isFallbackElement(element)) {
              // the state would have been set to normal processing if we were expecting the fallback element
              // now that we're done processing it, we should ignore all the other children of the include element
              if (getState() == STATE_NORMAL_PROCESSING) {
                  setState(STATE_IGNORE);
              }
          }
          else if (
              fDocumentHandler != null
                  && getState() == STATE_NORMAL_PROCESSING) {
              fDocumentHandler.endElement(element, augs);
              if (fRootDepth == fDepth) {
                  fRootDepth = 0;
              }
          }
  
          // reset the out of scope stack elements
          setSawFallback(fDepth + 1, false);
          setSawInclude(fDepth + 1, false);
  
          fDepth--;
      }
  
      public void startGeneralEntity(
          String name,
          XMLResourceIdentifier resId,
          String encoding,
          Augmentations augs)
          throws XNIException {
          if (fDocumentHandler != null
              && getState() == STATE_NORMAL_PROCESSING) {
              fDocumentHandler.startGeneralEntity(name, resId, encoding, augs);
          }
      }
  
      public void textDecl(String version, String encoding, Augmentations augs)
          throws XNIException {
          if (fDocumentHandler != null
              && getState() == STATE_NORMAL_PROCESSING) {
              fDocumentHandler.textDecl(version, encoding, augs);
          }
      }
  
      public void endGeneralEntity(String name, Augmentations augs)
          throws XNIException {
          if (fDocumentHandler != null
              && getState() == STATE_NORMAL_PROCESSING) {
              fDocumentHandler.endGeneralEntity(name, augs);
          }
      }
  
      public void characters(XMLString text, Augmentations augs)
          throws XNIException {
          if (fDocumentHandler != null
              && getState() == STATE_NORMAL_PROCESSING) {
              // we need to change the depth like this so that modifyAugmentations() works
              fDepth++;
              augs = modifyAugmentations(augs);
              fDocumentHandler.characters(text, augs);
              fDepth--;
          }
      }
  
      public void ignorableWhitespace(XMLString text, Augmentations augs)
          throws XNIException {
          if (fDocumentHandler != null
              && getState() == STATE_NORMAL_PROCESSING) {
              fDocumentHandler.ignorableWhitespace(text, augs);
          }
      }
  
      public void startCDATA(Augmentations augs) throws XNIException {
          if (fDocumentHandler != null
              && getState() == STATE_NORMAL_PROCESSING) {
              fDocumentHandler.startCDATA(augs);
          }
      }
  
      public void endCDATA(Augmentations augs) throws XNIException {
          if (fDocumentHandler != null
              && getState() == STATE_NORMAL_PROCESSING) {
              fDocumentHandler.endCDATA(augs);
          }
      }
  
      public void endDocument(Augmentations augs) throws XNIException {
          if (isRootDocument() && fDocumentHandler != null) {
              fDocumentHandler.endDocument(augs);
          }
      }
  
      public void setDocumentSource(XMLDocumentSource source) {
          fDocumentSource = source;
      }
  
      public XMLDocumentSource getDocumentSource() {
          return fDocumentSource;
      }
  
      protected boolean isIncludeElement(QName element) {
          return element.localpart.equals(XINCLUDE_INCLUDE)
              && fNamespaceContext.getURI(element.prefix).equals(XINCLUDE_NS_URI);
      }
  
      protected boolean isFallbackElement(QName element) {
          return element.localpart.equals(XINCLUDE_FALLBACK)
              && fNamespaceContext.getURI(element.prefix).equals(XINCLUDE_NS_URI);
      }
  
      protected boolean sameBaseURISourceAsParent() {
          if (fDepth == fRootDepth) {
              try {
                  // We test if the included file is in the same directory as it's parent
                  // by creating a new URL, with the filename of the included file and the
                  // base of the parent, and checking if it is the same file as the included file
                  URL input = createURL(fDocLocation);
                  URL parent = createURL(fParentXIncludeHandler.fDocLocation);
                  URL test = new URL(parent, new File(input.getFile()).getName());
                  return input.sameFile(test);
              }
              catch (MalformedURLException e) {
                  return false;
              }
          }
          return true;
      }
  
      protected boolean searchForRecursiveIncludes(XMLLocator includedSource) {
          String includedSystemId = includedSource.getExpandedSystemId();
  
          if (includedSystemId == null) {
              try {
                  includedSystemId =
                      XMLEntityManager.expandSystemId(
                          includedSource.getLiteralSystemId(),
                          includedSource.getBaseSystemId(),
                          false);
              }
              catch (MalformedURIException e) {
                  reportFatalError("ExpandedSystemId");
                  // returning false opens the possibility of attempting an infinite
                  // recursive parse, in the case when:
                  //   we can't resolve the system id
                  //   AND continue-after-fatal-error is set
                  //   AND there is actually a recursive parse.
                  // But that's probably a pretty slim chance.
                  return false;
              }
          }
  
          if (includedSystemId.equals(fDocLocation.getExpandedSystemId())) {
              return true;
          }
  
          if (fParentXIncludeHandler == null) {
              return false;
          }
          return fParentXIncludeHandler.searchForRecursiveIncludes(
              includedSource);
      }
  
      protected boolean isTopLevelIncludedItem() {
          return isTopLevelIncludedItemViaInclude()
              || isTopLevelIncludedItemViaFallback();
      }
  
      protected boolean isTopLevelIncludedItemViaInclude() {
          return fDepth == 1 && !isRootDocument();
      }
  
      protected boolean isTopLevelIncludedItemViaFallback() {
          return getSawFallback(fDepth - 1);
      }
  
      protected XMLAttributes processAttributes(XMLAttributes attributes) {
          if (isTopLevelIncludedItem()) {
              // Modify attributes to fix the base URI (spec 4.5.5).
              // We only do it to top level included elements, which have a different
              // base URI than their include parent.
              if (!sameBaseURISourceAsParent()) {
                  if (attributes == null) {
                      attributes = new XMLAttributesImpl();
                  }
  
                  // this causes errors with schema validation, since the schema doesn't specify that these elements can have an xml:base attribute
                  // TODO: add a user option to turn this off?
                  int index =
                      attributes.addAttribute(
                          XML_BASE_QNAME,
                          XMLSymbols.fCDATASymbol,
                          this.fDocLocation.getBaseSystemId());
                  attributes.setSpecified(index, true);
              }
  
              // Modify attributes of included items to do namespace-fixup. (spec 4.5.4)
              Enumeration inscopeNS = fNamespaceContext.getAllPrefixes();
              while (inscopeNS.hasMoreElements()) {
                  String prefix = (String)inscopeNS.nextElement();
                  String parentURI =
                      fNamespaceContext.getURIFromIncludeParent(prefix);
                  String uri = fNamespaceContext.getURI(prefix);
                  if (attributes.getValue(NamespaceContext.XMLNS_URI, prefix)
                      == null
                      && parentURI != uri) {
                      QName ns = (QName)NEW_NS_ATTR_QNAME.clone();
                      ns.localpart = prefix;
                      ns.rawname += prefix;
                      attributes.addAttribute(ns, XMLSymbols.fCDATASymbol, uri);
                  }
              }
          }
  
          if (attributes != null) {
              int length = attributes.getLength();
              for (int i = 0; i < length; i++) {
                  String type = attributes.getType(i);
                  String value = attributes.getValue(i);
                  if (checkGrammar()) {
                      // TODO: 4.5.1
                      if (type == XMLSymbols.fENTITYSymbol) {
  
                      }
                      if (type == XMLSymbols.fENTITIESSymbol) {
  
                      }
                      else if (type == XMLSymbols.fNOTATIONSymbol) {
                          // TODO: 4.5.2
                          //       Obtain [notations] property from DTD grammar
                          // TODO: 4.5.2 -- don't forget about searching unparsed entities for notations, too
                      }
                  }
                  /* We actually don't need to do anything for 4.5.3, because at this stage the
                   * value of the attribute is just a string. It will be taken care of later
                   * in the pipeline, when the IDREFs are actually resolved against elements.
                   *
                   * TODO: what about when XInclude processing comes after schema validation? 
                   * 
                   * if (type == XMLSymbols.fIDREFSymbol
                   *    || type == XMLSymbols.fIDREFSSymbol) {
                   *    // look at ValidationState for IDREFs (it should be a property)
                   * }
                   */
              }
          }
  
          return attributes;
      }
  
      protected Augmentations modifyAugmentations(Augmentations augs) {
          return modifyAugmentations(augs, false);
      }
  
      protected Augmentations modifyAugmentations(
          Augmentations augs,
          boolean force) {
          if (force || isTopLevelIncludedItem()) {
              if (augs == null) {
                  augs = new AugmentationsImpl();
              }
              augs.putItem(XINCLUDE_INCLUDED, Boolean.TRUE);
          }
          return augs;
      }
  
      protected Integer getState(int val) {
          return (Integer)this.fState.elementAt(val);
      }
  
      protected Integer getState() {
          return this.getState(fDepth);
      }
  
      protected void setState(Integer state) {
          while (fDepth >= fState.size()) {
              this.fState.add(STATE_NORMAL_PROCESSING);
          }
          this.fState.set(fDepth, state);
      }
  
      protected void setSawFallback(int depth, boolean val) {
          this.setSawFallback(depth, val ? Boolean.TRUE : Boolean.FALSE);
      }
  
      protected void setSawFallback(int depth, Boolean val) {
          while (depth >= fSawFallback.size()) {
              this.fSawFallback.add(Boolean.FALSE);
          }
          this.fSawFallback.set(depth, val);
      }
  
      protected boolean getSawFallback(int depth) {
          if (depth >= this.fSawFallback.size()) {
              return false;
          }
          return ((Boolean)this.fSawFallback.elementAt(depth)).booleanValue();
      }
  
      protected void setSawInclude(int depth, boolean val) {
          this.setSawInclude(depth, val ? Boolean.TRUE : Boolean.FALSE);
      }
  
      protected void setSawInclude(int depth, Boolean val) {
          while (depth >= fSawInclude.size()) {
              this.fSawInclude.add(Boolean.FALSE);
          }
          this.fSawInclude.set(depth, val);
      }
  
      protected boolean getSawInclude(int depth) {
          if (depth >= this.fSawInclude.size()) {
              return false;
          }
          return ((Boolean)this.fSawInclude.elementAt(depth)).booleanValue();
      }
  
      // TODO: this method really doesn't belong here; it's only temporary to make text include work
      //       see comment about XINCLUDE_PARSE_TEXT
      // TODO: does Java use IURIs by default?
      //       [Definition: An internationalized URI reference, or IURI, is a URI reference that directly uses [Unicode] characters.]
      // TODO: figure out what section 4.1.1 of the XInclude spec is talking about
      //       has to do with disallowed ASCII character escaping
      //       this ties in with the above IURI section, but I suspect Java already does it
      private URL createURL(XMLLocator source) throws MalformedURLException {
          return new URL(
              new URL(source.getBaseSystemId()),
              source.getExpandedSystemId());
      }
  
      private URL createURL(XMLInputSource source) throws MalformedURLException {
          return new URL(new URL(source.getBaseSystemId()), source.getSystemId());
      }
  
      protected void reportFatalError(String key) {
          this.reportFatalError(key, null);
      }
  
      protected void reportFatalError(String key, Object[] args) {
          if (fErrorReporter != null) {
              fErrorReporter.reportError(
                  fDocLocation,
                  XIncludeMessageFormatter.XINCLUDE_DOMAIN,
                  key,
                  args,
                  XMLErrorReporter.SEVERITY_FATAL_ERROR);
          }
          // we won't worry about when error reporter is null, since there should always be
          // at least the default error reporter
      }
  
      protected void setParent(XIncludeHandler parent) {
          this.fParentXIncludeHandler = parent;
      }
  
      // used to know whether to pass declarations to the document handler
      protected boolean isRootDocument() {
          return this.fParentXIncludeHandler == null;
      }
  
      private boolean checkGrammar() {
          /*        System.err.println("checking grammar");
                  if (fDTDGrammar == null) {
                      System.err.println("grammar is null");
                      if (fGrammarDesc == null || fGrammarPool == null) {
                          System.err.println("bailed out");
                          return false;
                      }
                      fDTDGrammar =
                          (DTDGrammar)fGrammarPool.retrieveGrammar(fGrammarDesc);
                      System.err.println(fDTDGrammar);
                      return fDTDGrammar != null;
                  }
                  else {
                      System.err.println("have grammar!");
                      return true;
                  }
          */
          return false;
      }
  } // class XIncludeHandler
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: xerces-cvs-unsubscribe@xml.apache.org
For additional commands, e-mail: xerces-cvs-help@xml.apache.org