1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /* 6 * Licensed to the Apache Software Foundation (ASF) under one or more 7 * contributor license agreements. See the NOTICE file distributed with 8 * this work for additional information regarding copyright ownership. 9 * The ASF licenses this file to You under the Apache License, Version 2.0 10 * (the "License"); you may not use this file except in compliance with 11 * the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 */ 21 22 package com.sun.org.apache.xml.internal.utils; 23 24 import java.io.IOException; 25 26 import javax.xml.parsers.DocumentBuilder; 27 import javax.xml.parsers.DocumentBuilderFactory; 28 import javax.xml.parsers.ParserConfigurationException; 29 import javax.xml.transform.TransformerException; 30 31 import org.w3c.dom.Attr; 32 import org.w3c.dom.Document; 33 import org.w3c.dom.Element; 34 import org.w3c.dom.Node; 35 36 import org.xml.sax.InputSource; 37 38 /** 39 * @deprecated Since the introduction of the DTM, this class will be removed. 40 * This class provides a DOM level 2 "helper", which provides services currently 41 * not provided be the DOM standard. 42 */ 43 public class DOM2Helper extends DOMHelper 44 { 45 46 /** 47 * Construct an instance. 48 */ 49 public DOM2Helper(){} 50 51 /** 52 * Check node to see if it was created by a DOM implementation 53 * that this helper is intended to support. This is currently 54 * disabled, and assumes all nodes are acceptable rather than checking 55 * that they implement com.sun.org.apache.xerces.internal.dom.NodeImpl. 56 * 57 * @param node The node to be tested. 58 * 59 * @throws TransformerException if the node is not one which this 60 * DOM2Helper can support. If we return without throwing the exception, 61 * the node is compatable. 62 * @xsl.usage internal 63 */ 64 public void checkNode(Node node) throws TransformerException 65 { 66 67 // if(!(node instanceof com.sun.org.apache.xerces.internal.dom.NodeImpl)) 68 // throw new TransformerException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_XERCES_CANNOT_HANDLE_NODES, new Object[]{((Object)node).getClass()})); //"DOM2Helper can not handle nodes of type" 69 //+((Object)node).getClass()); 70 } 71 72 /** 73 * Returns true if the DOM implementation handled by this helper 74 * supports the SAX ContentHandler interface. 75 * 76 * @return true (since Xerces does). 77 */ 78 public boolean supportsSAX() 79 { 80 return true; 81 } 82 83 /** Field m_doc: Document Node for the document this helper is currently 84 * accessing or building 85 * @see #setDocument 86 * @see #getDocument 87 * */ 88 private Document m_doc; 89 90 /** 91 * Specify which document this helper is currently operating on. 92 * 93 * @param doc The DOM Document node for this document. 94 * @see #getDocument 95 */ 96 public void setDocument(Document doc) 97 { 98 m_doc = doc; 99 } 100 101 /** 102 * Query which document this helper is currently operating on. 103 * 104 * @return The DOM Document node for this document. 105 * @see #setDocument 106 */ 107 public Document getDocument() 108 { 109 return m_doc; 110 } 111 112 /** 113 * Parse an XML document. 114 * 115 * <p>Right now the Xerces DOMParser class is used. This needs 116 * fixing, either via jaxp, or via some other, standard method.</p> 117 * 118 * <p>The application can use this method to instruct the SAX parser 119 * to begin parsing an XML document from any valid input 120 * source (a character stream, a byte stream, or a URI).</p> 121 * 122 * <p>Applications may not invoke this method while a parse is in 123 * progress (they should create a new Parser instead for each 124 * additional XML document). Once a parse is complete, an 125 * application may reuse the same Parser object, possibly with a 126 * different input source.</p> 127 * 128 * @param source The input source for the top-level of the 129 * XML document. 130 * 131 * @throws TransformerException if any checked exception is thrown. 132 * @xsl.usage internal 133 */ 134 public void parse(InputSource source) throws TransformerException 135 { 136 137 try 138 { 139 140 // I guess I should use JAXP factory here... when it's legal. 141 // com.sun.org.apache.xerces.internal.parsers.DOMParser parser 142 // = new com.sun.org.apache.xerces.internal.parsers.DOMParser(); 143 DocumentBuilderFactory builderFactory = 144 DocumentBuilderFactory.newInstance(); 145 146 builderFactory.setNamespaceAware(true); 147 builderFactory.setValidating(true); 148 149 DocumentBuilder parser = builderFactory.newDocumentBuilder(); 150 151 /* 152 // domParser.setFeature("http://apache.org/xml/features/dom/create-entity-ref-nodes", getShouldExpandEntityRefs()? false : true); 153 if(m_useDOM2getNamespaceURI) 154 { 155 parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", true); 156 parser.setFeature("http://xml.org/sax/features/namespaces", true); 157 } 158 else 159 { 160 parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", false); 161 } 162 163 parser.setFeature("http://apache.org/xml/features/allow-java-encodings", true); 164 */ 165 166 parser.setErrorHandler( 167 new com.sun.org.apache.xml.internal.utils.DefaultErrorHandler()); 168 169 // if(null != m_entityResolver) 170 // { 171 // System.out.println("Setting the entity resolver."); 172 // parser.setEntityResolver(m_entityResolver); 173 // } 174 setDocument(parser.parse(source)); 175 } 176 catch (org.xml.sax.SAXException se) 177 { 178 throw new TransformerException(se); 179 } 180 catch (ParserConfigurationException pce) 181 { 182 throw new TransformerException(pce); 183 } 184 catch (IOException ioe) 185 { 186 throw new TransformerException(ioe); 187 } 188 189 // setDocument(((com.sun.org.apache.xerces.internal.parsers.DOMParser)parser).getDocument()); 190 } 191 192 /** 193 * Given an XML ID, return the element. This requires assistance from the 194 * DOM and parser, and is meaningful only in the context of a DTD 195 * or schema which declares attributes as being of type ID. This 196 * information may or may not be available in all parsers, may or 197 * may not be available for specific documents, and may or may not 198 * be available when validation is not turned on. 199 * 200 * @param id The ID to search for, as a String. 201 * @param doc The document to search within, as a DOM Document node. 202 * @return DOM Element node with an attribute of type ID whose value 203 * uniquely matches the requested id string, or null if there isn't 204 * such an element or if the DOM can't answer the question for other 205 * reasons. 206 */ 207 public Element getElementByID(String id, Document doc) 208 { 209 return doc.getElementById(id); 210 } 211 212 /** 213 * Figure out whether node2 should be considered as being later 214 * in the document than node1, in Document Order as defined 215 * by the XPath model. This may not agree with the ordering defined 216 * by other XML applications. 217 * <p> 218 * There are some cases where ordering isn't defined, and neither are 219 * the results of this function -- though we'll generally return true. 220 * <p> 221 * TODO: Make sure this does the right thing with attribute nodes!!! 222 * 223 * @param node1 DOM Node to perform position comparison on. 224 * @param node2 DOM Node to perform position comparison on . 225 * 226 * @return false if node2 comes before node1, otherwise return true. 227 * You can think of this as 228 * <code>(node1.documentOrderPosition <= node2.documentOrderPosition)</code>. 229 */ 230 public static boolean isNodeAfter(Node node1, Node node2) 231 { 232 233 // Assume first that the nodes are DTM nodes, since discovering node 234 // order is massivly faster for the DTM. 235 if(node1 instanceof DOMOrder && node2 instanceof DOMOrder) 236 { 237 int index1 = ((DOMOrder) node1).getUid(); 238 int index2 = ((DOMOrder) node2).getUid(); 239 240 return index1 <= index2; 241 } 242 else 243 { 244 245 // isNodeAfter will return true if node is after countedNode 246 // in document order. The base isNodeAfter is sloooow (relatively). 247 return DOMHelper.isNodeAfter(node1, node2); 248 } 249 } 250 251 /** 252 * Get the XPath-model parent of a node. This version takes advantage 253 * of the DOM Level 2 Attr.ownerElement() method; the base version we 254 * would otherwise inherit is prepared to fall back on exhaustively 255 * walking the document to find an Attr's parent. 256 * 257 * @param node Node to be examined 258 * 259 * @return the DOM parent of the input node, if there is one, or the 260 * ownerElement if the input node is an Attr, or null if the node is 261 * a Document, a DocumentFragment, or an orphan. 262 */ 263 public static Node getParentOfNode(Node node) 264 { 265 Node parent=node.getParentNode(); 266 if(parent==null && (Node.ATTRIBUTE_NODE == node.getNodeType()) ) 267 parent=((Attr) node).getOwnerElement(); 268 return parent; 269 } 270 271 /** 272 * Returns the local name of the given node, as defined by the 273 * XML Namespaces specification. This is prepared to handle documents 274 * built using DOM Level 1 methods by falling back upon explicitly 275 * parsing the node name. 276 * 277 * @param n Node to be examined 278 * 279 * @return String containing the local name, or null if the node 280 * was not assigned a Namespace. 281 */ 282 public String getLocalNameOfNode(Node n) 283 { 284 285 String name = n.getLocalName(); 286 287 return (null == name) ? super.getLocalNameOfNode(n) : name; 288 } 289 290 /** 291 * Returns the Namespace Name (Namespace URI) for the given node. 292 * In a Level 2 DOM, you can ask the node itself. Note, however, that 293 * doing so conflicts with our decision in getLocalNameOfNode not 294 * to trust the that the DOM was indeed created using the Level 2 295 * methods. If Level 1 methods were used, these two functions will 296 * disagree with each other. 297 * <p> 298 * TODO: Reconcile with getLocalNameOfNode. 299 * 300 * @param n Node to be examined 301 * 302 * @return String containing the Namespace URI bound to this DOM node 303 * at the time the Node was created. 304 */ 305 public String getNamespaceOfNode(Node n) 306 { 307 return n.getNamespaceURI(); 308 } 309 310 /** Field m_useDOM2getNamespaceURI is a compile-time flag which 311 * gates some of the parser options used to build a DOM -- but 312 * that code is commented out at this time and nobody else 313 * references it, so I've commented this out as well. */ 314 //private boolean m_useDOM2getNamespaceURI = false; 315 } | 1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 */ 4 /* 5 * Licensed to the Apache Software Foundation (ASF) under one or more 6 * contributor license agreements. See the NOTICE file distributed with 7 * this work for additional information regarding copyright ownership. 8 * The ASF licenses this file to You under the Apache License, Version 2.0 9 * (the "License"); you may not use this file except in compliance with 10 * the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 package com.sun.org.apache.xml.internal.utils; 21 22 import com.sun.org.apache.xml.internal.dtm.ref.DTMNodeProxy; 23 import org.w3c.dom.Attr; 24 import org.w3c.dom.NamedNodeMap; 25 import org.w3c.dom.Node; 26 27 28 /** 29 * This class provides a DOM level 2 "helper", which provides several services. 30 * 31 * The original class extended DOMHelper that was deprecated and then removed. 32 */ 33 public final class DOM2Helper { 34 35 /** 36 * Construct an instance. 37 */ 38 private DOM2Helper() { 39 } 40 41 /** 42 * Returns the local name of the given node, as defined by the XML 43 * Namespaces specification. This is prepared to handle documents built 44 * using DOM Level 1 methods by falling back upon explicitly parsing the 45 * node name. 46 * 47 * @param n Node to be examined 48 * 49 * @return String containing the local name, or null if the node was not 50 * assigned a Namespace. 51 */ 52 public static String getLocalNameOfNode(Node n) { 53 54 String name = n.getLocalName(); 55 56 return (null == name) ? getLocalNameOfNodeFallback(n) : name; 57 } 58 59 /** 60 * Returns the local name of the given node. If the node's name begins with 61 * a namespace prefix, this is the part after the colon; otherwise it's the 62 * full node name. 63 * 64 * This method is copied from 65 * com.sun.org.apache.xml.internal.utils.DOMHelper 66 * 67 * @param n the node to be examined. 68 * 69 * @return String containing the Local Name 70 */ 71 private static String getLocalNameOfNodeFallback(Node n) { 72 73 String qname = n.getNodeName(); 74 int index = qname.indexOf(':'); 75 76 return (index < 0) ? qname : qname.substring(index + 1); 77 } 78 79 /** 80 * Returns the Namespace Name (Namespace URI) for the given node. In a Level 81 * 2 DOM, you can ask the node itself. Note, however, that doing so 82 * conflicts with our decision in getLocalNameOfNode not to trust the that 83 * the DOM was indeed created using the Level 2 methods. If Level 1 methods 84 * were used, these two functions will disagree with each other. 85 * <p> 86 * TODO: Reconcile with getLocalNameOfNode. 87 * 88 * @param n Node to be examined 89 * 90 * @return String containing the Namespace URI bound to this DOM node at the 91 * time the Node was created. 92 */ 93 public static String getNamespaceOfNode(Node n) { 94 return n.getNamespaceURI(); 95 } 96 97 /** 98 * Figure out whether node2 should be considered as being later in the 99 * document than node1, in Document Order as defined by the XPath model. 100 * This may not agree with the ordering defined by other XML applications. 101 * <p> 102 * There are some cases where ordering isn't defined, and neither are the 103 * results of this function -- though we'll generally return true. 104 * 105 * @param node1 DOM Node to perform position comparison on. 106 * @param node2 DOM Node to perform position comparison on . 107 * 108 * @return false if node2 comes before node1, otherwise return true. You can 109 * think of this as 110 * {@code (node1.documentOrderPosition <= node2.documentOrderPosition)}. 111 */ 112 public static boolean isNodeAfter(Node node1, Node node2) { 113 if (node1 == node2 || isNodeTheSame(node1, node2)) { 114 return true; 115 } 116 117 // Default return value, if there is no defined ordering 118 boolean isNodeAfter = true; 119 120 Node parent1 = getParentOfNode(node1); 121 Node parent2 = getParentOfNode(node2); 122 123 // Optimize for most common case 124 if (parent1 == parent2 || isNodeTheSame(parent1, parent2)) // then we know they are siblings 125 { 126 if (null != parent1) { 127 isNodeAfter = isNodeAfterSibling(parent1, node1, node2); 128 } 129 } else { 130 // General strategy: Figure out the lengths of the two 131 // ancestor chains, reconcile the lengths, and look for 132 // the lowest common ancestor. If that ancestor is one of 133 // the nodes being compared, it comes before the other. 134 // Otherwise perform a sibling compare. 135 // 136 // NOTE: If no common ancestor is found, ordering is undefined 137 // and we return the default value of isNodeAfter. 138 // Count parents in each ancestor chain 139 int nParents1 = 2, nParents2 = 2; // include node & parent obtained above 140 141 while (parent1 != null) { 142 nParents1++; 143 144 parent1 = getParentOfNode(parent1); 145 } 146 147 while (parent2 != null) { 148 nParents2++; 149 150 parent2 = getParentOfNode(parent2); 151 } 152 153 // Initially assume scan for common ancestor starts with 154 // the input nodes. 155 Node startNode1 = node1, startNode2 = node2; 156 157 // If one ancestor chain is longer, adjust its start point 158 // so we're comparing at the same depths 159 if (nParents1 < nParents2) { 160 // Adjust startNode2 to depth of startNode1 161 int adjust = nParents2 - nParents1; 162 163 for (int i = 0; i < adjust; i++) { 164 startNode2 = getParentOfNode(startNode2); 165 } 166 } else if (nParents1 > nParents2) { 167 // adjust startNode1 to depth of startNode2 168 int adjust = nParents1 - nParents2; 169 170 for (int i = 0; i < adjust; i++) { 171 startNode1 = getParentOfNode(startNode1); 172 } 173 } 174 175 Node prevChild1 = null, prevChild2 = null; // so we can "back up" 176 177 // Loop up the ancestor chain looking for common parent 178 while (null != startNode1) { 179 if (startNode1 == startNode2 || isNodeTheSame(startNode1, startNode2)) // common parent? 180 { 181 if (null == prevChild1) // first time in loop? 182 { 183 184 // Edge condition: one is the ancestor of the other. 185 isNodeAfter = (nParents1 < nParents2) ? true : false; 186 187 break; // from while loop 188 } else { 189 // Compare ancestors below lowest-common as siblings 190 isNodeAfter = isNodeAfterSibling(startNode1, prevChild1, 191 prevChild2); 192 193 break; // from while loop 194 } 195 } // end if(startNode1 == startNode2) 196 197 // Move up one level and try again 198 prevChild1 = startNode1; 199 startNode1 = getParentOfNode(startNode1); 200 prevChild2 = startNode2; 201 startNode2 = getParentOfNode(startNode2); 202 } // end while(parents exist to examine) 203 } // end big else (not immediate siblings) 204 205 return isNodeAfter; 206 } // end isNodeAfter(Node node1, Node node2) 207 208 /** 209 * Use DTMNodeProxy to determine whether two nodes are the same. 210 * 211 * @param node1 The first DOM node to compare. 212 * @param node2 The second DOM node to compare. 213 * @return true if the two nodes are the same. 214 */ 215 public static boolean isNodeTheSame(Node node1, Node node2) { 216 if (node1 instanceof DTMNodeProxy && node2 instanceof DTMNodeProxy) { 217 return ((DTMNodeProxy) node1).equals((DTMNodeProxy) node2); 218 } else { 219 return (node1 == node2); 220 } 221 } 222 223 /** 224 * Get the XPath-model parent of a node. This version takes advantage of the 225 * DOM Level 2 Attr.ownerElement() method; the base version we would 226 * otherwise inherit is prepared to fall back on exhaustively walking the 227 * document to find an Attr's parent. 228 * 229 * @param node Node to be examined 230 * 231 * @return the DOM parent of the input node, if there is one, or the 232 * ownerElement if the input node is an Attr, or null if the node is a 233 * Document, a DocumentFragment, or an orphan. 234 */ 235 public static Node getParentOfNode(Node node) { 236 Node parent = node.getParentNode(); 237 if (parent == null && (Node.ATTRIBUTE_NODE == node.getNodeType())) { 238 parent = ((Attr) node).getOwnerElement(); 239 } 240 return parent; 241 } 242 243 /** 244 * Figure out if child2 is after child1 in document order. 245 * <p> 246 * Warning: Some aspects of "document order" are not well defined. For 247 * example, the order of attributes is considered meaningless in XML, and 248 * the order reported by our model will be consistent for a given invocation 249 * but may not match that of either the source file or the serialized 250 * output. 251 * 252 * @param parent Must be the parent of both child1 and child2. 253 * @param child1 Must be the child of parent and not equal to child2. 254 * @param child2 Must be the child of parent and not equal to child1. 255 * @return true if child 2 is after child1 in document order. 256 */ 257 private static boolean isNodeAfterSibling(Node parent, Node child1, 258 Node child2) { 259 260 boolean isNodeAfterSibling = false; 261 short child1type = child1.getNodeType(); 262 short child2type = child2.getNodeType(); 263 264 if ((Node.ATTRIBUTE_NODE != child1type) 265 && (Node.ATTRIBUTE_NODE == child2type)) { 266 267 // always sort attributes before non-attributes. 268 isNodeAfterSibling = false; 269 } else if ((Node.ATTRIBUTE_NODE == child1type) 270 && (Node.ATTRIBUTE_NODE != child2type)) { 271 272 // always sort attributes before non-attributes. 273 isNodeAfterSibling = true; 274 } else if (Node.ATTRIBUTE_NODE == child1type) { 275 NamedNodeMap children = parent.getAttributes(); 276 int nNodes = children.getLength(); 277 boolean found1 = false, found2 = false; 278 279 // Count from the start until we find one or the other. 280 for (int i = 0; i < nNodes; i++) { 281 Node child = children.item(i); 282 283 if (child1 == child || isNodeTheSame(child1, child)) { 284 if (found2) { 285 isNodeAfterSibling = false; 286 287 break; 288 } 289 290 found1 = true; 291 } else if (child2 == child || isNodeTheSame(child2, child)) { 292 if (found1) { 293 isNodeAfterSibling = true; 294 295 break; 296 } 297 298 found2 = true; 299 } 300 } 301 } else { 302 // TODO: Check performance of alternate solution: 303 // There are two choices here: Count from the start of 304 // the document until we find one or the other, or count 305 // from one until we find or fail to find the other. 306 // Either can wind up scanning all the siblings in the worst 307 // case, which on a wide document can be a lot of work but 308 // is more typically is a short list. 309 // Scanning from the start involves two tests per iteration, 310 // but it isn't clear that scanning from the middle doesn't 311 // yield more iterations on average. 312 // We should run some testcases. 313 Node child = parent.getFirstChild(); 314 boolean found1 = false, found2 = false; 315 316 while (null != child) { 317 318 // Node child = children.item(i); 319 if (child1 == child || isNodeTheSame(child1, child)) { 320 if (found2) { 321 isNodeAfterSibling = false; 322 323 break; 324 } 325 326 found1 = true; 327 } else if (child2 == child || isNodeTheSame(child2, child)) { 328 if (found1) { 329 isNodeAfterSibling = true; 330 331 break; 332 } 333 334 found2 = true; 335 } 336 337 child = child.getNextSibling(); 338 } 339 } 340 341 return isNodeAfterSibling; 342 } // end isNodeAfterSibling(Node parent, Node child1, Node child2) 343 } |