domhelper.java

来自「JAVA 所有包」· Java 代码 · 共 1,333 行 · 第 1/3 页

JAVA
1,333
字号
/* * Copyright 1999-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *     http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *//* * $Id: DOMHelper.java,v 1.2.4.1 2005/09/15 08:15:40 suresh_emailid Exp $ */package com.sun.org.apache.xml.internal.utils;import java.util.Hashtable;import java.util.Vector;import javax.xml.XMLConstants;import javax.xml.parsers.DocumentBuilder;import javax.xml.parsers.DocumentBuilderFactory;import javax.xml.parsers.ParserConfigurationException;import com.sun.org.apache.xml.internal.dtm.ref.DTMNodeProxy;import com.sun.org.apache.xml.internal.res.XMLErrorResources;import com.sun.org.apache.xml.internal.res.XMLMessages;import org.w3c.dom.Attr;import org.w3c.dom.DOMImplementation;import org.w3c.dom.Document;import org.w3c.dom.DocumentType;import org.w3c.dom.Element;import org.w3c.dom.Entity;import org.w3c.dom.NamedNodeMap;import org.w3c.dom.Node;import org.w3c.dom.Text;/** * @deprecated Since the introduction of the DTM, this class will be removed. * This class provides a front-end to DOM implementations, providing * a number of utility functions that either aren't yet standardized * by the DOM spec or that are defined in optional DOM modules and * hence may not be present in all DOMs. */public class DOMHelper{  /**   * DOM Level 1 did not have a standard mechanism for creating a new   * Document object. This function provides a DOM-implementation-independent   * abstraction for that for that concept. It's typically used when    * outputting a new DOM as the result of an operation.   * <p>   * TODO: This isn't directly compatable with DOM Level 2.    * The Level 2 createDocument call also creates the root    * element, and thus requires that you know what that element will be   * before creating the Document. We should think about whether we want   * to change this code, and the callers, so we can use the DOM's own    * method. (It's also possible that DOM Level 3 may relax this   * sequence, but you may give up some intelligence in the DOM by   * doing so; the intent was that knowing the document type and root   * element might let the DOM automatically switch to a specialized   * subclass for particular kinds of documents.)   *   * @param isSecureProcessing state of the secure processing feature.   * @return The newly created DOM Document object, with no children, or   * null if we can't find a DOM implementation that permits creating   * new empty Documents.   */  public static Document createDocument(boolean isSecureProcessing)  {    try    {      // Use an implementation of the JAVA API for XML Parsing 1.0 to      // create a DOM Document node to contain the result.      DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();      dfactory.setNamespaceAware(true);      dfactory.setValidating(true);            if (isSecureProcessing)      {        try        {          dfactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);        }        catch (ParserConfigurationException pce) {}      }            DocumentBuilder docBuilder = dfactory.newDocumentBuilder();      Document outNode = docBuilder.newDocument();      return outNode;    }    catch (ParserConfigurationException pce)    {      throw new RuntimeException(        XMLMessages.createXMLMessage(          XMLErrorResources.ER_CREATEDOCUMENT_NOT_SUPPORTED, null));  //"createDocument() not supported in XPathContext!");      // return null;    }  }  /**   * DOM Level 1 did not have a standard mechanism for creating a new   * Document object. This function provides a DOM-implementation-independent   * abstraction for that for that concept. It's typically used when    * outputting a new DOM as the result of an operation.   *   * @return The newly created DOM Document object, with no children, or   * null if we can't find a DOM implementation that permits creating   * new empty Documents.   */  public static Document createDocument()  {    return createDocument(false);  }    /**   * Tells, through the combination of the default-space attribute   * on xsl:stylesheet, xsl:strip-space, xsl:preserve-space, and the   * xml:space attribute, whether or not extra whitespace should be stripped   * from the node.  Literal elements from template elements should   * <em>not</em> be tested with this function.   * @param textNode A text node from the source tree.   * @return true if the text node should be stripped of extra whitespace.   *   * @throws javax.xml.transform.TransformerException   * @xsl.usage advanced   */  public boolean shouldStripSourceNode(Node textNode)          throws javax.xml.transform.TransformerException  {    // return (null == m_envSupport) ? false : m_envSupport.shouldStripSourceNode(textNode);    return false;  }  /**   * Supports the XPath function GenerateID by returning a unique   * identifier string for any given DOM Node.   * <p>   * Warning: The base implementation uses the Node object's hashCode(),   * which is NOT guaranteed to be unique. If that method hasn't been   * overridden in this DOM ipmlementation, most Java implementions will   * derive it from the object's address and should be OK... but if   * your DOM uses a different definition of hashCode (eg hashing the   * contents of the subtree), or if your DOM may have multiple objects   * that represent a single Node in the data structure (eg via proxying),   * you may need to find another way to assign a unique identifier.   * <p>   * Also, be aware that if nodes are destroyed and recreated, there is   * an open issue regarding whether an ID may be reused. Currently   * we're assuming that the input document is stable for the duration   * of the XPath/XSLT operation, so this shouldn't arise in this context.   * <p>   * (DOM Level 3 is investigating providing a unique node "key", but   * that won't help Level 1 and Level 2 implementations.)   *   * @param node whose identifier you want to obtain   *   * @return a string which should be different for every Node object.   */  public String getUniqueID(Node node)  {    return "N" + Integer.toHexString(node.hashCode()).toUpperCase();  }  /**   * Figure out whether node2 should be considered as being later   * in the document than node1, in Document Order as defined   * by the XPath model. This may not agree with the ordering defined   * by other XML applications.   * <p>   * There are some cases where ordering isn't defined, and neither are   * the results of this function -- though we'll generally return true.   *    * TODO: Make sure this does the right thing with attribute nodes!!!   *   * @param node1 DOM Node to perform position comparison on.   * @param node2 DOM Node to perform position comparison on .   *    * @return false if node2 comes before node1, otherwise return true.   * You can think of this as    * <code>(node1.documentOrderPosition &lt;= node2.documentOrderPosition)</code>.   */  public static boolean isNodeAfter(Node node1, Node node2)  {    if (node1 == node2 || isNodeTheSame(node1, node2))      return true;        // Default return value, if there is no defined ordering    boolean isNodeAfter = true;            Node parent1 = getParentOfNode(node1);    Node parent2 = getParentOfNode(node2);              // Optimize for most common case    if (parent1 == parent2 || isNodeTheSame(parent1, parent2))  // then we know they are siblings    {      if (null != parent1)        isNodeAfter = isNodeAfterSibling(parent1, node1, node2);      else      {                  // If both parents are null, ordering is not defined.                  // We're returning a value in lieu of throwing an exception.                  // Not a case we expect to arise in XPath, but beware if you                  // try to reuse this method.                                    // We can just fall through in this case, which allows us                  // to hit the debugging code at the end of the function.          //return isNodeAfter;      }    }    else    {      // General strategy: Figure out the lengths of the two       // ancestor chains, reconcile the lengths, and look for          // the lowest common ancestor. If that ancestor is one of          // the nodes being compared, it comes before the other.      // Otherwise perform a sibling compare.                 //                // NOTE: If no common ancestor is found, ordering is undefined                // and we return the default value of isNodeAfter.                      // Count parents in each ancestor chain      int nParents1 = 2, nParents2 = 2;  // include node & parent obtained above      while (parent1 != null)      {        nParents1++;        parent1 = getParentOfNode(parent1);      }      while (parent2 != null)      {        nParents2++;        parent2 = getParentOfNode(parent2);      }          // Initially assume scan for common ancestor starts with          // the input nodes.      Node startNode1 = node1, startNode2 = node2;      // If one ancestor chain is longer, adjust its start point          // so we're comparing at the same depths      if (nParents1 < nParents2)      {        // Adjust startNode2 to depth of startNode1        int adjust = nParents2 - nParents1;        for (int i = 0; i < adjust; i++)        {          startNode2 = getParentOfNode(startNode2);        }      }      else if (nParents1 > nParents2)      {        // adjust startNode1 to depth of startNode2        int adjust = nParents1 - nParents2;        for (int i = 0; i < adjust; i++)        {          startNode1 = getParentOfNode(startNode1);        }      }      Node prevChild1 = null, prevChild2 = null;  // so we can "back up"      // Loop up the ancestor chain looking for common parent      while (null != startNode1)      {        if (startNode1 == startNode2 || isNodeTheSame(startNode1, startNode2))  // common parent?        {          if (null == prevChild1)  // first time in loop?          {            // Edge condition: one is the ancestor of the other.            isNodeAfter = (nParents1 < nParents2) ? true : false;            break;  // from while loop          }          else           {                        // Compare ancestors below lowest-common as siblings            isNodeAfter = isNodeAfterSibling(startNode1, prevChild1,                                             prevChild2);            break;  // from while loop          }        }  // end if(startNode1 == startNode2)                // Move up one level and try again        prevChild1 = startNode1;        startNode1 = getParentOfNode(startNode1);        prevChild2 = startNode2;        startNode2 = getParentOfNode(startNode2);      }  // end while(parents exist to examine)    }  // end big else (not immediate siblings)                // WARNING: The following diagnostic won't report the early        // "same node" case. Fix if/when needed.            /* -- please do not remove... very useful for diagnostics --    System.out.println("node1 = "+node1.getNodeName()+"("+node1.getNodeType()+")"+    ", node2 = "+node2.getNodeName()    +"("+node2.getNodeType()+")"+    ", isNodeAfter = "+isNodeAfter); */    return isNodeAfter;  }  // end isNodeAfter(Node node1, Node node2)  /**   * Use DTMNodeProxy to determine whether two nodes are the same.   *    * @param node1 The first DOM node to compare.   * @param node2 The second DOM node to compare.   * @return true if the two nodes are the same.   */  public static boolean isNodeTheSame(Node node1, Node node2)  {    if (node1 instanceof DTMNodeProxy && node2 instanceof DTMNodeProxy)      return ((DTMNodeProxy)node1).equals((DTMNodeProxy)node2);    else      return (node1 == node2);  }  /**   * Figure out if child2 is after child1 in document order.   * <p>   * Warning: Some aspects of "document order" are not well defined.   * For example, the order of attributes is considered   * meaningless in XML, and the order reported by our model will   * be consistant for a given invocation but may not    * match that of either the source file or the serialized output.   *    * @param parent Must be the parent of both child1 and child2.   * @param child1 Must be the child of parent and not equal to child2.   * @param child2 Must be the child of parent and not equal to child1.   * @return true if child 2 is after child1 in document order.   */  private static boolean isNodeAfterSibling(Node parent, Node child1,                                            Node child2)  {    boolean isNodeAfterSibling = false;    short child1type = child1.getNodeType();    short child2type = child2.getNodeType();    if ((Node.ATTRIBUTE_NODE != child1type)            && (Node.ATTRIBUTE_NODE == child2type))    {      // always sort attributes before non-attributes.      isNodeAfterSibling = false;    }    else if ((Node.ATTRIBUTE_NODE == child1type)             && (Node.ATTRIBUTE_NODE != child2type))    {      // always sort attributes before non-attributes.      isNodeAfterSibling = true;    }    else if (Node.ATTRIBUTE_NODE == child1type)    {      NamedNodeMap children = parent.getAttributes();      int nNodes = children.getLength();      boolean found1 = false, found2 = false;          // Count from the start until we find one or the other.      for (int i = 0; i < nNodes; i++)      {        Node child = children.item(i);        if (child1 == child || isNodeTheSame(child1, child))        {          if (found2)          {            isNodeAfterSibling = false;            break;          }          found1 = true;        }        else if (child2 == child || isNodeTheSame(child2, child))        {          if (found1)          {            isNodeAfterSibling = true;            break;          }          found2 = true;        }      }    }    else    {                // TODO: Check performance of alternate solution:                // There are two choices here: Count from the start of                // the document until we find one or the other, or count                // from one until we find or fail to find the other.                // Either can wind up scanning all the siblings in the worst                // case, which on a wide document can be a lot of work but                // is more typically is a short list.                 // Scanning from the start involves two tests per iteration,                // but it isn't clear that scanning from the middle doesn't                // yield more iterations on average.                 // We should run some testcases.      Node child = parent.getFirstChild();      boolean found1 = false, found2 = false;      while (null != child)      {        // Node child = children.item(i);        if (child1 == child || isNodeTheSame(child1, child))        {          if (found2)          {            isNodeAfterSibling = false;            break;          }          found1 = true;        }        else if (child2 == child || isNodeTheSame(child2, child))        {          if (found1)          {            isNodeAfterSibling = true;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?