19f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson/*
29f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * Licensed to the Apache Software Foundation (ASF) under one
39f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * or more contributor license agreements. See the NOTICE file
49f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * distributed with this work for additional information
59f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * regarding copyright ownership. The ASF licenses this file
69f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * to you under the Apache License, Version 2.0 (the  "License");
79f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * you may not use this file except in compliance with the License.
89f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * You may obtain a copy of the License at
99f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson *
109f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson *     http://www.apache.org/licenses/LICENSE-2.0
119f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson *
129f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * Unless required by applicable law or agreed to in writing, software
139f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS,
149f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
159f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * See the License for the specific language governing permissions and
169f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * limitations under the License.
179f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson */
189f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson/*
199f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * $Id: DOMHelper.java 468655 2006-10-28 07:12:06Z minchau $
209f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson */
219f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonpackage org.apache.xml.utils;
229f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
239f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport java.util.Hashtable;
249f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport java.util.Vector;
259f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
269f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport javax.xml.XMLConstants;
279f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport javax.xml.parsers.DocumentBuilder;
289f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport javax.xml.parsers.DocumentBuilderFactory;
299f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport javax.xml.parsers.ParserConfigurationException;
309f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
319f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport org.apache.xml.dtm.ref.DTMNodeProxy;
329f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport org.apache.xml.res.XMLErrorResources;
339f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport org.apache.xml.res.XMLMessages;
349f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
359f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport org.w3c.dom.Attr;
369f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport org.w3c.dom.DOMImplementation;
379f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport org.w3c.dom.Document;
389f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport org.w3c.dom.DocumentType;
399f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport org.w3c.dom.Element;
409f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport org.w3c.dom.Entity;
419f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport org.w3c.dom.NamedNodeMap;
429f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport org.w3c.dom.Node;
439f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonimport org.w3c.dom.Text;
449f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
459f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson/**
469f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * @deprecated Since the introduction of the DTM, this class will be removed.
479f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * This class provides a front-end to DOM implementations, providing
489f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * a number of utility functions that either aren't yet standardized
499f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * by the DOM spec or that are defined in optional DOM modules and
509f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson * hence may not be present in all DOMs.
519f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson */
529f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilsonpublic class DOMHelper
539f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson{
549f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
559f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
569f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * DOM Level 1 did not have a standard mechanism for creating a new
579f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Document object. This function provides a DOM-implementation-independent
589f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * abstraction for that for that concept. It's typically used when
599f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * outputting a new DOM as the result of an operation.
609f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * <p>
619f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * TODO: This isn't directly compatable with DOM Level 2.
629f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * The Level 2 createDocument call also creates the root
639f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * element, and thus requires that you know what that element will be
649f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * before creating the Document. We should think about whether we want
659f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * to change this code, and the callers, so we can use the DOM's own
669f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * method. (It's also possible that DOM Level 3 may relax this
679f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * sequence, but you may give up some intelligence in the DOM by
689f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * doing so; the intent was that knowing the document type and root
699f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * element might let the DOM automatically switch to a specialized
709f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * subclass for particular kinds of documents.)
719f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
729f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param isSecureProcessing state of the secure processing feature.
739f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return The newly created DOM Document object, with no children, or
749f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * null if we can't find a DOM implementation that permits creating
759f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * new empty Documents.
769f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
779f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public static Document createDocument(boolean isSecureProcessing)
789f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
799f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
809f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    try
819f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
829f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
839f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // Use an implementation of the JAVA API for XML Parsing 1.0 to
849f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // create a DOM Document node to contain the result.
859f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
869f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
879f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      dfactory.setNamespaceAware(true);
889f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // BEGIN android-removed
899f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      //     If set, DocumentBuilderFactoryImpl.newDocumentBuilder() fails
909f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      //     because we haven't implemented validation
919f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // dfactory.setValidating(true);
929f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // BEGIN android-removed
939f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
949f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // BEGIN android-removed
959f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      //     We haven't implemented secure processing
969f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // if (isSecureProcessing)
979f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // {
989f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      //   try
999f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      //   {
1009f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      //     dfactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
1019f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      //   }
1029f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      //   catch (ParserConfigurationException pce) {}
1039f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // }
1049f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // END android-removed
1059f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
1069f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
1079f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      Document outNode = docBuilder.newDocument();
1089f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
1099f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      return outNode;
1109f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
1119f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    catch (ParserConfigurationException pce)
1129f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
1139f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      throw new RuntimeException(
1149f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        XMLMessages.createXMLMessage(
1159f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          XMLErrorResources.ER_CREATEDOCUMENT_NOT_SUPPORTED, null));  //"createDocument() not supported in XPathContext!");
1169f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
1179f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // return null;
1189f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
1199f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
1209f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
1219f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
1229f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * DOM Level 1 did not have a standard mechanism for creating a new
1239f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Document object. This function provides a DOM-implementation-independent
1249f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * abstraction for that for that concept. It's typically used when
1259f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * outputting a new DOM as the result of an operation.
1269f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
1279f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return The newly created DOM Document object, with no children, or
1289f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * null if we can't find a DOM implementation that permits creating
1299f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * new empty Documents.
1309f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
1319f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public static Document createDocument()
1329f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
1339f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return createDocument(false);
1349f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
1359f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
1369f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
1379f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Tells, through the combination of the default-space attribute
1389f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * on xsl:stylesheet, xsl:strip-space, xsl:preserve-space, and the
1399f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * xml:space attribute, whether or not extra whitespace should be stripped
1409f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * from the node.  Literal elements from template elements should
1419f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * <em>not</em> be tested with this function.
1429f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param textNode A text node from the source tree.
1439f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return true if the text node should be stripped of extra whitespace.
1449f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
1459f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @throws javax.xml.transform.TransformerException
1469f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @xsl.usage advanced
1479f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
1489f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public boolean shouldStripSourceNode(Node textNode)
1499f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          throws javax.xml.transform.TransformerException
1509f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
1519f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
1529f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    // return (null == m_envSupport) ? false : m_envSupport.shouldStripSourceNode(textNode);
1539f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return false;
1549f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
1559f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
1569f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
1579f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Supports the XPath function GenerateID by returning a unique
1589f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * identifier string for any given DOM Node.
1599f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * <p>
1609f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Warning: The base implementation uses the Node object's hashCode(),
1619f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * which is NOT guaranteed to be unique. If that method hasn't been
1629f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * overridden in this DOM ipmlementation, most Java implementions will
1639f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * derive it from the object's address and should be OK... but if
1649f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * your DOM uses a different definition of hashCode (eg hashing the
1659f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * contents of the subtree), or if your DOM may have multiple objects
1669f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * that represent a single Node in the data structure (eg via proxying),
1679f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * you may need to find another way to assign a unique identifier.
1689f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * <p>
1699f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Also, be aware that if nodes are destroyed and recreated, there is
1709f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * an open issue regarding whether an ID may be reused. Currently
1719f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * we're assuming that the input document is stable for the duration
1729f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * of the XPath/XSLT operation, so this shouldn't arise in this context.
1739f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * <p>
1749f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * (DOM Level 3 is investigating providing a unique node "key", but
1759f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * that won't help Level 1 and Level 2 implementations.)
1769f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
1779f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param node whose identifier you want to obtain
1789f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
1799f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return a string which should be different for every Node object.
1809f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
1819f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public String getUniqueID(Node node)
1829f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
1839f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return "N" + Integer.toHexString(node.hashCode()).toUpperCase();
1849f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
1859f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
1869f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
1879f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Figure out whether node2 should be considered as being later
1889f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * in the document than node1, in Document Order as defined
1899f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * by the XPath model. This may not agree with the ordering defined
1909f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * by other XML applications.
1919f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * <p>
1929f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * There are some cases where ordering isn't defined, and neither are
1939f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * the results of this function -- though we'll generally return true.
1949f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
1959f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * TODO: Make sure this does the right thing with attribute nodes!!!
1969f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
1979f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param node1 DOM Node to perform position comparison on.
1989f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param node2 DOM Node to perform position comparison on .
1999f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
2009f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return false if node2 comes before node1, otherwise return true.
2019f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * You can think of this as
2029f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * <code>(node1.documentOrderPosition &lt;= node2.documentOrderPosition)</code>.
2039f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
2049f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public static boolean isNodeAfter(Node node1, Node node2)
2059f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
2069f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    if (node1 == node2 || isNodeTheSame(node1, node2))
2079f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      return true;
2089f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
2099f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // Default return value, if there is no defined ordering
2109f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    boolean isNodeAfter = true;
2119f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
2129f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    Node parent1 = getParentOfNode(node1);
2139f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    Node parent2 = getParentOfNode(node2);
2149f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
2159f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    // Optimize for most common case
2169f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    if (parent1 == parent2 || isNodeTheSame(parent1, parent2))  // then we know they are siblings
2179f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
2189f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      if (null != parent1)
2199f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        isNodeAfter = isNodeAfterSibling(parent1, node1, node2);
2209f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      else
2219f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      {
2229f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                  // If both parents are null, ordering is not defined.
2239f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                  // We're returning a value in lieu of throwing an exception.
2249f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                  // Not a case we expect to arise in XPath, but beware if you
2259f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                  // try to reuse this method.
2269f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
2279f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                  // We can just fall through in this case, which allows us
2289f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                  // to hit the debugging code at the end of the function.
2299f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          //return isNodeAfter;
2309f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      }
2319f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
2329f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    else
2339f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
2349f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
2359f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // General strategy: Figure out the lengths of the two
2369f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // ancestor chains, reconcile the lengths, and look for
2379f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // the lowest common ancestor. If that ancestor is one of
2389f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // the nodes being compared, it comes before the other.
2399f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // Otherwise perform a sibling compare.
2409f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                //
2419f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                // NOTE: If no common ancestor is found, ordering is undefined
2429f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                // and we return the default value of isNodeAfter.
2439f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
2449f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // Count parents in each ancestor chain
2459f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      int nParents1 = 2, nParents2 = 2;  // include node & parent obtained above
2469f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
2479f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      while (parent1 != null)
2489f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      {
2499f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        nParents1++;
2509f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
2519f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        parent1 = getParentOfNode(parent1);
2529f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      }
2539f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
2549f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      while (parent2 != null)
2559f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      {
2569f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        nParents2++;
2579f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
2589f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        parent2 = getParentOfNode(parent2);
2599f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      }
2609f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
2619f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // Initially assume scan for common ancestor starts with
2629f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // the input nodes.
2639f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      Node startNode1 = node1, startNode2 = node2;
2649f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
2659f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // If one ancestor chain is longer, adjust its start point
2669f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // so we're comparing at the same depths
2679f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      if (nParents1 < nParents2)
2689f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      {
2699f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // Adjust startNode2 to depth of startNode1
2709f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        int adjust = nParents2 - nParents1;
2719f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
2729f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        for (int i = 0; i < adjust; i++)
2739f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        {
2749f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          startNode2 = getParentOfNode(startNode2);
2759f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        }
2769f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      }
2779f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      else if (nParents1 > nParents2)
2789f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      {
2799f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // adjust startNode1 to depth of startNode2
2809f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        int adjust = nParents1 - nParents2;
2819f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
2829f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        for (int i = 0; i < adjust; i++)
2839f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        {
2849f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          startNode1 = getParentOfNode(startNode1);
2859f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        }
2869f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      }
2879f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
2889f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      Node prevChild1 = null, prevChild2 = null;  // so we can "back up"
2899f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
2909f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // Loop up the ancestor chain looking for common parent
2919f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      while (null != startNode1)
2929f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      {
2939f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        if (startNode1 == startNode2 || isNodeTheSame(startNode1, startNode2))  // common parent?
2949f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        {
2959f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          if (null == prevChild1)  // first time in loop?
2969f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          {
2979f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
2989f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            // Edge condition: one is the ancestor of the other.
2999f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            isNodeAfter = (nParents1 < nParents2) ? true : false;
3009f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
3019f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            break;  // from while loop
3029f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          }
3039f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          else
3049f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          {
3059f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                        // Compare ancestors below lowest-common as siblings
3069f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            isNodeAfter = isNodeAfterSibling(startNode1, prevChild1,
3079f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                                             prevChild2);
3089f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
3099f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            break;  // from while loop
3109f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          }
3119f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        }  // end if(startNode1 == startNode2)
3129f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
3139f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                // Move up one level and try again
3149f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        prevChild1 = startNode1;
3159f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        startNode1 = getParentOfNode(startNode1);
3169f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        prevChild2 = startNode2;
3179f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        startNode2 = getParentOfNode(startNode2);
3189f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      }  // end while(parents exist to examine)
3199f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }  // end big else (not immediate siblings)
3209f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
3219f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // WARNING: The following diagnostic won't report the early
3229f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // "same node" case. Fix if/when needed.
3239f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
3249f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    /* -- please do not remove... very useful for diagnostics --
3259f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    System.out.println("node1 = "+node1.getNodeName()+"("+node1.getNodeType()+")"+
3269f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    ", node2 = "+node2.getNodeName()
3279f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    +"("+node2.getNodeType()+")"+
3289f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    ", isNodeAfter = "+isNodeAfter); */
3299f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return isNodeAfter;
3309f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }  // end isNodeAfter(Node node1, Node node2)
3319f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
3329f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
3339f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Use DTMNodeProxy to determine whether two nodes are the same.
3349f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
3359f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param node1 The first DOM node to compare.
3369f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param node2 The second DOM node to compare.
3379f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return true if the two nodes are the same.
3389f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
3399f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public static boolean isNodeTheSame(Node node1, Node node2)
3409f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
3419f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    if (node1 instanceof DTMNodeProxy && node2 instanceof DTMNodeProxy)
3429f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      return ((DTMNodeProxy)node1).equals((DTMNodeProxy)node2);
3439f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    else
3449f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      return (node1 == node2);
3459f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
3469f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
3479f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
3489f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Figure out if child2 is after child1 in document order.
3499f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * <p>
3509f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Warning: Some aspects of "document order" are not well defined.
3519f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * For example, the order of attributes is considered
3529f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * meaningless in XML, and the order reported by our model will
3539f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * be consistant for a given invocation but may not
3549f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * match that of either the source file or the serialized output.
3559f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
3569f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param parent Must be the parent of both child1 and child2.
3579f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param child1 Must be the child of parent and not equal to child2.
3589f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param child2 Must be the child of parent and not equal to child1.
3599f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return true if child 2 is after child1 in document order.
3609f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
3619f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  private static boolean isNodeAfterSibling(Node parent, Node child1,
3629f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                                            Node child2)
3639f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
3649f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
3659f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    boolean isNodeAfterSibling = false;
3669f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    short child1type = child1.getNodeType();
3679f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    short child2type = child2.getNodeType();
3689f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
3699f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    if ((Node.ATTRIBUTE_NODE != child1type)
3709f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            && (Node.ATTRIBUTE_NODE == child2type))
3719f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
3729f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
3739f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // always sort attributes before non-attributes.
3749f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      isNodeAfterSibling = false;
3759f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
3769f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    else if ((Node.ATTRIBUTE_NODE == child1type)
3779f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson             && (Node.ATTRIBUTE_NODE != child2type))
3789f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
3799f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
3809f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // always sort attributes before non-attributes.
3819f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      isNodeAfterSibling = true;
3829f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
3839f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    else if (Node.ATTRIBUTE_NODE == child1type)
3849f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
3859f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      NamedNodeMap children = parent.getAttributes();
3869f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      int nNodes = children.getLength();
3879f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      boolean found1 = false, found2 = false;
3889f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
3899f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // Count from the start until we find one or the other.
3909f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      for (int i = 0; i < nNodes; i++)
3919f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      {
3929f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        Node child = children.item(i);
3939f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
3949f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        if (child1 == child || isNodeTheSame(child1, child))
3959f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        {
3969f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          if (found2)
3979f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          {
3989f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            isNodeAfterSibling = false;
3999f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
4009f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            break;
4019f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          }
4029f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
4039f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          found1 = true;
4049f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        }
4059f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        else if (child2 == child || isNodeTheSame(child2, child))
4069f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        {
4079f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          if (found1)
4089f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          {
4099f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            isNodeAfterSibling = true;
4109f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
4119f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            break;
4129f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          }
4139f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
4149f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          found2 = true;
4159f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        }
4169f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      }
4179f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
4189f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    else
4199f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
4209f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                // TODO: Check performance of alternate solution:
4219f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                // There are two choices here: Count from the start of
4229f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                // the document until we find one or the other, or count
4239f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                // from one until we find or fail to find the other.
4249f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                // Either can wind up scanning all the siblings in the worst
4259f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                // case, which on a wide document can be a lot of work but
4269f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                // is more typically is a short list.
4279f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                // Scanning from the start involves two tests per iteration,
4289f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                // but it isn't clear that scanning from the middle doesn't
4299f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                // yield more iterations on average.
4309f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                // We should run some testcases.
4319f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      Node child = parent.getFirstChild();
4329f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      boolean found1 = false, found2 = false;
4339f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
4349f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      while (null != child)
4359f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      {
4369f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
4379f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // Node child = children.item(i);
4389f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        if (child1 == child || isNodeTheSame(child1, child))
4399f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        {
4409f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          if (found2)
4419f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          {
4429f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            isNodeAfterSibling = false;
4439f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
4449f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            break;
4459f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          }
4469f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
4479f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          found1 = true;
4489f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        }
4499f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        else if (child2 == child || isNodeTheSame(child2, child))
4509f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        {
4519f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          if (found1)
4529f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          {
4539f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            isNodeAfterSibling = true;
4549f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
4559f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            break;
4569f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          }
4579f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
4589f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          found2 = true;
4599f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        }
4609f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
4619f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        child = child.getNextSibling();
4629f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      }
4639f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
4649f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
4659f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return isNodeAfterSibling;
4669f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }  // end isNodeAfterSibling(Node parent, Node child1, Node child2)
4679f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
4689f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  //==========================================================
4699f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  // SECTION: Namespace resolution
4709f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  //==========================================================
4719f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
4729f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
4739f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Get the depth level of this node in the tree (equals 1 for
4749f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * a parentless node).
4759f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
4769f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param n Node to be examined.
4779f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return the number of ancestors, plus one
4789f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @xsl.usage internal
4799f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
4809f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public short getLevel(Node n)
4819f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
4829f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
4839f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    short level = 1;
4849f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
4859f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    while (null != (n = getParentOfNode(n)))
4869f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
4879f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      level++;
4889f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
4899f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
4909f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return level;
4919f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
4929f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
4939f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
4949f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Given an XML Namespace prefix and a context in which the prefix
4959f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * is to be evaluated, return the Namespace Name this prefix was
4969f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * bound to. Note that DOM Level 3 is expected to provide a version of
4979f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * this which deals with the DOM's "early binding" behavior.
4989f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
4999f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Default handling:
5009f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
5019f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param prefix String containing namespace prefix to be resolved,
5029f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * without the ':' which separates it from the localname when used
5039f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * in a Node Name. The empty sting signifies the default namespace
5049f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * at this point in the document.
5059f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param namespaceContext Element which provides context for resolution.
5069f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * (We could extend this to work for other nodes by first seeking their
5079f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * nearest Element ancestor.)
5089f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
5099f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return a String containing the Namespace URI which this prefix
5109f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * represents in the specified context.
5119f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
5129f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public String getNamespaceForPrefix(String prefix, Element namespaceContext)
5139f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
5149f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
5159f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    int type;
5169f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    Node parent = namespaceContext;
5179f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    String namespace = null;
5189f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
5199f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    if (prefix.equals("xml"))
5209f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
5219f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      namespace = QName.S_XMLNAMESPACEURI; // Hardcoded, per Namespace spec
5229f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
5239f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        else if(prefix.equals("xmlns"))
5249f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
5259f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // Hardcoded in the DOM spec, expected to be adopted by
5269f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // Namespace spec. NOTE: Namespace declarations _must_ use
5279f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // the xmlns: prefix; other prefixes declared as belonging
5289f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // to this namespace will not be recognized and should
5299f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // probably be rejected by parsers as erroneous declarations.
5309f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      namespace = "http://www.w3.org/2000/xmlns/";
5319f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
5329f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    else
5339f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
5349f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // Attribute name for this prefix's declaration
5359f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          String declname=(prefix=="")
5369f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                        ? "xmlns"
5379f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                        : "xmlns:"+prefix;
5389f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
5399f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // Scan until we run out of Elements or have resolved the namespace
5409f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      while ((null != parent) && (null == namespace)
5419f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson             && (((type = parent.getNodeType()) == Node.ELEMENT_NODE)
5429f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                 || (type == Node.ENTITY_REFERENCE_NODE)))
5439f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      {
5449f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        if (type == Node.ELEMENT_NODE)
5459f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        {
5469f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
5479f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                        // Look for the appropriate Namespace Declaration attribute,
5489f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                        // either "xmlns:prefix" or (if prefix is "") "xmlns".
5499f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                        // TODO: This does not handle "implicit declarations"
5509f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                        // which may be created when the DOM is edited. DOM Level
5519f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                        // 3 will define how those should be interpreted. But
5529f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                        // this issue won't arise in freshly-parsed DOMs.
5539f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
5549f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                // NOTE: declname is set earlier, outside the loop.
5559f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                        Attr attr=((Element)parent).getAttributeNode(declname);
5569f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                        if(attr!=null)
5579f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                        {
5589f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                namespace = attr.getNodeValue();
5599f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                break;
5609f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                        }
5619f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                }
5629f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
5639f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        parent = getParentOfNode(parent);
5649f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      }
5659f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
5669f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
5679f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return namespace;
5689f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
5699f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
5709f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
5719f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * An experiment for the moment.
5729f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
5739f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  Hashtable m_NSInfos = new Hashtable();
5749f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
5759f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /** Object to put into the m_NSInfos table that tells that a node has not been
5769f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *  processed, but has xmlns namespace decls.  */
5779f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  protected static final NSInfo m_NSInfoUnProcWithXMLNS = new NSInfo(false,
5789f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                                                            true);
5799f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
5809f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /** Object to put into the m_NSInfos table that tells that a node has not been
5819f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *  processed, but has no xmlns namespace decls.  */
5829f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  protected static final NSInfo m_NSInfoUnProcWithoutXMLNS = new NSInfo(false,
5839f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                                                               false);
5849f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
5859f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /** Object to put into the m_NSInfos table that tells that a node has not been
5869f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *  processed, and has no xmlns namespace decls, and has no ancestor decls.  */
5879f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  protected static final NSInfo m_NSInfoUnProcNoAncestorXMLNS =
5889f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    new NSInfo(false, false, NSInfo.ANCESTORNOXMLNS);
5899f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
5909f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /** Object to put into the m_NSInfos table that tells that a node has been
5919f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *  processed, and has xmlns namespace decls.  */
5929f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  protected static final NSInfo m_NSInfoNullWithXMLNS = new NSInfo(true,
5939f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                                                          true);
5949f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
5959f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /** Object to put into the m_NSInfos table that tells that a node has been
5969f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *  processed, and has no xmlns namespace decls.  */
5979f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  protected static final NSInfo m_NSInfoNullWithoutXMLNS = new NSInfo(true,
5989f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                                                             false);
5999f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
6009f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /** Object to put into the m_NSInfos table that tells that a node has been
6019f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *  processed, and has no xmlns namespace decls. and has no ancestor decls.  */
6029f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  protected static final NSInfo m_NSInfoNullNoAncestorXMLNS =
6039f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    new NSInfo(true, false, NSInfo.ANCESTORNOXMLNS);
6049f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
6059f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /** Vector of node (odd indexes) and NSInfos (even indexes) that tell if
6069f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *  the given node is a candidate for ancestor namespace processing.  */
6079f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  protected Vector m_candidateNoAncestorXMLNS = new Vector();
6089f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
6099f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
6109f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Returns the namespace of the given node. Differs from simply getting
6119f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * the node's prefix and using getNamespaceForPrefix in that it attempts
6129f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * to cache some of the data in NSINFO objects, to avoid repeated lookup.
6139f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * TODO: Should we consider moving that logic into getNamespaceForPrefix?
6149f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
6159f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param n Node to be examined.
6169f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
6179f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return String containing the Namespace Name (uri) for this node.
6189f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Note that this is undefined for any nodes other than Elements and
6199f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Attributes.
6209f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
6219f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public String getNamespaceOfNode(Node n)
6229f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
6239f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
6249f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    String namespaceOfPrefix;
6259f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    boolean hasProcessedNS;
6269f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    NSInfo nsInfo;
6279f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    short ntype = n.getNodeType();
6289f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
6299f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    if (Node.ATTRIBUTE_NODE != ntype)
6309f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
6319f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      Object nsObj = m_NSInfos.get(n);  // return value
6329f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
6339f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      nsInfo = (nsObj == null) ? null : (NSInfo) nsObj;
6349f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      hasProcessedNS = (nsInfo == null) ? false : nsInfo.m_hasProcessedNS;
6359f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
6369f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    else
6379f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
6389f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      hasProcessedNS = false;
6399f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      nsInfo = null;
6409f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
6419f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
6429f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    if (hasProcessedNS)
6439f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
6449f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      namespaceOfPrefix = nsInfo.m_namespace;
6459f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
6469f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    else
6479f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
6489f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      namespaceOfPrefix = null;
6499f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
6509f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      String nodeName = n.getNodeName();
6519f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      int indexOfNSSep = nodeName.indexOf(':');
6529f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      String prefix;
6539f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
6549f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      if (Node.ATTRIBUTE_NODE == ntype)
6559f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      {
6569f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        if (indexOfNSSep > 0)
6579f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        {
6589f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          prefix = nodeName.substring(0, indexOfNSSep);
6599f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        }
6609f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        else
6619f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        {
6629f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
6639f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // Attributes don't use the default namespace, so if
6649f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // there isn't a prefix, we're done.
6659f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          return namespaceOfPrefix;
6669f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        }
6679f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      }
6689f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      else
6699f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      {
6709f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        prefix = (indexOfNSSep >= 0)
6719f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                 ? nodeName.substring(0, indexOfNSSep) : "";
6729f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      }
6739f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
6749f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      boolean ancestorsHaveXMLNS = false;
6759f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      boolean nHasXMLNS = false;
6769f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
6779f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      if (prefix.equals("xml"))
6789f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      {
6799f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        namespaceOfPrefix = QName.S_XMLNAMESPACEURI;
6809f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      }
6819f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      else
6829f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      {
6839f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        int parentType;
6849f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        Node parent = n;
6859f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
6869f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        while ((null != parent) && (null == namespaceOfPrefix))
6879f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        {
6889f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          if ((null != nsInfo)
6899f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                  && (nsInfo.m_ancestorHasXMLNSAttrs
6909f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                      == NSInfo.ANCESTORNOXMLNS))
6919f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          {
6929f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            break;
6939f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          }
6949f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
6959f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          parentType = parent.getNodeType();
6969f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
6979f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          if ((null == nsInfo) || nsInfo.m_hasXMLNSAttrs)
6989f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          {
6999f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            boolean elementHasXMLNS = false;
7009f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
7019f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            if (parentType == Node.ELEMENT_NODE)
7029f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            {
7039f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson              NamedNodeMap nnm = parent.getAttributes();
7049f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
7059f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson              for (int i = 0; i < nnm.getLength(); i++)
7069f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson              {
7079f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                Node attr = nnm.item(i);
7089f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                String aname = attr.getNodeName();
7099f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
7109f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                if (aname.charAt(0) == 'x')
7119f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                {
7129f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                  boolean isPrefix = aname.startsWith("xmlns:");
7139f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
7149f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                  if (aname.equals("xmlns") || isPrefix)
7159f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                  {
7169f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                    if (n == parent)
7179f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                      nHasXMLNS = true;
7189f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
7199f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                    elementHasXMLNS = true;
7209f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                    ancestorsHaveXMLNS = true;
7219f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
7229f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                    String p = isPrefix ? aname.substring(6) : "";
7239f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
7249f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                    if (p.equals(prefix))
7259f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                    {
7269f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                      namespaceOfPrefix = attr.getNodeValue();
7279f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
7289f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                      break;
7299f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                    }
7309f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                  }
7319f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                }
7329f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson              }
7339f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            }
7349f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
7359f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            if ((Node.ATTRIBUTE_NODE != parentType) && (null == nsInfo)
7369f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                    && (n != parent))
7379f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            {
7389f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson              nsInfo = elementHasXMLNS
7399f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                       ? m_NSInfoUnProcWithXMLNS : m_NSInfoUnProcWithoutXMLNS;
7409f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
7419f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson              m_NSInfos.put(parent, nsInfo);
7429f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            }
7439f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          }
7449f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
7459f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          if (Node.ATTRIBUTE_NODE == parentType)
7469f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          {
7479f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            parent = getParentOfNode(parent);
7489f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          }
7499f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          else
7509f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          {
7519f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            m_candidateNoAncestorXMLNS.addElement(parent);
7529f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            m_candidateNoAncestorXMLNS.addElement(nsInfo);
7539f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
7549f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            parent = parent.getParentNode();
7559f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          }
7569f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
7579f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          if (null != parent)
7589f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          {
7599f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            Object nsObj = m_NSInfos.get(parent);  // return value
7609f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
7619f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            nsInfo = (nsObj == null) ? null : (NSInfo) nsObj;
7629f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          }
7639f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        }
7649f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
7659f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        int nCandidates = m_candidateNoAncestorXMLNS.size();
7669f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
7679f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        if (nCandidates > 0)
7689f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        {
7699f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          if ((false == ancestorsHaveXMLNS) && (null == parent))
7709f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          {
7719f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            for (int i = 0; i < nCandidates; i += 2)
7729f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            {
7739f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson              Object candidateInfo = m_candidateNoAncestorXMLNS.elementAt(i
7749f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                                       + 1);
7759f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
7769f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson              if (candidateInfo == m_NSInfoUnProcWithoutXMLNS)
7779f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson              {
7789f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                m_NSInfos.put(m_candidateNoAncestorXMLNS.elementAt(i),
7799f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                              m_NSInfoUnProcNoAncestorXMLNS);
7809f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson              }
7819f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson              else if (candidateInfo == m_NSInfoNullWithoutXMLNS)
7829f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson              {
7839f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                m_NSInfos.put(m_candidateNoAncestorXMLNS.elementAt(i),
7849f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                              m_NSInfoNullNoAncestorXMLNS);
7859f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson              }
7869f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            }
7879f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          }
7889f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
7899f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          m_candidateNoAncestorXMLNS.removeAllElements();
7909f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        }
7919f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      }
7929f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
7939f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      if (Node.ATTRIBUTE_NODE != ntype)
7949f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      {
7959f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        if (null == namespaceOfPrefix)
7969f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        {
7979f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          if (ancestorsHaveXMLNS)
7989f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          {
7999f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            if (nHasXMLNS)
8009f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson              m_NSInfos.put(n, m_NSInfoNullWithXMLNS);
8019f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            else
8029f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson              m_NSInfos.put(n, m_NSInfoNullWithoutXMLNS);
8039f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          }
8049f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          else
8059f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          {
8069f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            m_NSInfos.put(n, m_NSInfoNullNoAncestorXMLNS);
8079f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          }
8089f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        }
8099f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        else
8109f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        {
8119f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          m_NSInfos.put(n, new NSInfo(namespaceOfPrefix, nHasXMLNS));
8129f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        }
8139f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      }
8149f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
8159f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
8169f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return namespaceOfPrefix;
8179f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
8189f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
8199f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
8209f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Returns the local name of the given node. If the node's name begins
8219f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * with a namespace prefix, this is the part after the colon; otherwise
8229f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * it's the full node name.
8239f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
8249f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param n the node to be examined.
8259f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
8269f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return String containing the Local Name
8279f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
8289f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public String getLocalNameOfNode(Node n)
8299f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
8309f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
8319f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    String qname = n.getNodeName();
8329f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    int index = qname.indexOf(':');
8339f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
8349f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return (index < 0) ? qname : qname.substring(index + 1);
8359f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
8369f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
8379f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
8389f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Returns the element name with the namespace prefix (if any) replaced
8399f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * by the Namespace URI it was bound to. This is not a standard
8409f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * representation of a node name, but it allows convenient
8419f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * single-string comparison of the "universal" names of two nodes.
8429f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
8439f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param elem Element to be examined.
8449f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
8459f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return String in the form "namespaceURI:localname" if the node
8469f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * belongs to a namespace, or simply "localname" if it doesn't.
8479f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @see #getExpandedAttributeName
8489f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
8499f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public String getExpandedElementName(Element elem)
8509f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
8519f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
8529f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    String namespace = getNamespaceOfNode(elem);
8539f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
8549f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return (null != namespace)
8559f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson           ? namespace + ":" + getLocalNameOfNode(elem)
8569f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson           : getLocalNameOfNode(elem);
8579f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
8589f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
8599f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
8609f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Returns the attribute name with the namespace prefix (if any) replaced
8619f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * by the Namespace URI it was bound to. This is not a standard
8629f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * representation of a node name, but it allows convenient
8639f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * single-string comparison of the "universal" names of two nodes.
8649f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
8659f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param attr Attr to be examined
8669f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
8679f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return String in the form "namespaceURI:localname" if the node
8689f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * belongs to a namespace, or simply "localname" if it doesn't.
8699f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @see #getExpandedElementName
8709f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
8719f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public String getExpandedAttributeName(Attr attr)
8729f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
8739f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
8749f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    String namespace = getNamespaceOfNode(attr);
8759f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
8769f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return (null != namespace)
8779f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson           ? namespace + ":" + getLocalNameOfNode(attr)
8789f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson           : getLocalNameOfNode(attr);
8799f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
8809f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
8819f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  //==========================================================
8829f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  // SECTION: DOM Helper Functions
8839f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  //==========================================================
8849f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
8859f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
8869f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Tell if the node is ignorable whitespace. Note that this can
8879f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * be determined only in the context of a DTD or other Schema,
8889f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * and that DOM Level 2 has nostandardized DOM API which can
8899f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * return that information.
8909f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @deprecated
8919f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
8929f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param node Node to be examined
8939f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
8949f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return CURRENTLY HARDCODED TO FALSE, but should return true if
8959f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * and only if the node is of type Text, contains only whitespace,
8969f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * and does not appear as part of the #PCDATA content of an element.
8979f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * (Note that determining this last may require allowing for
8989f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Entity References.)
8999f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
9009f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public boolean isIgnorableWhitespace(Text node)
9019f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
9029f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
9039f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    boolean isIgnorable = false;  // return value
9049f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
9059f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    // TODO: I can probably do something to figure out if this
9069f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    // space is ignorable from just the information in
9079f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    // the DOM tree.
9089f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // -- You need to be able to distinguish whitespace
9099f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // that is #PCDATA from whitespace that isn't.  That requires
9109f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // DTD support, which won't be standardized until DOM Level 3.
9119f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return isIgnorable;
9129f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
9139f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
9149f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
9159f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Get the first unparented node in the ancestor chain.
9169f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @deprecated
9179f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
9189f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param node Starting node, to specify which chain to chase
9199f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
9209f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return the topmost ancestor.
9219f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
9229f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public Node getRoot(Node node)
9239f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
9249f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
9259f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    Node root = null;
9269f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
9279f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    while (node != null)
9289f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
9299f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      root = node;
9309f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      node = getParentOfNode(node);
9319f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
9329f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
9339f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return root;
9349f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
9359f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
9369f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
9379f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Get the root node of the document tree, regardless of
9389f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * whether or not the node passed in is a document node.
9399f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * <p>
9409f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * TODO: This doesn't handle DocumentFragments or "orphaned" subtrees
9419f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * -- it's currently returning ownerDocument even when the tree is
9429f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * not actually part of the main Document tree. We should either
9439f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * rewrite the description to say that it finds the Document node,
9449f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * or change the code to walk up the ancestor chain.
9459f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
9469f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
9479f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param n Node to be examined
9489f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
9499f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return the Document node. Note that this is not the correct answer
9509f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * if n was (or was a child of) a DocumentFragment or an orphaned node,
9519f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * as can arise if the DOM has been edited rather than being generated
9529f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * by a parser.
9539f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
9549f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public Node getRootNode(Node n)
9559f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
9569f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    int nt = n.getNodeType();
9579f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return ( (Node.DOCUMENT_NODE == nt) || (Node.DOCUMENT_FRAGMENT_NODE == nt) )
9589f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson           ? n : n.getOwnerDocument();
9599f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
9609f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
9619f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
9629f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Test whether the given node is a namespace decl node. In DOM Level 2
9639f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * this can be done in a namespace-aware manner, but in Level 1 DOMs
9649f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * it has to be done by testing the node name.
9659f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
9669f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param n Node to be examined.
9679f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
9689f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return boolean -- true iff the node is an Attr whose name is
9699f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * "xmlns" or has the "xmlns:" prefix.
9709f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
9719f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public boolean isNamespaceNode(Node n)
9729f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
9739f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
9749f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    if (Node.ATTRIBUTE_NODE == n.getNodeType())
9759f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
9769f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      String attrName = n.getNodeName();
9779f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
9789f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      return (attrName.startsWith("xmlns:") || attrName.equals("xmlns"));
9799f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
9809f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
9819f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return false;
9829f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
9839f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
9849f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
9859f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Obtain the XPath-model parent of a DOM node -- ownerElement for Attrs,
9869f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * parent for other nodes.
9879f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * <p>
9889f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Background: The DOM believes that you must be your Parent's
9899f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Child, and thus Attrs don't have parents. XPath said that Attrs
9909f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * do have their owning Element as their parent. This function
9919f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * bridges the difference, either by using the DOM Level 2 ownerElement
9929f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * function or by using a "silly and expensive function" in Level 1
9939f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * DOMs.
9949f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * <p>
9959f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * (There's some discussion of future DOMs generalizing ownerElement
9969f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * into ownerNode and making it work on all types of nodes. This
9979f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * still wouldn't help the users of Level 1 or Level 2 DOMs)
9989f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * <p>
9999f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
10009f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param node Node whose XPath parent we want to obtain
10019f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
10029f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return the parent of the node, or the ownerElement if it's an
10039f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Attr node, or null if the node is an orphan.
10049f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
10059f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @throws RuntimeException if the Document has no root element.
10069f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * This can't arise if the Document was created
10079f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * via the DOM Level 2 factory methods, but is possible if other
10089f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * mechanisms were used to obtain it
10099f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
10109f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public static Node getParentOfNode(Node node) throws RuntimeException
10119f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
10129f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    Node parent;
10139f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    short nodeType = node.getNodeType();
10149f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
10159f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    if (Node.ATTRIBUTE_NODE == nodeType)
10169f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
10179f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      Document doc = node.getOwnerDocument();
10189f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          /*
10199f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      TBD:
10209f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      if(null == doc)
10219f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      {
10229f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_CHILD_HAS_NO_OWNER_DOCUMENT, null));//"Attribute child does not have an owner document!");
10239f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      }
10249f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      */
10259f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
10269f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // Given how expensive the tree walk may be, we should first ask
10279f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // whether this DOM can answer the question for us. The additional
10289f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // test does slow down Level 1 DOMs slightly. DOMHelper2, which
10299f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // is currently specialized for Xerces, assumes it can use the
10309f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // Level 2 solution. We might want to have an intermediate stage,
10319f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // which would assume DOM Level 2 but not assume Xerces.
10329f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          //
10339f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // (Shouldn't have to check whether impl is null in a compliant DOM,
10349f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // but let's be paranoid for a moment...)
10359f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          DOMImplementation impl=doc.getImplementation();
10369f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          if(impl!=null && impl.hasFeature("Core","2.0"))
10379f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          {
10389f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                  parent=((Attr)node).getOwnerElement();
10399f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                  return parent;
10409f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          }
10419f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
10429f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // DOM Level 1 solution, as fallback. Hugely expensive.
10439f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
10449f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      Element rootElem = doc.getDocumentElement();
10459f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
10469f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      if (null == rootElem)
10479f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      {
10489f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        throw new RuntimeException(
10499f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          XMLMessages.createXMLMessage(
10509f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            XMLErrorResources.ER_CHILD_HAS_NO_OWNER_DOCUMENT_ELEMENT,
10519f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            null));  //"Attribute child does not have an owner document element!");
10529f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      }
10539f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
10549f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      parent = locateAttrParent(rootElem, node);
10559f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
10569f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        }
10579f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    else
10589f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
10599f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      parent = node.getParentNode();
10609f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
10619f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // if((Node.DOCUMENT_NODE != nodeType) && (null == parent))
10629f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // {
10639f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      //   throw new RuntimeException("Child does not have parent!");
10649f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // }
10659f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
10669f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
10679f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return parent;
10689f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
10699f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
10709f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
10719f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Given an ID, return the element. This can work only if the document
10729f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * is interpreted in the context of a DTD or Schema, since otherwise
10739f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * we don't know which attributes are or aren't IDs.
10749f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * <p>
10759f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Note that DOM Level 1 had no ability to retrieve this information.
10769f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * DOM Level 2 introduced it but does not promise that it will be
10779f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * supported in all DOMs; those which can't support it will always
10789f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * return null.
10799f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * <p>
10809f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * TODO: getElementByID is currently unimplemented. Support DOM Level 2?
10819f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
10829f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param id The unique identifier to be searched for.
10839f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param doc The document to search within.
10849f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return CURRENTLY HARDCODED TO NULL, but it should be:
10859f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * The node which has this unique identifier, or null if there
10869f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * is no such node or this DOM can't reliably recognize it.
10879f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
10889f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public Element getElementByID(String id, Document doc)
10899f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
10909f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return null;
10919f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
10929f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
10939f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
10949f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * The getUnparsedEntityURI function returns the URI of the unparsed
10959f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * entity with the specified name in the same document as the context
10969f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * node (see [3.3 Unparsed Entities]). It returns the empty string if
10979f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * there is no such entity.
10989f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * <p>
10999f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * XML processors may choose to use the System Identifier (if one
11009f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * is provided) to resolve the entity, rather than the URI in the
11019f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Public Identifier. The details are dependent on the processor, and
11029f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * we would have to support some form of plug-in resolver to handle
11039f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * this properly. Currently, we simply return the System Identifier if
11049f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * present, and hope that it a usable URI or that our caller can
11059f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * map it to one.
11069f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * TODO: Resolve Public Identifiers... or consider changing function name.
11079f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * <p>
11089f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * If we find a relative URI
11099f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * reference, XML expects it to be resolved in terms of the base URI
11109f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * of the document. The DOM doesn't do that for us, and it isn't
11119f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * entirely clear whether that should be done here; currently that's
11129f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * pushed up to a higher levelof our application. (Note that DOM Level
11139f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * 1 didn't store the document's base URI.)
11149f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * TODO: Consider resolving Relative URIs.
11159f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * <p>
11169f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * (The DOM's statement that "An XML processor may choose to
11179f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * completely expand entities before the structure model is passed
11189f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * to the DOM" refers only to parsed entities, not unparsed, and hence
11199f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * doesn't affect this function.)
11209f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
11219f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param name A string containing the Entity Name of the unparsed
11229f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * entity.
11239f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param doc Document node for the document to be searched.
11249f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
11259f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return String containing the URI of the Unparsed Entity, or an
11269f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * empty string if no such entity exists.
11279f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
11289f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public String getUnparsedEntityURI(String name, Document doc)
11299f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
11309f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
11319f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    String url = "";
11329f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    DocumentType doctype = doc.getDoctype();
11339f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
11349f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    if (null != doctype)
11359f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
11369f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      NamedNodeMap entities = doctype.getEntities();
11379f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      if(null == entities)
11389f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        return url;
11399f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      Entity entity = (Entity) entities.getNamedItem(name);
11409f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      if(null == entity)
11419f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        return url;
11429f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
11439f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      String notationName = entity.getNotationName();
11449f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
11459f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      if (null != notationName)  // then it's unparsed
11469f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      {
11479f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // The draft says: "The XSLT processor may use the public
11489f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // identifier to generate a URI for the entity instead of the URI
11499f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // specified in the system identifier. If the XSLT processor does
11509f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // not use the public identifier to generate the URI, it must use
11519f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // the system identifier; if the system identifier is a relative
11529f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // URI, it must be resolved into an absolute URI using the URI of
11539f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // the resource containing the entity declaration as the base
11549f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // URI [RFC2396]."
11559f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // So I'm falling a bit short here.
11569f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        url = entity.getSystemId();
11579f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
11589f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        if (null == url)
11599f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        {
11609f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          url = entity.getPublicId();
11619f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        }
11629f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        else
11639f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        {
11649f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // This should be resolved to an absolute URL, but that's hard
11659f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          // to do from here.
11669f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        }
11679f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      }
11689f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
11699f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
11709f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return url;
11719f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
11729f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
11739f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
11749f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Support for getParentOfNode; walks a DOM tree until it finds
11759f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * the Element which owns the Attr. This is hugely expensive, and
11769f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * if at all possible you should use the DOM Level 2 Attr.ownerElement()
11779f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * method instead.
11789f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *  <p>
11799f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * The DOM Level 1 developers expected that folks would keep track
11809f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * of the last Element they'd seen and could recover the info from
11819f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * that source. Obviously that doesn't work very well if the only
11829f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * information you've been presented with is the Attr. The DOM Level 2
11839f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * getOwnerElement() method fixes that, but only for Level 2 and
11849f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * later DOMs.
11859f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
11869f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param elem Element whose subtree is to be searched for this Attr
11879f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param attr Attr whose owner is to be located.
11889f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
11899f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return the first Element whose attribute list includes the provided
11909f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * attr. In modern DOMs, this will also be the only such Element. (Early
11919f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * DOMs had some hope that Attrs might be sharable, but this idea has
11929f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * been abandoned.)
11939f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
11949f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  private static Node locateAttrParent(Element elem, Node attr)
11959f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
11969f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
11979f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    Node parent = null;
11989f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
11999f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // This should only be called for Level 1 DOMs, so we don't have to
12009f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // worry about namespace issues. In later levels, it's possible
12019f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // for a DOM to have two Attrs with the same NodeName but
12029f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // different namespaces, and we'd need to get getAttributeNodeNS...
12039f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        // but later levels also have Attr.getOwnerElement.
12049f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        Attr check=elem.getAttributeNode(attr.getNodeName());
12059f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        if(check==attr)
12069f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson                parent = elem;
12079f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
12089f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    if (null == parent)
12099f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
12109f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      for (Node node = elem.getFirstChild(); null != node;
12119f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson              node = node.getNextSibling())
12129f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      {
12139f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        if (Node.ELEMENT_NODE == node.getNodeType())
12149f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        {
12159f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          parent = locateAttrParent((Element) node, attr);
12169f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
12179f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson          if (null != parent)
12189f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson            break;
12199f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        }
12209f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      }
12219f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
12229f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
12239f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return parent;
12249f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
12259f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
12269f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
12279f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * The factory object used for creating nodes
12289f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * in the result tree.
12299f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
12309f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  protected Document m_DOMFactory = null;
12319f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
12329f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
12339f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Store the factory object required to create DOM nodes
12349f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * in the result tree. In fact, that's just the result tree's
12359f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Document node...
12369f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
12379f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param domFactory The DOM Document Node within whose context
12389f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * the result tree will be built.
12399f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
12409f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public void setDOMFactory(Document domFactory)
12419f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
12429f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    this.m_DOMFactory = domFactory;
12439f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
12449f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
12459f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
12469f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Retrieve the factory object required to create DOM nodes
12479f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * in the result tree.
12489f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
12499f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return The result tree's DOM Document Node.
12509f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
12519f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public Document getDOMFactory()
12529f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
12539f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
12549f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    if (null == this.m_DOMFactory)
12559f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
12569f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      this.m_DOMFactory = createDocument();
12579f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
12589f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
12599f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return this.m_DOMFactory;
12609f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
12619f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
12629f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
12639f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Get the textual contents of the node. See
12649f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * getNodeData(Node,FastStringBuffer) for discussion of how
12659f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * whitespace nodes are handled.
12669f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
12679f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param node DOM Node to be examined
12689f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @return String containing a concatenation of all the
12699f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * textual content within that node.
12709f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @see #getNodeData(Node,FastStringBuffer)
12719f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
12729f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
12739f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public static String getNodeData(Node node)
12749f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
12759f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
12769f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    FastStringBuffer buf = StringBufferPool.get();
12779f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    String s;
12789f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
12799f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    try
12809f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
12819f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      getNodeData(node, buf);
12829f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
12839f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      s = (buf.length() > 0) ? buf.toString() : "";
12849f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
12859f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    finally
12869f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
12879f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      StringBufferPool.free(buf);
12889f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
12899f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
12909f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    return s;
12919f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
12929f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
12939f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  /**
12949f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Retrieve the text content of a DOM subtree, appending it into a
12959f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * user-supplied FastStringBuffer object. Note that attributes are
12969f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * not considered part of the content of an element.
12979f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * <p>
12989f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * There are open questions regarding whitespace stripping.
12999f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * Currently we make no special effort in that regard, since the standard
13009f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * DOM doesn't yet provide DTD-based information to distinguish
13019f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * whitespace-in-element-context from genuine #PCDATA. Note that we
13029f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * should probably also consider xml:space if/when we address this.
13039f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * DOM Level 3 may solve the problem for us.
13049f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   *
13059f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param node Node whose subtree is to be walked, gathering the
13069f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * contents of all Text or CDATASection nodes.
13079f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * @param buf FastStringBuffer into which the contents of the text
13089f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   * nodes are to be concatenated.
13099f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson   */
13109f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  public static void getNodeData(Node node, FastStringBuffer buf)
13119f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  {
13129f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson
13139f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    switch (node.getNodeType())
13149f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
13159f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    case Node.DOCUMENT_FRAGMENT_NODE :
13169f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    case Node.DOCUMENT_NODE :
13179f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    case Node.ELEMENT_NODE :
13189f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    {
13199f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      for (Node child = node.getFirstChild(); null != child;
13209f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson              child = child.getNextSibling())
13219f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      {
13229f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson        getNodeData(child, buf);
13239f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      }
13249f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
13259f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    break;
13269f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    case Node.TEXT_NODE :
13279f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    case Node.CDATA_SECTION_NODE :
13289f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      buf.append(node.getNodeValue());
13299f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      break;
13309f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    case Node.ATTRIBUTE_NODE :
13319f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      buf.append(node.getNodeValue());
13329f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      break;
13339f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    case Node.PROCESSING_INSTRUCTION_NODE :
13349f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // warning(XPATHErrorResources.WG_PARSING_AND_PREPARING);
13359f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      break;
13369f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    default :
13379f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      // ignore
13389f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson      break;
13399f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson    }
13409f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson  }
13419f8118474e9513f7a5b7d2a05e4a0fb15d1a6569Jesse Wilson}
1342