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