1/* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5/** 6 * Licensed to the Apache Software Foundation (ASF) under one 7 * or more contributor license agreements. See the NOTICE file 8 * distributed with this work for additional information 9 * regarding copyright ownership. The ASF licenses this file 10 * to you under the Apache License, Version 2.0 (the 11 * "License"); you may not use this file except in compliance 12 * with the License. You may obtain a copy of the License at 13 * 14 * http://www.apache.org/licenses/LICENSE-2.0 15 * 16 * Unless required by applicable law or agreed to in writing, 17 * software distributed under the License is distributed on an 18 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19 * KIND, either express or implied. See the License for the 20 * specific language governing permissions and limitations 21 * under the License. 22 */ 23/* 24 * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. 25 */ 26/* 27 * $Id: DOMUtils.java 1333415 2012-05-03 12:03:51Z coheigea $ 28 */ 29package org.jcp.xml.dsig.internal.dom; 30 31import java.util.*; 32import java.security.spec.AlgorithmParameterSpec; 33import org.w3c.dom.Attr; 34import org.w3c.dom.Document; 35import org.w3c.dom.Element; 36import org.w3c.dom.Node; 37import org.w3c.dom.NodeList; 38import javax.xml.crypto.*; 39import javax.xml.crypto.dsig.*; 40import javax.xml.crypto.dsig.spec.*; 41 42/** 43 * Useful static DOM utility methods. 44 * 45 * @author Sean Mullan 46 */ 47public class DOMUtils { 48 49 // class cannot be instantiated 50 private DOMUtils() {} 51 52 /** 53 * Returns the owner document of the specified node. 54 * 55 * @param node the node 56 * @return the owner document 57 */ 58 public static Document getOwnerDocument(Node node) { 59 if (node.getNodeType() == Node.DOCUMENT_NODE) { 60 return (Document)node; 61 } else { 62 return node.getOwnerDocument(); 63 } 64 } 65 66 /** 67 * Creates an element in the specified namespace, with the specified tag 68 * and namespace prefix. 69 * 70 * @param doc the owner document 71 * @param tag the tag 72 * @param nsURI the namespace URI 73 * @param prefix the namespace prefix 74 * @return the newly created element 75 */ 76 public static Element createElement(Document doc, String tag, 77 String nsURI, String prefix) 78 { 79 String qName = (prefix == null || prefix.length() == 0) 80 ? tag : prefix + ":" + tag; 81 return doc.createElementNS(nsURI, qName); 82 } 83 84 /** 85 * Sets an element's attribute (using DOM level 2) with the 86 * specified value and namespace prefix. 87 * 88 * @param elem the element to set the attribute on 89 * @param name the name of the attribute 90 * @param value the attribute value. If null, no attribute is set. 91 */ 92 public static void setAttribute(Element elem, String name, String value) { 93 if (value == null) { 94 return; 95 } 96 elem.setAttributeNS(null, name, value); 97 } 98 99 /** 100 * Sets an element's attribute (using DOM level 2) with the 101 * specified value and namespace prefix AND registers the ID value with 102 * the specified element. This is for resolving same-document 103 * ID references. 104 * 105 * @param elem the element to set the attribute on 106 * @param name the name of the attribute 107 * @param value the attribute value. If null, no attribute is set. 108 */ 109 public static void setAttributeID(Element elem, String name, String value) { 110 if (value == null) { 111 return; 112 } 113 elem.setAttributeNS(null, name, value); 114 elem.setIdAttributeNS(null, name, true); 115 } 116 117 /** 118 * Returns the first child element of the specified node, or null if there 119 * is no such element. 120 * 121 * @param node the node 122 * @return the first child element of the specified node, or null if there 123 * is no such element 124 * @throws NullPointerException if <code>node == null</code> 125 */ 126 public static Element getFirstChildElement(Node node) { 127 Node child = node.getFirstChild(); 128 while (child != null && child.getNodeType() != Node.ELEMENT_NODE) { 129 child = child.getNextSibling(); 130 } 131 return (Element)child; 132 } 133 134 /** 135 * Returns the first child element of the specified node and checks that 136 * the local name is equal to {@code localName}. 137 * 138 * @param node the node 139 * @return the first child element of the specified node 140 * @throws NullPointerException if {@code node == null} 141 * @throws MarshalException if no such element or the local name is not 142 * equal to {@code localName} 143 */ 144 public static Element getFirstChildElement(Node node, String localName) 145 throws MarshalException 146 { 147 return verifyElement(getFirstChildElement(node), localName); 148 } 149 150 private static Element verifyElement(Element elem, String localName) 151 throws MarshalException 152 { 153 if (elem == null) { 154 throw new MarshalException("Missing " + localName + " element"); 155 } 156 String name = elem.getLocalName(); 157 if (!name.equals(localName)) { 158 throw new MarshalException("Invalid element name: " + 159 name + ", expected " + localName); 160 } 161 return elem; 162 } 163 164 /** 165 * Returns the last child element of the specified node, or null if there 166 * is no such element. 167 * 168 * @param node the node 169 * @return the last child element of the specified node, or null if there 170 * is no such element 171 * @throws NullPointerException if <code>node == null</code> 172 */ 173 public static Element getLastChildElement(Node node) { 174 Node child = node.getLastChild(); 175 while (child != null && child.getNodeType() != Node.ELEMENT_NODE) { 176 child = child.getPreviousSibling(); 177 } 178 return (Element)child; 179 } 180 181 /** 182 * Returns the next sibling element of the specified node, or null if there 183 * is no such element. 184 * 185 * @param node the node 186 * @return the next sibling element of the specified node, or null if there 187 * is no such element 188 * @throws NullPointerException if <code>node == null</code> 189 */ 190 public static Element getNextSiblingElement(Node node) { 191 Node sibling = node.getNextSibling(); 192 while (sibling != null && sibling.getNodeType() != Node.ELEMENT_NODE) { 193 sibling = sibling.getNextSibling(); 194 } 195 return (Element)sibling; 196 } 197 198 /** 199 * Returns the next sibling element of the specified node and checks that 200 * the local name is equal to {@code localName}. 201 * 202 * @param node the node 203 * @return the next sibling element of the specified node 204 * @throws NullPointerException if {@code node == null} 205 * @throws MarshalException if no such element or the local name is not 206 * equal to {@code localName} 207 */ 208 public static Element getNextSiblingElement(Node node, String localName) 209 throws MarshalException 210 { 211 return verifyElement(getNextSiblingElement(node), localName); 212 } 213 214 /** 215 * Returns the attribute value for the attribute with the specified name. 216 * Returns null if there is no such attribute, or 217 * the empty string if the attribute value is empty. 218 * 219 * <p>This works around a limitation of the DOM 220 * <code>Element.getAttributeNode</code> method, which does not distinguish 221 * between an unspecified attribute and an attribute with a value of 222 * "" (it returns "" for both cases). 223 * 224 * @param elem the element containing the attribute 225 * @param name the name of the attribute 226 * @return the attribute value (may be null if unspecified) 227 */ 228 public static String getAttributeValue(Element elem, String name) { 229 Attr attr = elem.getAttributeNodeNS(null, name); 230 return (attr == null) ? null : attr.getValue(); 231 } 232 233 /** 234 * Returns a Set of <code>Node</code>s, backed by the specified 235 * <code>NodeList</code>. 236 * 237 * @param nl the NodeList 238 * @return a Set of Nodes 239 */ 240 public static Set<Node> nodeSet(NodeList nl) { 241 return new NodeSet(nl); 242 } 243 244 static class NodeSet extends AbstractSet<Node> { 245 private NodeList nl; 246 public NodeSet(NodeList nl) { 247 this.nl = nl; 248 } 249 250 public int size() { return nl.getLength(); } 251 public Iterator<Node> iterator() { 252 return new Iterator<Node>() { 253 int index = 0; 254 255 public void remove() { 256 throw new UnsupportedOperationException(); 257 } 258 public Node next() { 259 if (!hasNext()) { 260 throw new NoSuchElementException(); 261 } 262 return nl.item(index++); 263 } 264 public boolean hasNext() { 265 return index < nl.getLength() ? true : false; 266 } 267 }; 268 } 269 } 270 271 /** 272 * Returns the prefix associated with the specified namespace URI 273 * 274 * @param context contains the namespace map 275 * @param nsURI the namespace URI 276 * @return the prefix associated with the specified namespace URI, or 277 * null if not set 278 */ 279 public static String getNSPrefix(XMLCryptoContext context, String nsURI) { 280 if (context != null) { 281 return context.getNamespacePrefix 282 (nsURI, context.getDefaultNamespacePrefix()); 283 } else { 284 return null; 285 } 286 } 287 288 /** 289 * Returns the prefix associated with the XML Signature namespace URI 290 * 291 * @param context contains the namespace map 292 * @return the prefix associated with the specified namespace URI, or 293 * null if not set 294 */ 295 public static String getSignaturePrefix(XMLCryptoContext context) { 296 return getNSPrefix(context, XMLSignature.XMLNS); 297 } 298 299 /** 300 * Removes all children nodes from the specified node. 301 * 302 * @param node the parent node whose children are to be removed 303 */ 304 public static void removeAllChildren(Node node) { 305 NodeList children = node.getChildNodes(); 306 for (int i = 0, length = children.getLength(); i < length; i++) { 307 node.removeChild(children.item(i)); 308 } 309 } 310 311 /** 312 * Compares 2 nodes for equality. Implementation is not complete. 313 */ 314 public static boolean nodesEqual(Node thisNode, Node otherNode) { 315 if (thisNode == otherNode) { 316 return true; 317 } 318 if (thisNode.getNodeType() != otherNode.getNodeType()) { 319 return false; 320 } 321 // FIXME - test content, etc 322 return true; 323 } 324 325 /** 326 * Checks if child element has same owner document before 327 * appending to the parent, and imports it to the parent's document 328 * if necessary. 329 */ 330 public static void appendChild(Node parent, Node child) { 331 Document ownerDoc = getOwnerDocument(parent); 332 if (child.getOwnerDocument() != ownerDoc) { 333 parent.appendChild(ownerDoc.importNode(child, true)); 334 } else { 335 parent.appendChild(child); 336 } 337 } 338 339 public static boolean paramsEqual(AlgorithmParameterSpec spec1, 340 AlgorithmParameterSpec spec2) { 341 if (spec1 == spec2) { 342 return true; 343 } 344 if (spec1 instanceof XPathFilter2ParameterSpec && 345 spec2 instanceof XPathFilter2ParameterSpec) { 346 return paramsEqual((XPathFilter2ParameterSpec)spec1, 347 (XPathFilter2ParameterSpec)spec2); 348 } 349 if (spec1 instanceof ExcC14NParameterSpec && 350 spec2 instanceof ExcC14NParameterSpec) { 351 return paramsEqual((ExcC14NParameterSpec) spec1, 352 (ExcC14NParameterSpec)spec2); 353 } 354 if (spec1 instanceof XPathFilterParameterSpec && 355 spec2 instanceof XPathFilterParameterSpec) { 356 return paramsEqual((XPathFilterParameterSpec)spec1, 357 (XPathFilterParameterSpec)spec2); 358 } 359 if (spec1 instanceof XSLTTransformParameterSpec && 360 spec2 instanceof XSLTTransformParameterSpec) { 361 return paramsEqual((XSLTTransformParameterSpec)spec1, 362 (XSLTTransformParameterSpec)spec2); 363 } 364 return false; 365 } 366 367 private static boolean paramsEqual(XPathFilter2ParameterSpec spec1, 368 XPathFilter2ParameterSpec spec2) 369 { 370 List<XPathType> types = spec1.getXPathList(); 371 List<XPathType> otypes = spec2.getXPathList(); 372 int size = types.size(); 373 if (size != otypes.size()) { 374 return false; 375 } 376 for (int i = 0; i < size; i++) { 377 XPathType type = types.get(i); 378 XPathType otype = otypes.get(i); 379 if (!type.getExpression().equals(otype.getExpression()) || 380 !type.getNamespaceMap().equals(otype.getNamespaceMap()) || 381 type.getFilter() != otype.getFilter()) { 382 return false; 383 } 384 } 385 return true; 386 } 387 388 private static boolean paramsEqual(ExcC14NParameterSpec spec1, 389 ExcC14NParameterSpec spec2) 390 { 391 return spec1.getPrefixList().equals(spec2.getPrefixList()); 392 } 393 394 private static boolean paramsEqual(XPathFilterParameterSpec spec1, 395 XPathFilterParameterSpec spec2) 396 { 397 return (spec1.getXPath().equals(spec2.getXPath()) && 398 spec1.getNamespaceMap().equals(spec2.getNamespaceMap())); 399 } 400 401 private static boolean paramsEqual(XSLTTransformParameterSpec spec1, 402 XSLTTransformParameterSpec spec2) 403 { 404 405 XMLStructure ostylesheet = spec2.getStylesheet(); 406 if (!(ostylesheet instanceof javax.xml.crypto.dom.DOMStructure)) { 407 return false; 408 } 409 Node ostylesheetElem = 410 ((javax.xml.crypto.dom.DOMStructure) ostylesheet).getNode(); 411 XMLStructure stylesheet = spec1.getStylesheet(); 412 Node stylesheetElem = 413 ((javax.xml.crypto.dom.DOMStructure) stylesheet).getNode(); 414 return nodesEqual(stylesheetElem, ostylesheetElem); 415 } 416} 417