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 <= 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 + -
显示快捷键?