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 package com.sun.org.apache.xml.internal.security.utils;
  24 
  25 import java.io.IOException;
  26 import java.io.OutputStream;
  27 import java.security.AccessController;
  28 import java.security.PrivilegedAction;
  29 import java.util.ArrayList;
  30 import java.util.HashSet;
  31 import java.util.Iterator;
  32 import java.util.List;
  33 import java.util.Set;
  34 
  35 import com.sun.org.apache.xml.internal.security.c14n.CanonicalizationException;
  36 import com.sun.org.apache.xml.internal.security.c14n.Canonicalizer;
  37 import com.sun.org.apache.xml.internal.security.c14n.InvalidCanonicalizerException;
  38 import org.w3c.dom.Attr;
  39 import org.w3c.dom.Document;
  40 import org.w3c.dom.Element;
  41 import org.w3c.dom.NamedNodeMap;
  42 import org.w3c.dom.Node;
  43 import org.w3c.dom.NodeList;
  44 import org.w3c.dom.ProcessingInstruction;
  45 import org.w3c.dom.Text;
  46 
  47 /**
  48  * DOM and XML accessibility and comfort functions.
  49  *
  50  * @author Christian Geuer-Pollmann
  51  */
  52 public class XMLUtils {
  53 
  54     private static boolean ignoreLineBreaks =
  55         AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
  56             public Boolean run() {
  57                 return Boolean.valueOf(Boolean.getBoolean
  58                     ("com.sun.org.apache.xml.internal.security.ignoreLineBreaks"));
  59             }
  60         }).booleanValue();
  61     
  62     private static volatile String dsPrefix = "ds";
  63     private static volatile String ds11Prefix = "dsig11";
  64     private static volatile String xencPrefix = "xenc";
  65     private static volatile String xenc11Prefix = "xenc11";
  66     
  67     /** {@link org.apache.commons.logging} logging facility */
  68     private static final java.util.logging.Logger log = 
  69         java.util.logging.Logger.getLogger(XMLUtils.class.getName());
  70 
  71 
  72     /**
  73      * Constructor XMLUtils
  74      *
  75      */
  76     private XMLUtils() {
  77         // we don't allow instantiation
  78     }
  79     
  80     /**
  81      * Set the prefix for the digital signature namespace
  82      * @param prefix the new prefix for the digital signature namespace
  83      */
  84     public static void setDsPrefix(String prefix) {
  85         dsPrefix = prefix;
  86     }
  87     
  88     /**
  89      * Set the prefix for the digital signature 1.1 namespace 
  90      * @param prefix the new prefix for the digital signature 1.1 namespace
  91      */
  92     public static void setDs11Prefix(String prefix) {
  93         ds11Prefix = prefix;
  94     }
  95     
  96     /**
  97      * Set the prefix for the encryption namespace
  98      * @param prefix the new prefix for the encryption namespace
  99      */
 100     public static void setXencPrefix(String prefix) {
 101         xencPrefix = prefix;
 102     }
 103     
 104     /**
 105      * Set the prefix for the encryption namespace 1.1
 106      * @param prefix the new prefix for the encryption namespace 1.1
 107      */
 108     public static void setXenc11Prefix(String prefix) {
 109         xenc11Prefix = prefix;
 110     }
 111     
 112     public static Element getNextElement(Node el) {
 113         Node node = el;
 114         while ((node != null) && (node.getNodeType() != Node.ELEMENT_NODE)) {
 115             node = node.getNextSibling();
 116         }
 117         return (Element)node;
 118     }
 119 
 120     /**
 121      * @param rootNode
 122      * @param result
 123      * @param exclude
 124      * @param com whether comments or not
 125      */   
 126     public static void getSet(Node rootNode, Set<Node> result, Node exclude, boolean com) {
 127         if ((exclude != null) && isDescendantOrSelf(exclude, rootNode)) {
 128             return;
 129         }
 130         getSetRec(rootNode, result, exclude, com);
 131     }
 132     
 133     @SuppressWarnings("fallthrough")
 134     private static void getSetRec(final Node rootNode, final Set<Node> result,
 135                                 final Node exclude, final boolean com) {
 136         if (rootNode == exclude) {
 137             return;
 138         }
 139         switch (rootNode.getNodeType()) {                                                       
 140         case Node.ELEMENT_NODE:
 141             result.add(rootNode);
 142             Element el = (Element)rootNode;
 143             if (el.hasAttributes()) {
 144                 NamedNodeMap nl = el.getAttributes();
 145                 for (int i = 0;i < nl.getLength(); i++) {
 146                     result.add(nl.item(i));
 147                 }
 148             }
 149             //no return keep working
 150         case Node.DOCUMENT_NODE:                                
 151             for (Node r = rootNode.getFirstChild(); r != null; r = r.getNextSibling()) {                                    
 152                 if (r.getNodeType() == Node.TEXT_NODE) {
 153                     result.add(r); 
 154                     while ((r != null) && (r.getNodeType() == Node.TEXT_NODE)) {
 155                         r = r.getNextSibling();
 156                     }
 157                     if (r == null) {
 158                         return;
 159                     }
 160                 }  
 161                 getSetRec(r, result, exclude, com);                
 162             }
 163             return;
 164         case Node.COMMENT_NODE:
 165             if (com) {
 166                 result.add(rootNode);
 167             }
 168             return;
 169         case Node.DOCUMENT_TYPE_NODE:
 170             return;
 171         default:
 172             result.add(rootNode);
 173         }
 174     }
 175 
 176 
 177     /**
 178      * Outputs a DOM tree to an {@link OutputStream}.
 179      *
 180      * @param contextNode root node of the DOM tree
 181      * @param os the {@link OutputStream}
 182      */
 183     public static void outputDOM(Node contextNode, OutputStream os) {
 184         XMLUtils.outputDOM(contextNode, os, false);
 185     }
 186 
 187     /**
 188      * Outputs a DOM tree to an {@link OutputStream}. <I>If an Exception is
 189      * thrown during execution, it's StackTrace is output to System.out, but the
 190      * Exception is not re-thrown.</I>
 191      *
 192      * @param contextNode root node of the DOM tree
 193      * @param os the {@link OutputStream}
 194      * @param addPreamble
 195      */
 196     public static void outputDOM(Node contextNode, OutputStream os, boolean addPreamble) {
 197         try {
 198             if (addPreamble) {
 199                 os.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n".getBytes("UTF-8"));
 200             }
 201 
 202             os.write(Canonicalizer.getInstance(
 203                 Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS).canonicalizeSubtree(contextNode)
 204             );
 205         } catch (IOException ex) {
 206             if (log.isLoggable(java.util.logging.Level.FINE)) {
 207                 log.log(java.util.logging.Level.FINE, ex.getMessage(), ex);
 208             }
 209         }
 210         catch (InvalidCanonicalizerException ex) {
 211             if (log.isLoggable(java.util.logging.Level.FINE)) {
 212                 log.log(java.util.logging.Level.FINE, ex.getMessage(), ex);
 213             }
 214         } catch (CanonicalizationException ex) {
 215             if (log.isLoggable(java.util.logging.Level.FINE)) {
 216                 log.log(java.util.logging.Level.FINE, ex.getMessage(), ex);
 217             }
 218         }
 219     }
 220 
 221     /**
 222      * Serializes the <CODE>contextNode</CODE> into the OutputStream, <I>but
 223      * suppresses all Exceptions</I>.
 224      * <BR />
 225      * NOTE: <I>This should only be used for debugging purposes,
 226      * NOT in a production environment; this method ignores all exceptions,
 227      * so you won't notice if something goes wrong. If you're asking what is to
 228      * be used in a production environment, simply use the code inside the
 229      * <code>try{}</code> statement, but handle the Exceptions appropriately.</I>
 230      *
 231      * @param contextNode
 232      * @param os
 233      */
 234     public static void outputDOMc14nWithComments(Node contextNode, OutputStream os) {
 235         try {
 236             os.write(Canonicalizer.getInstance(
 237                 Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS).canonicalizeSubtree(contextNode)
 238             );
 239         } catch (IOException ex) {
 240             if (log.isLoggable(java.util.logging.Level.FINE)) {
 241                 log.log(java.util.logging.Level.FINE, ex.getMessage(), ex);
 242             }
 243             // throw new RuntimeException(ex.getMessage());
 244         } catch (InvalidCanonicalizerException ex) {
 245             if (log.isLoggable(java.util.logging.Level.FINE)) {
 246                 log.log(java.util.logging.Level.FINE, ex.getMessage(), ex);
 247             }
 248             // throw new RuntimeException(ex.getMessage());
 249         } catch (CanonicalizationException ex) {
 250             if (log.isLoggable(java.util.logging.Level.FINE)) {
 251                 log.log(java.util.logging.Level.FINE, ex.getMessage(), ex);
 252             }
 253             // throw new RuntimeException(ex.getMessage());
 254         }
 255     }
 256 
 257     /**
 258      * Method getFullTextChildrenFromElement
 259      *
 260      * @param element
 261      * @return the string of children
 262      */
 263     public static String getFullTextChildrenFromElement(Element element) {
 264         StringBuilder sb = new StringBuilder();
 265         
 266         Node child = element.getFirstChild();
 267         while (child != null) {
 268             if (child.getNodeType() == Node.TEXT_NODE) {
 269                 sb.append(((Text)child).getData());
 270             }
 271             child = child.getNextSibling();
 272         }
 273 
 274         return sb.toString();
 275     }
 276 
 277     /**
 278      * Creates an Element in the XML Signature specification namespace.
 279      *
 280      * @param doc the factory Document
 281      * @param elementName the local name of the Element
 282      * @return the Element
 283      */
 284     public static Element createElementInSignatureSpace(Document doc, String elementName) {
 285         if (doc == null) {
 286             throw new RuntimeException("Document is null");
 287         }
 288 
 289         if ((dsPrefix == null) || (dsPrefix.length() == 0)) {
 290             return doc.createElementNS(Constants.SignatureSpecNS, elementName);
 291         } 
 292         return doc.createElementNS(Constants.SignatureSpecNS, dsPrefix + ":" + elementName);
 293     }
 294     
 295     /**
 296      * Creates an Element in the XML Signature 1.1 specification namespace.
 297      *
 298      * @param doc the factory Document
 299      * @param elementName the local name of the Element
 300      * @return the Element
 301      */
 302     public static Element createElementInSignature11Space(Document doc, String elementName) {
 303         if (doc == null) {
 304             throw new RuntimeException("Document is null");
 305         }
 306 
 307         if ((ds11Prefix == null) || (ds11Prefix.length() == 0)) {
 308             return doc.createElementNS(Constants.SignatureSpec11NS, elementName);
 309         } 
 310         return doc.createElementNS(Constants.SignatureSpec11NS, ds11Prefix + ":" + elementName);
 311     }
 312 
 313     /**
 314      * Creates an Element in the XML Encryption specification namespace.
 315      *
 316      * @param doc the factory Document
 317      * @param elementName the local name of the Element
 318      * @return the Element
 319      */
 320     public static Element createElementInEncryptionSpace(Document doc, String elementName) {
 321         if (doc == null) {
 322             throw new RuntimeException("Document is null");
 323         }
 324 
 325         if ((xencPrefix == null) || (xencPrefix.length() == 0)) {
 326             return doc.createElementNS(EncryptionConstants.EncryptionSpecNS, elementName);
 327         }
 328         return 
 329             doc.createElementNS(
 330                 EncryptionConstants.EncryptionSpecNS, xencPrefix + ":" + elementName
 331             );
 332     }
 333     
 334     /**
 335      * Creates an Element in the XML Encryption 1.1 specification namespace.
 336      *
 337      * @param doc the factory Document
 338      * @param elementName the local name of the Element
 339      * @return the Element
 340      */
 341     public static Element createElementInEncryption11Space(Document doc, String elementName) {
 342         if (doc == null) {
 343             throw new RuntimeException("Document is null");
 344         }
 345 
 346         if ((xenc11Prefix == null) || (xenc11Prefix.length() == 0)) {
 347             return doc.createElementNS(EncryptionConstants.EncryptionSpec11NS, elementName);
 348         }
 349         return 
 350             doc.createElementNS(
 351                 EncryptionConstants.EncryptionSpec11NS, xenc11Prefix + ":" + elementName
 352             );
 353     }
 354 
 355     /**
 356      * Returns true if the element is in XML Signature namespace and the local
 357      * name equals the supplied one.
 358      *
 359      * @param element
 360      * @param localName
 361      * @return true if the element is in XML Signature namespace and the local name equals 
 362      * the supplied one
 363      */
 364     public static boolean elementIsInSignatureSpace(Element element, String localName) {
 365         if (element == null){
 366             return false;
 367         }
 368 
 369         return Constants.SignatureSpecNS.equals(element.getNamespaceURI()) 
 370             && element.getLocalName().equals(localName);
 371     }
 372     
 373     /**
 374      * Returns true if the element is in XML Signature 1.1 namespace and the local
 375      * name equals the supplied one.
 376      *
 377      * @param element
 378      * @param localName
 379      * @return true if the element is in XML Signature namespace and the local name equals 
 380      * the supplied one
 381      */
 382     public static boolean elementIsInSignature11Space(Element element, String localName) {
 383         if (element == null) {
 384             return false;
 385         }
 386 
 387         return Constants.SignatureSpec11NS.equals(element.getNamespaceURI()) 
 388             && element.getLocalName().equals(localName);
 389     }
 390 
 391     /**
 392      * Returns true if the element is in XML Encryption namespace and the local
 393      * name equals the supplied one.
 394      *
 395      * @param element
 396      * @param localName
 397      * @return true if the element is in XML Encryption namespace and the local name 
 398      * equals the supplied one
 399      */
 400     public static boolean elementIsInEncryptionSpace(Element element, String localName) {
 401         if (element == null){
 402             return false;
 403         }
 404         return EncryptionConstants.EncryptionSpecNS.equals(element.getNamespaceURI()) 
 405             && element.getLocalName().equals(localName);
 406     }
 407     
 408     /**
 409      * Returns true if the element is in XML Encryption 1.1 namespace and the local
 410      * name equals the supplied one.
 411      *
 412      * @param element
 413      * @param localName
 414      * @return true if the element is in XML Encryption 1.1 namespace and the local name 
 415      * equals the supplied one
 416      */
 417     public static boolean elementIsInEncryption11Space(Element element, String localName) {
 418         if (element == null){
 419             return false;
 420         }
 421         return EncryptionConstants.EncryptionSpec11NS.equals(element.getNamespaceURI()) 
 422             && element.getLocalName().equals(localName);
 423     }
 424 
 425     /**
 426      * This method returns the owner document of a particular node.
 427      * This method is necessary because it <I>always</I> returns a
 428      * {@link Document}. {@link Node#getOwnerDocument} returns <CODE>null</CODE>
 429      * if the {@link Node} is a {@link Document}.
 430      *
 431      * @param node
 432      * @return the owner document of the node
 433      */
 434     public static Document getOwnerDocument(Node node) {
 435         if (node.getNodeType() == Node.DOCUMENT_NODE) {
 436             return (Document) node;
 437         } 
 438         try {
 439             return node.getOwnerDocument();
 440         } catch (NullPointerException npe) {
 441             throw new NullPointerException(I18n.translate("endorsed.jdk1.4.0")
 442                                            + " Original message was \""
 443                                            + npe.getMessage() + "\"");
 444         }
 445     }
 446 
 447     /**
 448      * This method returns the first non-null owner document of the Nodes in this Set.
 449      * This method is necessary because it <I>always</I> returns a
 450      * {@link Document}. {@link Node#getOwnerDocument} returns <CODE>null</CODE>
 451      * if the {@link Node} is a {@link Document}.
 452      *
 453      * @param xpathNodeSet
 454      * @return the owner document 
 455      */
 456     public static Document getOwnerDocument(Set<Node> xpathNodeSet) {
 457         NullPointerException npe = null;
 458         for (Node node : xpathNodeSet) {
 459             int nodeType = node.getNodeType();
 460             if (nodeType == Node.DOCUMENT_NODE) {
 461                 return (Document) node;
 462             } 
 463             try {
 464                 if (nodeType == Node.ATTRIBUTE_NODE) {
 465                     return ((Attr)node).getOwnerElement().getOwnerDocument();  
 466                 }
 467                 return node.getOwnerDocument();
 468             } catch (NullPointerException e) {
 469                 npe = e;
 470             }
 471         }
 472         
 473         throw new NullPointerException(I18n.translate("endorsed.jdk1.4.0")
 474                                        + " Original message was \""
 475                                        + (npe == null ? "" : npe.getMessage()) + "\"");
 476     }
 477 
 478     /**
 479      * Method createDSctx
 480      *
 481      * @param doc
 482      * @param prefix
 483      * @param namespace
 484      * @return the element.
 485      */
 486     public static Element createDSctx(Document doc, String prefix, String namespace) {
 487         if ((prefix == null) || (prefix.trim().length() == 0)) {
 488             throw new IllegalArgumentException("You must supply a prefix");
 489         }
 490 
 491         Element ctx = doc.createElementNS(null, "namespaceContext");
 492 
 493         ctx.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:" + prefix.trim(), namespace);
 494 
 495         return ctx;
 496     }
 497 
 498     /**
 499      * Method addReturnToElement
 500      *
 501      * @param e
 502      */
 503     public static void addReturnToElement(Element e) {
 504         if (!ignoreLineBreaks) {
 505             Document doc = e.getOwnerDocument();
 506             e.appendChild(doc.createTextNode("\n"));
 507         }
 508     }
 509 
 510     public static void addReturnToElement(Document doc, HelperNodeList nl) {
 511         if (!ignoreLineBreaks) {
 512             nl.appendChild(doc.createTextNode("\n"));
 513         }
 514     }
 515 
 516     public static void addReturnBeforeChild(Element e, Node child) {
 517         if (!ignoreLineBreaks) {
 518             Document doc = e.getOwnerDocument();
 519             e.insertBefore(doc.createTextNode("\n"), child);
 520         }
 521     }
 522 
 523     /**
 524      * Method convertNodelistToSet
 525      *
 526      * @param xpathNodeSet
 527      * @return the set with the nodelist
 528      */
 529     public static Set<Node> convertNodelistToSet(NodeList xpathNodeSet) {
 530         if (xpathNodeSet == null) {
 531             return new HashSet<Node>();
 532         }
 533 
 534         int length = xpathNodeSet.getLength();
 535         Set<Node> set = new HashSet<Node>(length);
 536 
 537         for (int i = 0; i < length; i++) {
 538             set.add(xpathNodeSet.item(i));
 539         }
 540 
 541         return set;
 542     }
 543 
 544     /**
 545      * This method spreads all namespace attributes in a DOM document to their
 546      * children. This is needed because the XML Signature XPath transform
 547      * must evaluate the XPath against all nodes in the input, even against
 548      * XPath namespace nodes. Through a bug in XalanJ2, the namespace nodes are
 549      * not fully visible in the Xalan XPath model, so we have to do this by
 550      * hand in DOM spaces so that the nodes become visible in XPath space.
 551      *
 552      * @param doc
 553      * @see <A HREF="http://nagoya.apache.org/bugzilla/show_bug.cgi?id=2650">
 554      * Namespace axis resolution is not XPath compliant </A>
 555      */
 556     public static void circumventBug2650(Document doc) {
 557 
 558         Element documentElement = doc.getDocumentElement();
 559 
 560         // if the document element has no xmlns definition, we add xmlns=""
 561         Attr xmlnsAttr =
 562             documentElement.getAttributeNodeNS(Constants.NamespaceSpecNS, "xmlns");
 563 
 564         if (xmlnsAttr == null) {
 565             documentElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns", "");
 566         }
 567 
 568         XMLUtils.circumventBug2650internal(doc);
 569     }
 570 
 571     /**
 572      * This is the work horse for {@link #circumventBug2650}.
 573      *
 574      * @param node
 575      * @see <A HREF="http://nagoya.apache.org/bugzilla/show_bug.cgi?id=2650">
 576      * Namespace axis resolution is not XPath compliant </A>
 577      */
 578     @SuppressWarnings("fallthrough")
 579     private static void circumventBug2650internal(Node node) {
 580         Node parent = null;
 581         Node sibling = null;
 582         final String namespaceNs = Constants.NamespaceSpecNS;
 583         do {
 584             switch (node.getNodeType()) {
 585             case Node.ELEMENT_NODE :
 586                 Element element = (Element) node;
 587                 if (!element.hasChildNodes()) {
 588                     break;
 589                 }
 590                 if (element.hasAttributes()) {                   
 591                     NamedNodeMap attributes = element.getAttributes();          
 592                     int attributesLength = attributes.getLength();    
 593 
 594                     for (Node child = element.getFirstChild(); child!=null; 
 595                         child = child.getNextSibling()) {            
 596 
 597                         if (child.getNodeType() != Node.ELEMENT_NODE) {
 598                             continue;
 599                         }
 600                         Element childElement = (Element) child;
 601 
 602                         for (int i = 0; i < attributesLength; i++) {
 603                             Attr currentAttr = (Attr) attributes.item(i); 
 604                             if (!namespaceNs.equals(currentAttr.getNamespaceURI())) {
 605                                 continue;
 606                             }
 607                             if (childElement.hasAttributeNS(namespaceNs, 
 608                                                             currentAttr.getLocalName())) {
 609                                 continue;
 610                             }
 611                             childElement.setAttributeNS(namespaceNs,
 612                                                         currentAttr.getName(),
 613                                                         currentAttr.getNodeValue());                                            
 614                         }
 615                     }            
 616                 }
 617             case Node.ENTITY_REFERENCE_NODE :
 618             case Node.DOCUMENT_NODE :
 619                 parent = node;
 620                 sibling = node.getFirstChild();
 621                 break;
 622             }
 623             while ((sibling == null) && (parent != null)) {
 624                 sibling = parent.getNextSibling();
 625                 parent = parent.getParentNode();
 626             }
 627             if (sibling == null) {
 628                 return;
 629             }
 630 
 631             node = sibling;
 632             sibling = node.getNextSibling();
 633         } while (true);
 634     }
 635     
 636     /**
 637      * @param sibling
 638      * @param nodeName
 639      * @param number
 640      * @return nodes with the constraint
 641      */
 642     public static Element selectDsNode(Node sibling, String nodeName, int number) {
 643         while (sibling != null) {
 644             if (Constants.SignatureSpecNS.equals(sibling.getNamespaceURI()) 
 645                 && sibling.getLocalName().equals(nodeName)) {
 646                 if (number == 0){
 647                     return (Element)sibling;
 648                 }
 649                 number--;
 650             }
 651             sibling = sibling.getNextSibling();
 652         }
 653         return null;
 654     }
 655 
 656     /**
 657      * @param sibling
 658      * @param nodeName
 659      * @param number
 660      * @return nodes with the constraint
 661      */
 662     public static Element selectDs11Node(Node sibling, String nodeName, int number) {
 663         while (sibling != null) {
 664             if (Constants.SignatureSpec11NS.equals(sibling.getNamespaceURI()) 
 665                 && sibling.getLocalName().equals(nodeName)) {
 666                 if (number == 0){
 667                     return (Element)sibling;
 668                 }
 669                 number--;
 670             }
 671             sibling = sibling.getNextSibling();
 672         }
 673         return null;
 674     }
 675 
 676     /**
 677      * @param sibling
 678      * @param nodeName
 679      * @param number
 680      * @return nodes with the constrain
 681      */
 682     public static Element selectXencNode(Node sibling, String nodeName, int number) {
 683         while (sibling != null) {
 684             if (EncryptionConstants.EncryptionSpecNS.equals(sibling.getNamespaceURI()) 
 685                 && sibling.getLocalName().equals(nodeName)) {
 686                 if (number == 0){
 687                     return (Element)sibling;
 688                 }
 689                 number--;
 690             }
 691             sibling = sibling.getNextSibling();
 692         }
 693         return null;
 694     }
 695 
 696 
 697     /**
 698      * @param sibling
 699      * @param nodeName
 700      * @param number
 701      * @return nodes with the constrain
 702      */
 703     public static Text selectDsNodeText(Node sibling, String nodeName, int number) {
 704         Node n = selectDsNode(sibling,nodeName,number);
 705         if (n == null) {
 706             return null;
 707         }
 708         n = n.getFirstChild();
 709         while (n != null && n.getNodeType() != Node.TEXT_NODE) {
 710             n = n.getNextSibling();
 711         }
 712         return (Text)n;
 713     }
 714     
 715     /**
 716      * @param sibling
 717      * @param nodeName
 718      * @param number
 719      * @return nodes with the constrain
 720      */
 721     public static Text selectDs11NodeText(Node sibling, String nodeName, int number) {
 722         Node n = selectDs11Node(sibling,nodeName,number);
 723         if (n == null) {
 724             return null;
 725         }
 726         n = n.getFirstChild();
 727         while (n != null && n.getNodeType() != Node.TEXT_NODE) {
 728             n = n.getNextSibling();
 729         }
 730         return (Text)n;
 731     }
 732 
 733     /**
 734      * @param sibling
 735      * @param uri
 736      * @param nodeName
 737      * @param number
 738      * @return nodes with the constrain
 739      */
 740     public static Text selectNodeText(Node sibling, String uri, String nodeName, int number) {
 741         Node n = selectNode(sibling,uri,nodeName,number);
 742         if (n == null) {
 743             return null;
 744         }
 745         n = n.getFirstChild();
 746         while (n != null && n.getNodeType() != Node.TEXT_NODE) {
 747             n = n.getNextSibling();
 748         }
 749         return (Text)n;
 750     }
 751 
 752     /**
 753      * @param sibling
 754      * @param uri
 755      * @param nodeName
 756      * @param number
 757      * @return nodes with the constrain
 758      */
 759     public static Element selectNode(Node sibling, String uri, String nodeName, int number) {
 760         while (sibling != null) {
 761             if (sibling.getNamespaceURI() != null && sibling.getNamespaceURI().equals(uri) 
 762                 && sibling.getLocalName().equals(nodeName)) {
 763                 if (number == 0){
 764                     return (Element)sibling;
 765                 }
 766                 number--;
 767             }
 768             sibling = sibling.getNextSibling();
 769         }
 770         return null;
 771     }
 772 
 773     /**
 774      * @param sibling
 775      * @param nodeName    
 776      * @return nodes with the constrain
 777      */
 778     public static Element[] selectDsNodes(Node sibling, String nodeName) {
 779         return selectNodes(sibling, Constants.SignatureSpecNS, nodeName);
 780     }
 781     
 782     /**
 783      * @param sibling
 784      * @param nodeName    
 785      * @return nodes with the constrain
 786      */
 787     public static Element[] selectDs11Nodes(Node sibling, String nodeName) {
 788         return selectNodes(sibling, Constants.SignatureSpec11NS, nodeName);
 789     }
 790     
 791     /**
 792      * @param sibling
 793      * @param uri
 794      * @param nodeName
 795      * @return nodes with the constraint
 796      */
 797     public static Element[] selectNodes(Node sibling, String uri, String nodeName) {
 798         List<Element> list = new ArrayList<Element>();
 799         while (sibling != null) {
 800             if (sibling.getNamespaceURI() != null && sibling.getNamespaceURI().equals(uri) 
 801                 && sibling.getLocalName().equals(nodeName)) {
 802                 list.add((Element)sibling);
 803             }
 804             sibling = sibling.getNextSibling();
 805         }
 806         return list.toArray(new Element[list.size()]);
 807     }
 808 
 809     /**
 810      * @param signatureElement
 811      * @param inputSet
 812      * @return nodes with the constrain
 813      */
 814     public static Set<Node> excludeNodeFromSet(Node signatureElement, Set<Node> inputSet) {
 815         Set<Node> resultSet = new HashSet<Node>();
 816         Iterator<Node> iterator = inputSet.iterator();
 817 
 818         while (iterator.hasNext()) {
 819             Node inputNode = iterator.next();
 820 
 821             if (!XMLUtils.isDescendantOrSelf(signatureElement, inputNode)) {
 822                 resultSet.add(inputNode);
 823             }
 824         }
 825         return resultSet;
 826     }
 827     
 828     /**
 829      * Method getStrFromNode
 830      *
 831      * @param xpathnode
 832      * @return the string for the node.
 833      */
 834     public static String getStrFromNode(Node xpathnode) {
 835         if (xpathnode.getNodeType() == Node.TEXT_NODE) {
 836             // we iterate over all siblings of the context node because eventually,
 837             // the text is "polluted" with pi's or comments
 838             StringBuilder sb = new StringBuilder();
 839 
 840             for (Node currentSibling = xpathnode.getParentNode().getFirstChild();
 841                 currentSibling != null;
 842                 currentSibling = currentSibling.getNextSibling()) {
 843                 if (currentSibling.getNodeType() == Node.TEXT_NODE) {
 844                     sb.append(((Text) currentSibling).getData());
 845                 }
 846             }
 847 
 848             return sb.toString();
 849         } else if (xpathnode.getNodeType() == Node.ATTRIBUTE_NODE) {
 850             return ((Attr) xpathnode).getNodeValue();
 851         } else if (xpathnode.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) {
 852             return ((ProcessingInstruction) xpathnode).getNodeValue();
 853         }
 854 
 855         return null;
 856     }
 857 
 858     /**
 859      * Returns true if the descendantOrSelf is on the descendant-or-self axis
 860      * of the context node.
 861      *
 862      * @param ctx
 863      * @param descendantOrSelf
 864      * @return true if the node is descendant
 865      */
 866     public static boolean isDescendantOrSelf(Node ctx, Node descendantOrSelf) {
 867         if (ctx == descendantOrSelf) {
 868             return true;
 869         }
 870 
 871         Node parent = descendantOrSelf;
 872 
 873         while (true) {
 874             if (parent == null) {
 875                 return false;
 876             }
 877 
 878             if (parent == ctx) {
 879                 return true;
 880             }
 881 
 882             if (parent.getNodeType() == Node.ATTRIBUTE_NODE) {
 883                 parent = ((Attr) parent).getOwnerElement();
 884             } else {
 885                 parent = parent.getParentNode();
 886             }
 887         }
 888     }
 889 
 890     public static boolean ignoreLineBreaks() {
 891         return ignoreLineBreaks;
 892     }
 893     
 894     /**
 895      * Returns the attribute value for the attribute with the specified name.
 896      * Returns null if there is no such attribute, or 
 897      * the empty string if the attribute value is empty.
 898      *
 899      * <p>This works around a limitation of the DOM
 900      * <code>Element.getAttributeNode</code> method, which does not distinguish
 901      * between an unspecified attribute and an attribute with a value of
 902      * "" (it returns "" for both cases).
 903      *
 904      * @param elem the element containing the attribute
 905      * @param name the name of the attribute
 906      * @return the attribute value (may be null if unspecified)
 907      */
 908     public static String getAttributeValue(Element elem, String name) {
 909         Attr attr = elem.getAttributeNodeNS(null, name);
 910         return (attr == null) ? null : attr.getValue();
 911     }
 912     
 913     /**
 914      * This method is a tree-search to help prevent against wrapping attacks. It checks that no
 915      * two Elements have ID Attributes that match the "value" argument, if this is the case then
 916      * "false" is returned. Note that a return value of "true" does not necessarily mean that
 917      * a matching Element has been found, just that no wrapping attack has been detected.
 918      */
 919     public static boolean protectAgainstWrappingAttack(Node startNode, String value) {
 920         Node startParent = startNode.getParentNode();
 921         Node processedNode = null;
 922         Element foundElement = null;
 923         
 924         String id = value.trim();
 925         if (id.charAt(0) == '#') {
 926             id = id.substring(1);
 927         }
 928 
 929         while (startNode != null) {
 930             if (startNode.getNodeType() == Node.ELEMENT_NODE) {
 931                 Element se = (Element) startNode;
 932                 
 933                 NamedNodeMap attributes = se.getAttributes();
 934                 if (attributes != null) {
 935                     for (int i = 0; i < attributes.getLength(); i++) {
 936                         Attr attr = (Attr)attributes.item(i);
 937                         if (attr.isId() && id.equals(attr.getValue())) {
 938                             if (foundElement == null) {
 939                                 // Continue searching to find duplicates
 940                                 foundElement = attr.getOwnerElement();
 941                             } else {
 942                                 log.log(java.util.logging.Level.FINE, "Multiple elements with the same 'Id' attribute value!");
 943                                 return false;
 944                             }
 945                         }
 946                     }
 947                 }
 948             }
 949 
 950             processedNode = startNode;
 951             startNode = startNode.getFirstChild();
 952 
 953             // no child, this node is done.
 954             if (startNode == null) {
 955                 // close node processing, get sibling
 956                 startNode = processedNode.getNextSibling();
 957             }
 958             
 959             // no more siblings, get parent, all children
 960             // of parent are processed.
 961             while (startNode == null) {
 962                 processedNode = processedNode.getParentNode();
 963                 if (processedNode == startParent) {
 964                     return true;
 965                 }
 966                 // close parent node processing (processed node now)
 967                 startNode = processedNode.getNextSibling();
 968             }
 969         }
 970         return true;
 971     }
 972     
 973     /**
 974      * This method is a tree-search to help prevent against wrapping attacks. It checks that no other
 975      * Element than the given "knownElement" argument has an ID attribute that matches the "value" 
 976      * argument, which is the ID value of "knownElement". If this is the case then "false" is returned.
 977      */
 978     public static boolean protectAgainstWrappingAttack(
 979         Node startNode, Element knownElement, String value
 980     ) {
 981         Node startParent = startNode.getParentNode();
 982         Node processedNode = null;
 983         
 984         String id = value.trim();
 985         if (id.charAt(0) == '#') {
 986             id = id.substring(1);
 987         }
 988 
 989         while (startNode != null) {
 990             if (startNode.getNodeType() == Node.ELEMENT_NODE) {
 991                 Element se = (Element) startNode;
 992                 
 993                 NamedNodeMap attributes = se.getAttributes();
 994                 if (attributes != null) {
 995                     for (int i = 0; i < attributes.getLength(); i++) {
 996                         Attr attr = (Attr)attributes.item(i);
 997                         if (attr.isId() && id.equals(attr.getValue()) && se != knownElement) {
 998                             log.log(java.util.logging.Level.FINE, "Multiple elements with the same 'Id' attribute value!");
 999                             return false;
1000                         }
1001                     }
1002                 }
1003             }
1004 
1005             processedNode = startNode;
1006             startNode = startNode.getFirstChild();
1007 
1008             // no child, this node is done.
1009             if (startNode == null) {
1010                 // close node processing, get sibling
1011                 startNode = processedNode.getNextSibling();
1012             }
1013             
1014             // no more siblings, get parent, all children
1015             // of parent are processed.
1016             while (startNode == null) {
1017                 processedNode = processedNode.getParentNode();
1018                 if (processedNode == startParent) {
1019                     return true;
1020                 }
1021                 // close parent node processing (processed node now)
1022                 startNode = processedNode.getNextSibling();
1023             }
1024         }
1025         return true;
1026     }
1027 
1028 }