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.c14n.implementations;
  24 
  25 import java.io.ByteArrayOutputStream;
  26 import java.io.IOException;
  27 import java.io.OutputStream;
  28 import java.io.UnsupportedEncodingException;
  29 import java.util.ArrayList;
  30 import java.util.HashMap;
  31 import java.util.Iterator;
  32 import java.util.List;
  33 import java.util.ListIterator;
  34 import java.util.Map;
  35 import java.util.Set;
  36 
  37 import javax.xml.parsers.DocumentBuilder;
  38 import javax.xml.parsers.DocumentBuilderFactory;
  39 import javax.xml.parsers.ParserConfigurationException;
  40 
  41 import com.sun.org.apache.xml.internal.security.c14n.CanonicalizationException;
  42 import com.sun.org.apache.xml.internal.security.c14n.CanonicalizerSpi;
  43 import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
  44 import com.sun.org.apache.xml.internal.security.signature.NodeFilter;
  45 import com.sun.org.apache.xml.internal.security.signature.XMLSignatureInput;
  46 import com.sun.org.apache.xml.internal.security.utils.Constants;
  47 import com.sun.org.apache.xml.internal.security.utils.UnsyncByteArrayOutputStream;
  48 import com.sun.org.apache.xml.internal.security.utils.XMLUtils;
  49 import org.w3c.dom.Attr;
  50 import org.w3c.dom.Comment;
  51 import org.w3c.dom.Element;
  52 import org.w3c.dom.NamedNodeMap;
  53 import org.w3c.dom.Node;
  54 import org.w3c.dom.ProcessingInstruction;
  55 import org.xml.sax.SAXException;
  56 
  57 /**
  58  * Abstract base class for canonicalization algorithms.
  59  *
  60  * @author Christian Geuer-Pollmann <geuerp@apache.org>
  61  */
  62 public abstract class CanonicalizerBase extends CanonicalizerSpi {
  63     public static final String XML = "xml";
  64     public static final String XMLNS = "xmlns";
  65     
  66     protected static final AttrCompare COMPARE = new AttrCompare();
  67     protected static final Attr nullNode;
  68     
  69     private static final byte[] END_PI = {'?','>'};
  70     private static final byte[] BEGIN_PI = {'<','?'};
  71     private static final byte[] END_COMM = {'-','-','>'};
  72     private static final byte[] BEGIN_COMM = {'<','!','-','-'};
  73     private static final byte[] XA = {'&','#','x','A',';'};
  74     private static final byte[] X9 = {'&','#','x','9',';'};
  75     private static final byte[] QUOT = {'&','q','u','o','t',';'};
  76     private static final byte[] XD = {'&','#','x','D',';'};
  77     private static final byte[] GT = {'&','g','t',';'};
  78     private static final byte[] LT = {'&','l','t',';'};
  79     private static final byte[] END_TAG = {'<','/'};
  80     private static final byte[] AMP = {'&','a','m','p',';'};
  81     private static final byte[] equalsStr = {'=','\"'};
  82     
  83     protected static final int NODE_BEFORE_DOCUMENT_ELEMENT = -1;
  84     protected static final int NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT = 0;
  85     protected static final int NODE_AFTER_DOCUMENT_ELEMENT = 1;
  86     
  87     static {
  88         // The null xmlns definition.
  89         try {
  90             DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
  91             nullNode = documentBuilder.newDocument().createAttributeNS(Constants.NamespaceSpecNS, XMLNS);
  92             nullNode.setValue("");
  93         } catch (Exception e) {
  94             throw new RuntimeException("Unable to create nullNode: " + e);
  95         }
  96     }
  97 
  98     private List<NodeFilter> nodeFilter;
  99 
 100     private boolean includeComments;  
 101     private Set<Node> xpathNodeSet;
 102     /**
 103      * The node to be skipped/excluded from the DOM tree 
 104      * in subtree canonicalizations.
 105      */
 106     private Node excludeNode;
 107     private OutputStream writer = new ByteArrayOutputStream();
 108 
 109     /**
 110      * Constructor CanonicalizerBase
 111      *
 112      * @param includeComments
 113      */
 114     public CanonicalizerBase(boolean includeComments) {
 115         this.includeComments = includeComments;
 116     }
 117 
 118     /**
 119      * Method engineCanonicalizeSubTree
 120      * @inheritDoc
 121      * @param rootNode
 122      * @throws CanonicalizationException
 123      */
 124     public byte[] engineCanonicalizeSubTree(Node rootNode)
 125         throws CanonicalizationException {
 126         return engineCanonicalizeSubTree(rootNode, (Node)null);
 127     }
 128     
 129     /**
 130      * Method engineCanonicalizeXPathNodeSet
 131      * @inheritDoc
 132      * @param xpathNodeSet
 133      * @throws CanonicalizationException
 134      */
 135     public byte[] engineCanonicalizeXPathNodeSet(Set<Node> xpathNodeSet)
 136         throws CanonicalizationException {
 137         this.xpathNodeSet = xpathNodeSet;
 138         return engineCanonicalizeXPathNodeSetInternal(XMLUtils.getOwnerDocument(this.xpathNodeSet));
 139     }
 140 
 141     /**
 142      * Canonicalizes a Subtree node.
 143      * @param input the root of the subtree to canicalize
 144      * @return The canonicalize stream.
 145      * @throws CanonicalizationException
 146      */
 147     public byte[] engineCanonicalize(XMLSignatureInput input) throws CanonicalizationException {
 148         try {
 149             if (input.isExcludeComments()) {
 150                 includeComments = false;
 151             }
 152             if (input.isOctetStream()) {
 153                 return engineCanonicalize(input.getBytes());
 154             }
 155             if (input.isElement()) {
 156                 return engineCanonicalizeSubTree(input.getSubNode(), input.getExcludeNode());
 157             } else if (input.isNodeSet()) {
 158                 nodeFilter = input.getNodeFilters();
 159 
 160                 circumventBugIfNeeded(input);
 161 
 162                 if (input.getSubNode() != null) {
 163                     return engineCanonicalizeXPathNodeSetInternal(input.getSubNode());
 164                 } else {
 165                     return engineCanonicalizeXPathNodeSet(input.getNodeSet());
 166                 }
 167             }
 168             return null;
 169         } catch (CanonicalizationException ex) {
 170             throw new CanonicalizationException("empty", ex);
 171         } catch (ParserConfigurationException ex) {
 172             throw new CanonicalizationException("empty", ex);
 173         } catch (IOException ex) {
 174             throw new CanonicalizationException("empty", ex);
 175         } catch (SAXException ex) {
 176             throw new CanonicalizationException("empty", ex);
 177         }
 178     }
 179     
 180     /**
 181      * @param writer The writer to set.
 182      */
 183     public void setWriter(OutputStream writer) {
 184         this.writer = writer;
 185     }
 186 
 187     /**
 188      * Canonicalizes a Subtree node.
 189      * 
 190      * @param rootNode
 191      *            the root of the subtree to canonicalize
 192      * @param excludeNode
 193      *            a node to be excluded from the canonicalize operation
 194      * @return The canonicalize stream.
 195      * @throws CanonicalizationException
 196      */
 197     protected byte[] engineCanonicalizeSubTree(Node rootNode, Node excludeNode)
 198         throws CanonicalizationException {
 199         this.excludeNode = excludeNode;
 200         try {
 201             NameSpaceSymbTable ns = new NameSpaceSymbTable();
 202             int nodeLevel = NODE_BEFORE_DOCUMENT_ELEMENT;
 203             if (rootNode != null && Node.ELEMENT_NODE == rootNode.getNodeType()) {
 204                 //Fills the nssymbtable with the definitions of the parent of the root subnode
 205                 getParentNameSpaces((Element)rootNode, ns);
 206                 nodeLevel = NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT;
 207             }         
 208             this.canonicalizeSubTree(rootNode, ns, rootNode, nodeLevel);
 209             this.writer.flush();
 210             if (this.writer instanceof ByteArrayOutputStream) {
 211                 byte[] result = ((ByteArrayOutputStream)this.writer).toByteArray();
 212                 if (reset) {
 213                     ((ByteArrayOutputStream)this.writer).reset();        
 214                 } else {
 215                     this.writer.close();
 216                 }
 217                 return result;
 218             } else if (this.writer instanceof UnsyncByteArrayOutputStream) {
 219                 byte[] result = ((UnsyncByteArrayOutputStream)this.writer).toByteArray();
 220                 if (reset) {
 221                     ((UnsyncByteArrayOutputStream)this.writer).reset();        
 222                 } else {
 223                     this.writer.close();
 224                 }
 225                 return result;
 226             } else {
 227                 this.writer.close();
 228             }
 229             return null;
 230 
 231         } catch (UnsupportedEncodingException ex) {
 232             throw new CanonicalizationException("empty", ex);
 233         } catch (IOException ex) {
 234             throw new CanonicalizationException("empty", ex);
 235         } 
 236     }
 237 
 238 
 239     /**
 240      * Method canonicalizeSubTree, this function is a recursive one.
 241      *    
 242      * @param currentNode
 243      * @param ns 
 244      * @param endnode 
 245      * @throws CanonicalizationException
 246      * @throws IOException
 247      */
 248     protected final void canonicalizeSubTree(
 249         Node currentNode, NameSpaceSymbTable ns, Node endnode, int documentLevel
 250     ) throws CanonicalizationException, IOException {
 251         if (isVisibleInt(currentNode) == -1) {
 252             return;
 253         }
 254         Node sibling = null;
 255         Node parentNode = null;         
 256         final OutputStream writer = this.writer;    
 257         final Node excludeNode = this.excludeNode;
 258         final boolean includeComments = this.includeComments;
 259         Map<String, byte[]> cache = new HashMap<String, byte[]>();
 260         do {
 261             switch (currentNode.getNodeType()) {
 262 
 263             case Node.ENTITY_NODE :
 264             case Node.NOTATION_NODE :
 265             case Node.ATTRIBUTE_NODE :
 266                 // illegal node type during traversal
 267                 throw new CanonicalizationException("empty");
 268 
 269             case Node.DOCUMENT_FRAGMENT_NODE :
 270             case Node.DOCUMENT_NODE :
 271                 ns.outputNodePush();
 272                 sibling = currentNode.getFirstChild();
 273                 break;
 274 
 275             case Node.COMMENT_NODE :
 276                 if (includeComments) {
 277                     outputCommentToWriter((Comment) currentNode, writer, documentLevel);
 278                 }
 279                 break;
 280 
 281             case Node.PROCESSING_INSTRUCTION_NODE :
 282                 outputPItoWriter((ProcessingInstruction) currentNode, writer, documentLevel);
 283                 break;
 284 
 285             case Node.TEXT_NODE :
 286             case Node.CDATA_SECTION_NODE :
 287                 outputTextToWriter(currentNode.getNodeValue(), writer);
 288                 break;
 289 
 290             case Node.ELEMENT_NODE :
 291                 documentLevel = NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT;
 292                 if (currentNode == excludeNode) {
 293                     break;
 294                 }      
 295                 Element currentElement = (Element)currentNode;
 296                 //Add a level to the nssymbtable. So latter can be pop-back.
 297                 ns.outputNodePush();
 298                 writer.write('<');
 299                 String name = currentElement.getTagName();
 300                 UtfHelpper.writeByte(name, writer, cache);
 301 
 302                 Iterator<Attr> attrs = this.handleAttributesSubtree(currentElement, ns);
 303                 if (attrs != null) {
 304                     //we output all Attrs which are available
 305                     while (attrs.hasNext()) {
 306                         Attr attr = attrs.next();
 307                         outputAttrToWriter(attr.getNodeName(), attr.getNodeValue(), writer, cache);
 308                     }
 309                 }
 310                 writer.write('>');        
 311                 sibling = currentNode.getFirstChild(); 
 312                 if (sibling == null) {
 313                     writer.write(END_TAG);
 314                     UtfHelpper.writeStringToUtf8(name, writer);        
 315                     writer.write('>');
 316                     //We finished with this level, pop to the previous definitions.
 317                     ns.outputNodePop();
 318                     if (parentNode != null) {
 319                         sibling = currentNode.getNextSibling();
 320                     }
 321                 } else {
 322                     parentNode = currentElement;
 323                 }
 324                 break;
 325                 
 326             case Node.DOCUMENT_TYPE_NODE :
 327             default :
 328                 break;
 329             }
 330             while (sibling == null && parentNode != null) {                                                     
 331                 writer.write(END_TAG);
 332                 UtfHelpper.writeByte(((Element)parentNode).getTagName(), writer, cache);        
 333                 writer.write('>');
 334                 //We finished with this level, pop to the previous definitions.
 335                 ns.outputNodePop();
 336                 if (parentNode == endnode) {
 337                     return;
 338                 }
 339                 sibling = parentNode.getNextSibling();
 340                 parentNode = parentNode.getParentNode();
 341                 if (parentNode == null || Node.ELEMENT_NODE != parentNode.getNodeType()) {
 342                     documentLevel = NODE_AFTER_DOCUMENT_ELEMENT;
 343                     parentNode = null;
 344                 }                       
 345             }      
 346             if (sibling == null) {
 347                 return;
 348             }
 349             currentNode = sibling;      
 350             sibling = currentNode.getNextSibling();
 351         } while(true);
 352     }
 353 
 354 
 355     private byte[] engineCanonicalizeXPathNodeSetInternal(Node doc)
 356         throws CanonicalizationException {   
 357         try { 
 358             this.canonicalizeXPathNodeSet(doc, doc);
 359             this.writer.flush();
 360             if (this.writer instanceof ByteArrayOutputStream) {
 361                 byte[] sol = ((ByteArrayOutputStream)this.writer).toByteArray();
 362                 if (reset) {
 363                     ((ByteArrayOutputStream)this.writer).reset();
 364                 } else {
 365                     this.writer.close();
 366                 }
 367                 return sol;
 368             } else if (this.writer instanceof UnsyncByteArrayOutputStream) {
 369                 byte[] result = ((UnsyncByteArrayOutputStream)this.writer).toByteArray();
 370                 if (reset) {
 371                     ((UnsyncByteArrayOutputStream)this.writer).reset();        
 372                 } else {
 373                     this.writer.close();
 374                 }
 375                 return result;
 376             } else {
 377                 this.writer.close();
 378             }
 379             return null;
 380         } catch (UnsupportedEncodingException ex) {
 381             throw new CanonicalizationException("empty", ex);
 382         } catch (IOException ex) {
 383             throw new CanonicalizationException("empty", ex);
 384         } 
 385     }
 386 
 387     /**
 388      * Canonicalizes all the nodes included in the currentNode and contained in the 
 389      * xpathNodeSet field.
 390      *
 391      * @param currentNode
 392      * @param endnode
 393      * @throws CanonicalizationException
 394      * @throws IOException
 395      */
 396     protected final void canonicalizeXPathNodeSet(Node currentNode, Node endnode)
 397         throws CanonicalizationException, IOException {
 398         if (isVisibleInt(currentNode) == -1) {
 399             return;
 400         }
 401         boolean currentNodeIsVisible = false;     
 402         NameSpaceSymbTable ns = new NameSpaceSymbTable();
 403         if (currentNode != null && Node.ELEMENT_NODE == currentNode.getNodeType()) {
 404             getParentNameSpaces((Element)currentNode, ns);
 405         }
 406         if (currentNode == null) {
 407             return;
 408         }
 409         Node sibling = null;
 410         Node parentNode = null; 
 411         OutputStream writer = this.writer;
 412         int documentLevel = NODE_BEFORE_DOCUMENT_ELEMENT;
 413         Map<String, byte[]> cache = new HashMap<String, byte[]>();
 414         do {
 415             switch (currentNode.getNodeType()) {
 416 
 417             case Node.ENTITY_NODE :
 418             case Node.NOTATION_NODE :
 419             case Node.ATTRIBUTE_NODE :
 420                 // illegal node type during traversal
 421                 throw new CanonicalizationException("empty");
 422 
 423             case Node.DOCUMENT_FRAGMENT_NODE :
 424             case Node.DOCUMENT_NODE :
 425                 ns.outputNodePush();
 426                 sibling = currentNode.getFirstChild();
 427                 break;
 428 
 429             case Node.COMMENT_NODE :                    
 430                 if (this.includeComments && (isVisibleDO(currentNode, ns.getLevel()) == 1)) {
 431                     outputCommentToWriter((Comment) currentNode, writer, documentLevel);
 432                 }
 433                 break;
 434 
 435             case Node.PROCESSING_INSTRUCTION_NODE :
 436                 if (isVisible(currentNode)) {
 437                     outputPItoWriter((ProcessingInstruction) currentNode, writer, documentLevel);
 438                 }
 439                 break;
 440 
 441             case Node.TEXT_NODE :
 442             case Node.CDATA_SECTION_NODE :
 443                 if (isVisible(currentNode)) {
 444                     outputTextToWriter(currentNode.getNodeValue(), writer);
 445                     for (Node nextSibling = currentNode.getNextSibling();
 446                         (nextSibling != null) && ((nextSibling.getNodeType() == Node.TEXT_NODE)
 447                             || (nextSibling.getNodeType() == Node.CDATA_SECTION_NODE));
 448                         nextSibling = nextSibling.getNextSibling()) {
 449                         outputTextToWriter(nextSibling.getNodeValue(), writer);
 450                         currentNode = nextSibling;
 451                         sibling = currentNode.getNextSibling();
 452                     }
 453                 }
 454                 break;
 455 
 456             case Node.ELEMENT_NODE :
 457                 documentLevel = NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT;
 458                 Element currentElement = (Element) currentNode;
 459                 //Add a level to the nssymbtable. So latter can be pop-back.
 460                 String name = null;
 461                 int i = isVisibleDO(currentNode, ns.getLevel());
 462                 if (i == -1) {
 463                     sibling = currentNode.getNextSibling();
 464                     break;
 465                 }
 466                 currentNodeIsVisible = (i == 1);
 467                 if (currentNodeIsVisible) {
 468                     ns.outputNodePush();
 469                     writer.write('<');
 470                     name = currentElement.getTagName();
 471                     UtfHelpper.writeByte(name, writer, cache);
 472                 } else {
 473                     ns.push();
 474                 }
 475 
 476                 Iterator<Attr> attrs = handleAttributes(currentElement,ns);
 477                 if (attrs != null) {
 478                     //we output all Attrs which are available
 479                     while (attrs.hasNext()) {
 480                         Attr attr = attrs.next();
 481                         outputAttrToWriter(attr.getNodeName(), attr.getNodeValue(), writer, cache);
 482                     }
 483                 }
 484                 if (currentNodeIsVisible) {
 485                     writer.write('>');
 486                 }
 487                 sibling = currentNode.getFirstChild(); 
 488 
 489                 if (sibling == null) {
 490                     if (currentNodeIsVisible) {
 491                         writer.write(END_TAG);
 492                         UtfHelpper.writeByte(name, writer, cache);        
 493                         writer.write('>');
 494                         //We finished with this level, pop to the previous definitions.
 495                         ns.outputNodePop();
 496                     } else {
 497                         ns.pop();
 498                     }                           
 499                     if (parentNode != null) {
 500                         sibling = currentNode.getNextSibling();
 501                     }
 502                 } else {
 503                     parentNode = currentElement;
 504                 }                       
 505                 break;
 506                 
 507             case Node.DOCUMENT_TYPE_NODE :
 508             default :
 509                 break;
 510             }
 511             while (sibling == null && parentNode != null) {    
 512                 if (isVisible(parentNode)) {
 513                     writer.write(END_TAG);
 514                     UtfHelpper.writeByte(((Element)parentNode).getTagName(), writer, cache);        
 515                     writer.write('>');
 516                     //We finished with this level, pop to the previous definitions.
 517                     ns.outputNodePop();
 518                 } else {
 519                     ns.pop();
 520                 }
 521                 if (parentNode == endnode) {
 522                     return;
 523                 }
 524                 sibling = parentNode.getNextSibling();
 525                 parentNode = parentNode.getParentNode();   
 526                 if (parentNode == null || Node.ELEMENT_NODE != parentNode.getNodeType()) {
 527                     parentNode = null;
 528                     documentLevel = NODE_AFTER_DOCUMENT_ELEMENT;
 529                 }                       
 530             }      
 531             if (sibling == null) {
 532                 return;
 533             }
 534             currentNode = sibling;      
 535             sibling = currentNode.getNextSibling();  
 536         } while(true);
 537     }
 538     
 539     protected int isVisibleDO(Node currentNode, int level) {
 540         if (nodeFilter != null) {
 541             Iterator<NodeFilter> it = nodeFilter.iterator();
 542             while (it.hasNext()) {      
 543                 int i = (it.next()).isNodeIncludeDO(currentNode, level);
 544                 if (i != 1) {
 545                     return i;
 546                 }
 547             }
 548         }
 549         if ((this.xpathNodeSet != null) && !this.xpathNodeSet.contains(currentNode)) {
 550             return 0;
 551         }
 552         return 1;
 553     }
 554     
 555     protected int isVisibleInt(Node currentNode) {
 556         if (nodeFilter != null) {
 557             Iterator<NodeFilter> it = nodeFilter.iterator();
 558             while (it.hasNext()) {                      
 559                 int i = (it.next()).isNodeInclude(currentNode);
 560                 if (i != 1) {
 561                     return i;
 562                 }
 563             }
 564         }
 565         if ((this.xpathNodeSet != null) && !this.xpathNodeSet.contains(currentNode)) {
 566             return 0;
 567         }
 568         return 1;
 569     }
 570 
 571     protected boolean isVisible(Node currentNode) {
 572         if (nodeFilter != null) {
 573             Iterator<NodeFilter> it = nodeFilter.iterator();
 574             while (it.hasNext()) {                      
 575                 if (it.next().isNodeInclude(currentNode) != 1) {
 576                     return false;
 577                 }
 578             }
 579         }
 580         if ((this.xpathNodeSet != null) && !this.xpathNodeSet.contains(currentNode)) {
 581             return false;
 582         }
 583         return true;
 584     }
 585 
 586     protected void handleParent(Element e, NameSpaceSymbTable ns) {
 587         if (!e.hasAttributes() && e.getNamespaceURI() == null) {
 588             return;
 589         }
 590         NamedNodeMap attrs = e.getAttributes();
 591         int attrsLength = attrs.getLength();
 592         for (int i = 0; i < attrsLength; i++) {
 593             Attr attribute = (Attr) attrs.item(i);
 594             String NName = attribute.getLocalName();
 595             String NValue = attribute.getNodeValue();
 596             
 597             if (Constants.NamespaceSpecNS.equals(attribute.getNamespaceURI())
 598                 && (!XML.equals(NName) || !Constants.XML_LANG_SPACE_SpecNS.equals(NValue))) {
 599                 ns.addMapping(NName, NValue, attribute);
 600             }
 601         }
 602         if (e.getNamespaceURI() != null) {
 603             String NName = e.getPrefix();
 604             String NValue = e.getNamespaceURI();
 605             String Name;
 606             if (NName == null || NName.equals("")) {
 607                 NName = XMLNS;
 608                 Name = XMLNS;
 609             } else {
 610                 Name = XMLNS + ":" + NName;
 611             }
 612             Attr n = e.getOwnerDocument().createAttributeNS("http://www.w3.org/2000/xmlns/", Name);
 613             n.setValue(NValue);
 614             ns.addMapping(NName, NValue, n);
 615         }
 616     }
 617 
 618     /**
 619      * Adds to ns the definitions from the parent elements of el
 620      * @param el
 621      * @param ns
 622      */
 623     protected final void getParentNameSpaces(Element el, NameSpaceSymbTable ns)  {
 624         Node n1 = el.getParentNode();
 625         if (n1 == null || Node.ELEMENT_NODE != n1.getNodeType()) {
 626             return;
 627         }
 628         //Obtain all the parents of the element
 629         List<Element> parents = new ArrayList<Element>();
 630         Node parent = n1;
 631         while (parent != null && Node.ELEMENT_NODE == parent.getNodeType()) {
 632             parents.add((Element)parent);
 633             parent = parent.getParentNode();
 634         }
 635         //Visit them in reverse order.
 636         ListIterator<Element> it = parents.listIterator(parents.size());
 637         while (it.hasPrevious()) {
 638             Element ele = it.previous();
 639             handleParent(ele, ns);
 640         }
 641         parents.clear();
 642         Attr nsprefix;
 643         if (((nsprefix = ns.getMappingWithoutRendered(XMLNS)) != null) 
 644             && "".equals(nsprefix.getValue())) {
 645             ns.addMappingAndRender(XMLNS, "", nullNode);
 646         }
 647     }
 648     
 649     /**
 650      * Obtain the attributes to output for this node in XPathNodeSet c14n. 
 651      *
 652      * @param element
 653      * @param ns
 654      * @return the attributes nodes to output.
 655      * @throws CanonicalizationException
 656      */
 657     abstract Iterator<Attr> handleAttributes(Element element, NameSpaceSymbTable ns)
 658         throws CanonicalizationException;
 659 
 660     /**
 661      * Obtain the attributes to output for this node in a Subtree c14n.
 662      *
 663      * @param element
 664      * @param ns
 665      * @return the attributes nodes to output.
 666      * @throws CanonicalizationException
 667      */
 668     abstract Iterator<Attr> handleAttributesSubtree(Element element, NameSpaceSymbTable ns)
 669         throws CanonicalizationException;
 670 
 671     abstract void circumventBugIfNeeded(XMLSignatureInput input) 
 672         throws CanonicalizationException, ParserConfigurationException, IOException, SAXException;
 673 
 674     /**
 675      * Outputs an Attribute to the internal Writer.
 676      *
 677      * The string value of the node is modified by replacing
 678      * <UL>
 679      * <LI>all ampersands (&) with <CODE>&amp;amp;</CODE></LI>
 680      * <LI>all open angle brackets (<) with <CODE>&amp;lt;</CODE></LI>
 681      * <LI>all quotation mark characters with <CODE>&amp;quot;</CODE></LI>
 682      * <LI>and the whitespace characters <CODE>#x9</CODE>, #xA, and #xD, with character
 683      * references. The character references are written in uppercase
 684      * hexadecimal with no leading zeroes (for example, <CODE>#xD</CODE> is represented
 685      * by the character reference <CODE>&amp;#xD;</CODE>)</LI>
 686      * </UL>
 687      *
 688      * @param name
 689      * @param value
 690      * @param writer 
 691      * @throws IOException
 692      */
 693     protected static final void outputAttrToWriter(
 694         final String name, final String value, 
 695         final OutputStream writer, final Map<String, byte[]> cache
 696     ) throws IOException {
 697         writer.write(' ');
 698         UtfHelpper.writeByte(name, writer, cache);
 699         writer.write(equalsStr);
 700         byte[] toWrite;
 701         final int length = value.length();
 702         int i = 0;
 703         while (i < length) {        
 704             char c = value.charAt(i++);
 705 
 706             switch (c) {
 707 
 708             case '&' :
 709                 toWrite = AMP;
 710                 break;
 711 
 712             case '<' :
 713                 toWrite = LT;
 714                 break;
 715 
 716             case '"' :
 717                 toWrite = QUOT;
 718                 break;
 719 
 720             case 0x09 :    // '\t'
 721                 toWrite = X9;
 722                 break;
 723 
 724             case 0x0A :    // '\n'
 725                 toWrite = XA;
 726                 break;
 727 
 728             case 0x0D :    // '\r'
 729                 toWrite = XD;
 730                 break;
 731 
 732             default :
 733                 if (c < 0x80) {
 734                     writer.write(c);
 735                 } else {
 736                     UtfHelpper.writeCharToUtf8(c, writer);
 737                 }
 738                 continue;
 739             }
 740             writer.write(toWrite);
 741         }
 742 
 743         writer.write('\"');
 744     }
 745 
 746     /**
 747      * Outputs a PI to the internal Writer.
 748      *
 749      * @param currentPI
 750      * @param writer where to write the things
 751      * @throws IOException
 752      */
 753     protected void outputPItoWriter(
 754         ProcessingInstruction currentPI, OutputStream writer, int position
 755     ) throws IOException {        
 756         if (position == NODE_AFTER_DOCUMENT_ELEMENT) {
 757             writer.write('\n');
 758         }
 759         writer.write(BEGIN_PI);
 760 
 761         final String target = currentPI.getTarget();
 762         int length = target.length();
 763 
 764         for (int i = 0; i < length; i++) {         
 765             char c = target.charAt(i);
 766             if (c == 0x0D) {
 767                 writer.write(XD);
 768             } else {
 769                 if (c < 0x80) {
 770                     writer.write(c);
 771                 } else {
 772                     UtfHelpper.writeCharToUtf8(c, writer);
 773                 }     
 774             }
 775         }
 776 
 777         final String data = currentPI.getData();
 778 
 779         length = data.length();
 780 
 781         if (length > 0) {
 782             writer.write(' ');
 783 
 784             for (int i = 0; i < length; i++) {            
 785                 char c = data.charAt(i);
 786                 if (c == 0x0D) {
 787                     writer.write(XD);
 788                 } else {
 789                     UtfHelpper.writeCharToUtf8(c, writer);               
 790                 }
 791             }
 792         }
 793 
 794         writer.write(END_PI);
 795         if (position == NODE_BEFORE_DOCUMENT_ELEMENT) {
 796             writer.write('\n');
 797         }
 798     }
 799 
 800     /**
 801      * Method outputCommentToWriter
 802      *
 803      * @param currentComment
 804      * @param writer writer where to write the things
 805      * @throws IOException
 806      */
 807     protected void outputCommentToWriter(
 808         Comment currentComment, OutputStream writer, int position
 809     ) throws IOException {        
 810         if (position == NODE_AFTER_DOCUMENT_ELEMENT) {
 811             writer.write('\n');
 812         }
 813         writer.write(BEGIN_COMM);
 814 
 815         final String data = currentComment.getData();
 816         final int length = data.length();      
 817 
 818         for (int i = 0; i < length; i++) {         
 819             char c = data.charAt(i);
 820             if (c == 0x0D) {
 821                 writer.write(XD);
 822             } else {
 823                 if (c < 0x80) {
 824                     writer.write(c);
 825                 } else {
 826                     UtfHelpper.writeCharToUtf8(c, writer);
 827                 }    
 828             }      
 829         }
 830 
 831         writer.write(END_COMM);
 832         if (position == NODE_BEFORE_DOCUMENT_ELEMENT) {
 833             writer.write('\n');
 834         }
 835     }
 836 
 837     /**
 838      * Outputs a Text of CDATA section to the internal Writer.
 839      *
 840      * @param text
 841      * @param writer writer where to write the things
 842      * @throws IOException
 843      */
 844     protected static final void outputTextToWriter(
 845         final String text, final OutputStream writer
 846     ) throws IOException {
 847         final int length = text.length();
 848         byte[] toWrite;
 849         for (int i = 0; i < length; i++) {
 850             char c = text.charAt(i);
 851 
 852             switch (c) {
 853 
 854             case '&' :
 855                 toWrite = AMP;
 856                 break;
 857 
 858             case '<' :
 859                 toWrite = LT;
 860                 break;
 861 
 862             case '>' :
 863                 toWrite = GT;
 864                 break;
 865 
 866             case 0xD :
 867                 toWrite = XD;
 868                 break;
 869 
 870             default :
 871                 if (c < 0x80) {
 872                     writer.write(c);
 873                 } else {
 874                     UtfHelpper.writeCharToUtf8(c, writer);
 875                 }
 876                 continue;
 877             }
 878             writer.write(toWrite);
 879         }
 880     }
 881 
 882 }