001: /*
002: * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025: /*
026: * $Id: DOMUtils.java,v 1.18 2005/05/12 19:28:34 mullan Exp $
027: */
028: package org.jcp.xml.dsig.internal.dom;
029:
030: import java.util.*;
031: import java.security.spec.AlgorithmParameterSpec;
032: import org.w3c.dom.Attr;
033: import org.w3c.dom.Document;
034: import org.w3c.dom.Element;
035: import org.w3c.dom.Node;
036: import org.w3c.dom.NodeList;
037: import javax.xml.crypto.*;
038: import javax.xml.crypto.dsig.dom.DOMSignContext;
039: import javax.xml.crypto.dsig.*;
040: import javax.xml.crypto.dsig.spec.*;
041:
042: import com.sun.org.apache.xml.internal.security.utils.IdResolver;
043:
044: /**
045: * Useful static DOM utility methods.
046: *
047: * @author Sean Mullan
048: */
049: public class DOMUtils {
050:
051: // class cannot be instantiated
052: private DOMUtils() {
053: }
054:
055: /**
056: * Returns the owner document of the specified node.
057: *
058: * @param node the node
059: * @return the owner document
060: */
061: public static Document getOwnerDocument(Node node) {
062: if (node.getNodeType() == Node.DOCUMENT_NODE) {
063: return (Document) node;
064: } else {
065: return node.getOwnerDocument();
066: }
067: }
068:
069: /**
070: * Creates an element in the specified namespace, with the specified tag
071: * and namespace prefix.
072: *
073: * @param doc the owner document
074: * @param tag the tag
075: * @param nsURI the namespace URI
076: * @param prefix the namespace prefix
077: * @return the newly created element
078: */
079: public static Element createElement(Document doc, String tag,
080: String nsURI, String prefix) {
081: String qName = prefix == null ? tag : prefix + ":" + tag;
082: return doc.createElementNS(nsURI, qName);
083: }
084:
085: /**
086: * Sets an element's attribute (using DOM level 2) with the
087: * specified value and namespace prefix.
088: *
089: * @param elem the element to set the attribute on
090: * @param name the name of the attribute
091: * @param value the attribute value. If null, no attribute is set.
092: */
093: public static void setAttribute(Element elem, String name,
094: String value) {
095: if (value == null)
096: return;
097: elem.setAttributeNS(null, name, value);
098: }
099:
100: /**
101: * Sets an element's attribute (using DOM level 2) with the
102: * specified value and namespace prefix AND registers the ID value with
103: * the specified element. This is for resolving same-document
104: * ID references.
105: *
106: * @param elem the element to set the attribute on
107: * @param name the name of the attribute
108: * @param value the attribute value. If null, no attribute is set.
109: */
110: public static void setAttributeID(Element elem, String name,
111: String value) {
112: if (value == null)
113: return;
114: elem.setAttributeNS(null, name, value);
115: IdResolver.registerElementById(elem, value);
116: }
117:
118: /**
119: * Returns the first child element of the specified node, or null if there
120: * is no such element.
121: *
122: * @param node the node
123: * @return the first child element of the specified node, or null if there
124: * is no such element
125: * @throws NullPointerException if <code>node == null</code>
126: */
127: public static Element getFirstChildElement(Node node) {
128: Node child = node.getFirstChild();
129: while (child != null
130: && child.getNodeType() != Node.ELEMENT_NODE) {
131: child = child.getNextSibling();
132: }
133: return (Element) child;
134: }
135:
136: /**
137: * Returns the last child element of the specified node, or null if there
138: * is no such element.
139: *
140: * @param node the node
141: * @return the last child element of the specified node, or null if there
142: * is no such element
143: * @throws NullPointerException if <code>node == null</code>
144: */
145: public static Element getLastChildElement(Node node) {
146: Node child = node.getLastChild();
147: while (child != null
148: && child.getNodeType() != Node.ELEMENT_NODE) {
149: child = child.getPreviousSibling();
150: }
151: return (Element) child;
152: }
153:
154: /**
155: * Returns the next sibling element of the specified node, or null if there
156: * is no such element.
157: *
158: * @param node the node
159: * @return the next sibling element of the specified node, or null if there
160: * is no such element
161: * @throws NullPointerException if <code>node == null</code>
162: */
163: public static Element getNextSiblingElement(Node node) {
164: Node sibling = node.getNextSibling();
165: while (sibling != null
166: && sibling.getNodeType() != Node.ELEMENT_NODE) {
167: sibling = sibling.getNextSibling();
168: }
169: return (Element) sibling;
170: }
171:
172: /**
173: * Returns the attribute value for the attribute with the specified name.
174: * Returns null if there is no such attribute, or
175: * the empty string if the attribute value is empty.
176: *
177: * <p>This works around a limitation of the DOM
178: * <code>Element.getAttributeNode</code> method, which does not distinguish
179: * between an unspecified attribute and an attribute with a value of
180: * "" (it returns "" for both cases).
181: *
182: * @param elem the element containing the attribute
183: * @param name the name of the attribute
184: * @return the attribute value (may be null if unspecified)
185: */
186: public static String getAttributeValue(Element elem, String name) {
187: Attr attr = elem.getAttributeNodeNS(null, name);
188: return (attr == null) ? null : attr.getValue();
189: }
190:
191: /**
192: * Returns a Set of <code>Node</code>s, backed by the specified
193: * <code>NodeList</code>.
194: *
195: * @param nl the NodeList
196: * @return a Set of Nodes
197: */
198: public static Set nodeSet(NodeList nl) {
199: return new NodeSet(nl);
200: }
201:
202: static class NodeSet extends AbstractSet {
203: private NodeList nl;
204:
205: public NodeSet(NodeList nl) {
206: this .nl = nl;
207: }
208:
209: public int size() {
210: return nl.getLength();
211: }
212:
213: public Iterator iterator() {
214: return new Iterator() {
215: int index = 0;
216:
217: public void remove() {
218: throw new UnsupportedOperationException();
219: }
220:
221: public Object next() {
222: if (!hasNext()) {
223: throw new NoSuchElementException();
224: }
225: return nl.item(index++);
226: }
227:
228: public boolean hasNext() {
229: return index < nl.getLength() ? true : false;
230: }
231: };
232: }
233: }
234:
235: /**
236: * Returns the prefix associated with the specified namespace URI
237: *
238: * @param context contains the namespace map
239: * @param nsURI the namespace URI
240: * @return the prefix associated with the specified namespace URI, or
241: * null if not set
242: */
243: public static String getNSPrefix(XMLCryptoContext context,
244: String nsURI) {
245: if (context != null) {
246: return context.getNamespacePrefix(nsURI, context
247: .getDefaultNamespacePrefix());
248: } else {
249: return null;
250: }
251: }
252:
253: /**
254: * Returns the prefix associated with the XML Signature namespace URI
255: *
256: * @param context contains the namespace map
257: * @return the prefix associated with the specified namespace URI, or
258: * null if not set
259: */
260: public static String getSignaturePrefix(XMLCryptoContext context) {
261: return getNSPrefix(context, XMLSignature.XMLNS);
262: }
263:
264: /**
265: * Removes all children nodes from the specified node.
266: *
267: * @param node the parent node whose children are to be removed
268: */
269: public static void removeAllChildren(Node node) {
270: NodeList children = node.getChildNodes();
271: for (int i = 0, length = children.getLength(); i < length; i++) {
272: node.removeChild(children.item(i));
273: }
274: }
275:
276: /**
277: * Compares 2 nodes for equality. Implementation is not complete.
278: */
279: public static boolean nodesEqual(Node this Node, Node otherNode) {
280: if (this Node == otherNode) {
281: return true;
282: }
283: if (this Node.getNodeType() != otherNode.getNodeType()) {
284: return false;
285: }
286: // FIXME - test content, etc
287: return true;
288: }
289:
290: /**
291: * Checks if child element has same owner document before
292: * appending to the parent, and imports it to the parent's document
293: * if necessary.
294: */
295: public static void appendChild(Node parent, Node child) {
296: Document ownerDoc = getOwnerDocument(parent);
297: if (child.getOwnerDocument() != ownerDoc) {
298: parent.appendChild(ownerDoc.importNode(child, true));
299: } else {
300: parent.appendChild(child);
301: }
302: }
303:
304: public static boolean paramsEqual(AlgorithmParameterSpec spec1,
305: AlgorithmParameterSpec spec2) {
306: if (spec1 == spec2) {
307: return true;
308: }
309: if (spec1 instanceof XPathFilter2ParameterSpec
310: && spec2 instanceof XPathFilter2ParameterSpec) {
311: return paramsEqual((XPathFilter2ParameterSpec) spec1,
312: (XPathFilter2ParameterSpec) spec2);
313: }
314: if (spec1 instanceof ExcC14NParameterSpec
315: && spec2 instanceof ExcC14NParameterSpec) {
316: return paramsEqual((ExcC14NParameterSpec) spec1,
317: (ExcC14NParameterSpec) spec2);
318: }
319: if (spec1 instanceof XPathFilterParameterSpec
320: && spec2 instanceof XPathFilterParameterSpec) {
321: return paramsEqual((XPathFilterParameterSpec) spec1,
322: (XPathFilterParameterSpec) spec2);
323: }
324: if (spec1 instanceof XSLTTransformParameterSpec
325: && spec2 instanceof XSLTTransformParameterSpec) {
326: return paramsEqual((XSLTTransformParameterSpec) spec1,
327: (XSLTTransformParameterSpec) spec2);
328: }
329: return false;
330: }
331:
332: private static boolean paramsEqual(XPathFilter2ParameterSpec spec1,
333: XPathFilter2ParameterSpec spec2) {
334:
335: List types = spec1.getXPathList();
336: List otypes = spec2.getXPathList();
337: int size = types.size();
338: if (size != otypes.size()) {
339: return false;
340: }
341: for (int i = 0; i < size; i++) {
342: XPathType type = (XPathType) types.get(i);
343: XPathType otype = (XPathType) otypes.get(i);
344: if (!type.getExpression().equals(otype.getExpression())
345: || type.getFilter() != otype.getFilter()) {
346: return false;
347: }
348: }
349: return true;
350: }
351:
352: private static boolean paramsEqual(ExcC14NParameterSpec spec1,
353: ExcC14NParameterSpec spec2) {
354: return spec1.getPrefixList().equals(spec2.getPrefixList());
355: }
356:
357: private static boolean paramsEqual(XPathFilterParameterSpec spec1,
358: XPathFilterParameterSpec spec2) {
359:
360: return spec1.getXPath().equals(spec2.getXPath());
361: }
362:
363: private static boolean paramsEqual(
364: XSLTTransformParameterSpec spec1,
365: XSLTTransformParameterSpec spec2) {
366:
367: XMLStructure ostylesheet = spec2.getStylesheet();
368: if (!(ostylesheet instanceof javax.xml.crypto.dom.DOMStructure)) {
369: return false;
370: }
371: Node ostylesheetElem = ((javax.xml.crypto.dom.DOMStructure) ostylesheet)
372: .getNode();
373: XMLStructure stylesheet = spec1.getStylesheet();
374: Node stylesheetElem = ((javax.xml.crypto.dom.DOMStructure) stylesheet)
375: .getNode();
376: return nodesEqual(stylesheetElem, ostylesheetElem);
377: }
378: }
|