blob: 7f8de93e554fee7a55cf259da6dde86f7b6eb492 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5
6/*
7 * Copyright 1999-2004 The Apache Software Foundation.
8 *
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * 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 */
22package com.sun.org.apache.xml.internal.security.utils;
23
24
25
26import java.io.IOException;
27import java.io.OutputStream;
28import java.util.HashSet;
29import java.util.Iterator;
30import java.util.Set;
31
32import com.sun.org.apache.xml.internal.security.c14n.CanonicalizationException;
33import com.sun.org.apache.xml.internal.security.c14n.Canonicalizer;
34import com.sun.org.apache.xml.internal.security.c14n.InvalidCanonicalizerException;
35import org.w3c.dom.Attr;
36import org.w3c.dom.Document;
37import org.w3c.dom.Element;
38import org.w3c.dom.NamedNodeMap;
39import org.w3c.dom.Node;
40import org.w3c.dom.NodeList;
41import org.w3c.dom.Text;
42
43
44
45/**
46 * DOM and XML accessibility and comfort functions.
47 *
48 * @author Christian Geuer-Pollmann
49 */
50public class XMLUtils {
51
52 /**
53 * Constructor XMLUtils
54 *
55 */
56 private XMLUtils() {
57
58 // we don't allow instantiation
59 }
60
61
62 /**
63 * @param rootNode
64 * @param result
65 * @param exclude
66 * @param com wheather comments or not
67 */
68 public static void getSet(Node rootNode,Set result,Node exclude ,boolean com) {
69 if ((exclude!=null) && isDescendantOrSelf(exclude,rootNode)){
70 return;
71 }
72 getSetRec(rootNode,result,exclude,com);
73 }
74 static final void getSetRec(final Node rootNode,final Set result,
75 final Node exclude ,final boolean com) {
76 //Set result = new HashSet();
77 if (rootNode==exclude) {
78 return;
79 }
80 switch (rootNode.getNodeType()) {
81 case Node.ELEMENT_NODE:
82 result.add(rootNode);
83 Element el=(Element)rootNode;
84 if (el.hasAttributes()) {
85 NamedNodeMap nl = ((Element)rootNode).getAttributes();
86 for (int i=0;i<nl.getLength();i++) {
87 result.add(nl.item(i));
88 }
89 }
90 //no return keep working
91 case Node.DOCUMENT_NODE:
92 for (Node r=rootNode.getFirstChild();r!=null;r=r.getNextSibling()){
93 if (r.getNodeType()==Node.TEXT_NODE) {
94 result.add(r);
95 while ((r!=null) && (r.getNodeType()==Node.TEXT_NODE)) {
96 r=r.getNextSibling();
97 }
98 if (r==null)
99 return;
100 }
101 getSetRec(r,result,exclude,com);
102 }
103 return;
104 case Node.COMMENT_NODE:
105 if (com) {
106 result.add(rootNode);
107 }
108 return;
109 case Node.DOCUMENT_TYPE_NODE:
110 return;
111 default:
112 result.add(rootNode);
113 }
114 return;
115 }
116
117
118 /**
119 * Outputs a DOM tree to an {@link OutputStream}.
120 *
121 * @param contextNode root node of the DOM tree
122 * @param os the {@link OutputStream}
123 */
124 public static void outputDOM(Node contextNode, OutputStream os) {
125 XMLUtils.outputDOM(contextNode, os, false);
126 }
127
128 /**
129 * Outputs a DOM tree to an {@link OutputStream}. <I>If an Exception is
130 * thrown during execution, it's StackTrace is output to System.out, but the
131 * Exception is not re-thrown.</I>
132 *
133 * @param contextNode root node of the DOM tree
134 * @param os the {@link OutputStream}
135 * @param addPreamble
136 */
137 public static void outputDOM(Node contextNode, OutputStream os,
138 boolean addPreamble) {
139
140 try {
141 if (addPreamble) {
142 os.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n".getBytes());
143 }
144
145 os.write(
146 Canonicalizer.getInstance(
147 Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS).canonicalizeSubtree(
148 contextNode));
149 } catch (IOException ex) {}
150 catch (InvalidCanonicalizerException ex) {
151 ex.printStackTrace();
152 } catch (CanonicalizationException ex) {
153 ex.printStackTrace();
154 }
155 }
156
157 /**
158 * Serializes the <CODE>contextNode</CODE> into the OutputStream, <I>but
159 * supresses all Exceptions</I>.
160 * <BR />
161 * NOTE: <I>This should only be used for debugging purposes,
162 * NOT in a production environment; this method ignores all exceptions,
163 * so you won't notice if something goes wrong. If you're asking what is to
164 * be used in a production environment, simply use the code inside the
165 * <code>try{}</code> statement, but handle the Exceptions appropriately.</I>
166 *
167 * @param contextNode
168 * @param os
169 */
170 public static void outputDOMc14nWithComments(Node contextNode,
171 OutputStream os) {
172
173 try {
174 os.write(
175 Canonicalizer.getInstance(
176 Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS).canonicalizeSubtree(
177 contextNode));
178 } catch (IOException ex) {
179
180 // throw new RuntimeException(ex.getMessage());
181 } catch (InvalidCanonicalizerException ex) {
182
183 // throw new RuntimeException(ex.getMessage());
184 } catch (CanonicalizationException ex) {
185
186 // throw new RuntimeException(ex.getMessage());
187 }
188 }
189
190
191 /**
192 * Method getFullTextChildrenFromElement
193 *
194 * @param element
195 * @return the string of chi;ds
196 */
197 public static String getFullTextChildrenFromElement(Element element) {
198
199 StringBuffer sb = new StringBuffer();
200 NodeList children = element.getChildNodes();
201 int iMax = children.getLength();
202
203 for (int i = 0; i < iMax; i++) {
204 Node curr = children.item(i);
205
206 if (curr.getNodeType() == Node.TEXT_NODE) {
207 sb.append(((Text) curr).getData());
208 }
209 }
210
211 return sb.toString();
212 }
213
214
215 /**
216 * Creates an Element in the XML Signature specification namespace.
217 *
218 * @param doc the factory Document
219 * @param elementName the local name of the Element
220 * @return the Element
221 */
222 public static Element createElementInSignatureSpace(Document doc,
223 String elementName) {
224
225 if (doc == null) {
226 throw new RuntimeException("Document is null");
227 }
228
229 String ds = Constants.getSignatureSpecNSprefix();
230
231 if ((ds == null) || (ds.length() == 0)) {
232 Element element = doc.createElementNS(Constants.SignatureSpecNS,
233 elementName);
234
235 element.setAttributeNS(Constants.NamespaceSpecNS, "xmlns",
236 Constants.SignatureSpecNS);
237
238 return element;
239 }
240 Element element = doc.createElementNS(Constants.SignatureSpecNS,
241 ds + ":" + elementName);
242
243 element.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:" + ds,
244 Constants.SignatureSpecNS);
245
246 return element;
247
248 }
249
250
251 /**
252 * Returns true if the element is in XML Signature namespace and the local
253 * name equals the supplied one.
254 *
255 * @param element
256 * @param localName
257 * @return true if the element is in XML Signature namespace and the local name equals the supplied one
258 */
259 public static boolean elementIsInSignatureSpace(Element element,
260 String localName) {
261
262 if ((element == null) ||
263 !Constants.SignatureSpecNS.equals(element.getNamespaceURI()) ){
264 return false;
265 }
266
267 if (!element.getLocalName().equals(localName)) {
268 return false;
269 }
270
271 return true;
272 }
273
274 /**
275 * Returns true if the element is in XML Encryption namespace and the local
276 * name equals the supplied one.
277 *
278 * @param element
279 * @param localName
280 * @return true if the element is in XML Encryption namespace and the local name equals the supplied one
281 */
282 public static boolean elementIsInEncryptionSpace(Element element,
283 String localName) {
284
285 if ((element == null) ||
286 !EncryptionConstants.EncryptionSpecNS.equals(element.getNamespaceURI())
287 ){
288 return false;
289 }
290
291 if (!element.getLocalName().equals(localName)) {
292 return false;
293 }
294
295 return true;
296 }
297
298 /**
299 * This method returns the owner document of a particular node.
300 * This method is necessary because it <I>always</I> returns a
301 * {@link Document}. {@link Node#getOwnerDocument} returns <CODE>null</CODE>
302 * if the {@link Node} is a {@link Document}.
303 *
304 * @param node
305 * @return the owner document of the node
306 */
307 public static Document getOwnerDocument(Node node) {
308
309 if (node.getNodeType() == Node.DOCUMENT_NODE) {
310 return (Document) node;
311 }
312 try {
313 return node.getOwnerDocument();
314 } catch (NullPointerException npe) {
315 throw new NullPointerException(I18n.translate("endorsed.jdk1.4.0")
316 + " Original message was \""
317 + npe.getMessage() + "\"");
318 }
319
320 }
321
322 /**
323 * This method returns the first non-null owner document of the Node's in this Set.
324 * This method is necessary because it <I>always</I> returns a
325 * {@link Document}. {@link Node#getOwnerDocument} returns <CODE>null</CODE>
326 * if the {@link Node} is a {@link Document}.
327 *
328 * @param xpathNodeSet
329 * @return the owner document
330 */
331 public static Document getOwnerDocument(Set xpathNodeSet) {
332 NullPointerException npe = null;
333 Iterator iterator = xpathNodeSet.iterator();
334 while(iterator.hasNext()) {
335 Node node = (Node) iterator.next();
336 int nodeType =node.getNodeType();
337 if (nodeType == Node.DOCUMENT_NODE) {
338 return (Document) node;
339 }
340 try {
341 if (nodeType==Node.ATTRIBUTE_NODE) {
342 return ((Attr)node).getOwnerElement().getOwnerDocument();
343 }
344 return node.getOwnerDocument();
345 } catch (NullPointerException e) {
346 npe = e;
347 }
348
349 }
350 throw new NullPointerException(I18n.translate("endorsed.jdk1.4.0")
351 + " Original message was \""
352 + (npe == null ? "" : npe.getMessage()) + "\"");
353 }
354
355
356
357 /**
358 * Method createDSctx
359 *
360 * @param doc
361 * @param prefix
362 * @param namespace
363 * @return the element.
364 */
365 public static Element createDSctx(Document doc, String prefix,
366 String namespace) {
367
368 if ((prefix == null) || (prefix.trim().length() == 0)) {
369 throw new IllegalArgumentException("You must supply a prefix");
370 }
371
372 Element ctx = doc.createElementNS(null, "namespaceContext");
373
374 ctx.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:" + prefix.trim(),
375 namespace);
376
377 return ctx;
378 }
379
380
381
382 /**
383 * Method addReturnToElement
384 *
385 * @param e
386 */
387 public static void addReturnToElement(Element e) {
388
389 Document doc = e.getOwnerDocument();
390
391 e.appendChild(doc.createTextNode("\n"));
392 }
393
394 /**
395 * Method convertNodelistToSet
396 *
397 * @param xpathNodeSet
398 * @return the set with the nodelist
399 */
400 public static Set convertNodelistToSet(NodeList xpathNodeSet) {
401
402 if (xpathNodeSet == null) {
403 return new HashSet();
404 }
405
406 int length = xpathNodeSet.getLength();
407 Set set = new HashSet(length);
408
409 for (int i = 0; i < length; i++) {
410 set.add(xpathNodeSet.item(i));
411 }
412
413 return set;
414 }
415
416
417 /**
418 * This method spreads all namespace attributes in a DOM document to their
419 * children. This is needed because the XML Signature XPath transform
420 * must evaluate the XPath against all nodes in the input, even against
421 * XPath namespace nodes. Through a bug in XalanJ2, the namespace nodes are
422 * not fully visible in the Xalan XPath model, so we have to do this by
423 * hand in DOM spaces so that the nodes become visible in XPath space.
424 *
425 * @param doc
426 * @see <A HREF="http://nagoya.apache.org/bugzilla/show_bug.cgi?id=2650">Namespace axis resolution is not XPath compliant </A>
427 */
428 public static void circumventBug2650(Document doc) {
429
430 Element documentElement = doc.getDocumentElement();
431
432 // if the document element has no xmlns definition, we add xmlns=""
433 Attr xmlnsAttr =
434 documentElement.getAttributeNodeNS(Constants.NamespaceSpecNS, "xmlns");
435
436 if (xmlnsAttr == null) {
437 documentElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns", "");
438 }
439
440 XMLUtils.circumventBug2650internal(doc);
441 }
442
443 /**
444 * This is the work horse for {@link #circumventBug2650}.
445 *
446 * @param node
447 * @see <A HREF="http://nagoya.apache.org/bugzilla/show_bug.cgi?id=2650">Namespace axis resolution is not XPath compliant </A>
448 */
449 private static void circumventBug2650internal(Node node) {
450 Node parent=null;
451 Node sibling=null;
452 final String namespaceNs=Constants.NamespaceSpecNS;
453 do {
454 switch (node.getNodeType()) {
455 case Node.ELEMENT_NODE :
456 Element element = (Element) node;
457 if (!element.hasChildNodes())
458 break;
459 if (element.hasAttributes()) {
460 NamedNodeMap attributes = element.getAttributes();
461 int attributesLength = attributes.getLength();
462
463 for (Node child = element.getFirstChild(); child!=null;
464 child=child.getNextSibling()) {
465
466 if (child.getNodeType() != Node.ELEMENT_NODE) {
467 continue;
468 }
469 Element childElement = (Element) child;
470
471 for (int i = 0; i < attributesLength; i++) {
472 Attr currentAttr = (Attr) attributes.item(i);
473 if (!namespaceNs.equals(currentAttr.getNamespaceURI()))
474 continue;
475 if (childElement.hasAttributeNS(namespaceNs,
476 currentAttr.getLocalName())) {
477 continue;
478 }
479 childElement.setAttributeNS(namespaceNs,
480 currentAttr.getName(),
481 currentAttr.getNodeValue());
482
483
484 }
485 }
486 }
487 case Node.ENTITY_REFERENCE_NODE :
488 case Node.DOCUMENT_NODE :
489 parent=node;
490 sibling=node.getFirstChild();
491 break;
492 }
493 while ((sibling==null) && (parent!=null)) {
494 sibling=parent.getNextSibling();
495 parent=parent.getParentNode();
496 };
497 if (sibling==null) {
498 return;
499 }
500
501 node=sibling;
502 sibling=node.getNextSibling();
503 } while (true);
504 }
505
506 /**
507 * @param sibling
508 * @param nodeName
509 * @param number
510 * @return nodes with the constrain
511 */
512 public static Element selectDsNode(Node sibling, String nodeName, int number) {
513 while (sibling!=null) {
514 if (nodeName.equals(sibling.getLocalName())
515 && Constants.SignatureSpecNS.equals(sibling.getNamespaceURI())) {
516 if (number==0){
517 return (Element)sibling;
518 }
519 number--;
520 }
521 sibling=sibling.getNextSibling();
522 }
523 return null;
524 }
525
526 /**
527 * @param sibling
528 * @param nodeName
529 * @param number
530 * @return nodes with the constrain
531 */
532
533 public static Element selectXencNode(Node sibling, String nodeName, int number) {
534 while (sibling!=null) {
535 if (nodeName.equals(sibling.getLocalName())
536 && EncryptionConstants.EncryptionSpecNS.equals(sibling.getNamespaceURI())) {
537 if (number==0){
538 return (Element)sibling;
539 }
540 number--;
541 }
542 sibling=sibling.getNextSibling();
543 }
544 return null;
545 }
546
547
548 /**
549 * @param sibling
550 * @param nodeName
551 * @param number
552 * @return nodes with the constrain
553 */
554 public static Text selectDsNodeText(Node sibling, String nodeName, int number) {
555 Node n=selectDsNode(sibling,nodeName,number);
556 if (n==null) {
557 return null;
558 }
559 n=n.getFirstChild();
560 while (n!=null && n.getNodeType()!=Node.TEXT_NODE) {
561 n=n.getNextSibling();
562 }
563 return (Text)n;
564 }
565
566 /**
567 * @param sibling
568 * @param uri
569 * @param nodeName
570 * @param number
571 * @return nodes with the constrain
572 */
573 public static Text selectNodeText(Node sibling, String uri, String nodeName, int number) {
574 Node n=selectNode(sibling,uri,nodeName,number);
575 if (n==null) {
576 return null;
577 }
578 n=n.getFirstChild();
579 while (n!=null && n.getNodeType()!=Node.TEXT_NODE) {
580 n=n.getNextSibling();
581 }
582 return (Text)n;
583 }
584
585 /**
586 * @param sibling
587 * @param uri
588 * @param nodeName
589 * @param number
590 * @return nodes with the constrain
591 */
592 public static Element selectNode(Node sibling, String uri,String nodeName, int number) {
593 while (sibling!=null) {
594 if (nodeName.equals(sibling.getLocalName())
595 && uri.equals(sibling.getNamespaceURI())) {
596 if (number==0){
597 return (Element)sibling;
598 }
599 number--;
600 }
601 sibling=sibling.getNextSibling();
602 }
603 return null;
604 }
605
606 /**
607 * @param sibling
608 * @param nodeName
609 * @return nodes with the constrain
610 */
611 public static Element[] selectDsNodes(Node sibling,String nodeName) {
612 return selectNodes(sibling,Constants.SignatureSpecNS,nodeName);
613 }
614
615 /**
616 * @param sibling
617 * @param uri
618 * @param nodeName
619 * @return nodes with the constrain
620 */
621 public static Element[] selectNodes(Node sibling,String uri,String nodeName) {
622 int size=20;
623 Element[] a= new Element[size];
624 int curr=0;
625 //List list=new ArrayList();
626 while (sibling!=null) {
627 if (nodeName.equals(sibling.getLocalName())
628 && uri.equals(sibling.getNamespaceURI())) {
629 a[curr++]=(Element)sibling;
630 if (size<=curr) {
631 int cursize= size<<2;
632 Element []cp=new Element[cursize];
633 System.arraycopy(a,0,cp,0,size);
634 a=cp;
635 size=cursize;
636 }
637 }
638 sibling=sibling.getNextSibling();
639 }
640 Element []af=new Element[curr];
641 System.arraycopy(a,0,af,0,curr);
642 return af;
643 }
644
645 /**
646 * @param signatureElement
647 * @param inputSet
648 * @return nodes with the constrain
649 */
650 public static Set excludeNodeFromSet(Node signatureElement, Set inputSet) {
651 Set resultSet = new HashSet();
652 Iterator iterator = inputSet.iterator();
653
654 while (iterator.hasNext()) {
655 Node inputNode = (Node) iterator.next();
656
657 if (!XMLUtils
658 .isDescendantOrSelf(signatureElement, inputNode)) {
659 resultSet.add(inputNode);
660 }
661 }
662 return resultSet;
663 }
664
665 /**
666 * Returns true if the descendantOrSelf is on the descendant-or-self axis
667 * of the context node.
668 *
669 * @param ctx
670 * @param descendantOrSelf
671 * @return true if the node is descendant
672 */
673 static public boolean isDescendantOrSelf(Node ctx, Node descendantOrSelf) {
674
675 if (ctx == descendantOrSelf) {
676 return true;
677 }
678
679 Node parent = descendantOrSelf;
680
681 while (true) {
682 if (parent == null) {
683 return false;
684 }
685
686 if (parent == ctx) {
687 return true;
688 }
689
690 if (parent.getNodeType() == Node.ATTRIBUTE_NODE) {
691 parent = ((Attr) parent).getOwnerElement();
692 } else {
693 parent = parent.getParentNode();
694 }
695 }
696 }
697}