1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /* 6 * Copyright 1999-2004 The Apache Software Foundation. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 * 20 */ 21 package com.sun.org.apache.xml.internal.security.signature; 22 23 import java.io.IOException; 24 import java.io.StringWriter; 25 import java.io.Writer; 26 import java.util.Arrays; 27 import java.util.Set; 28 29 import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare; 30 import com.sun.org.apache.xml.internal.security.utils.XMLUtils; 31 import org.w3c.dom.Attr; 32 import org.w3c.dom.Comment; 33 import org.w3c.dom.Document; 34 import org.w3c.dom.Element; 35 import org.w3c.dom.NamedNodeMap; 36 import org.w3c.dom.Node; 37 import org.w3c.dom.ProcessingInstruction; 38 39 /** 40 * Class XMLSignatureInputDebugger 41 * 42 * @author $Author: mullan $ 43 * @version $Revision: 1.3 $ 44 */ 45 public class XMLSignatureInputDebugger { 46 47 48 49 /** Field _xmlSignatureInput */ 50 private Set<Node> _xpathNodeSet; 51 52 private Set<String> _inclusiveNamespaces; 53 54 /** Field _doc */ 55 private Document _doc = null; 56 57 /** Field _writer */ 58 private Writer _writer = null; 59 60 // J- 61 // public static final String HTMLPrefix = "<!DOCTYPE HTML PUBLIC 62 // \"-//W3C//DTD HTML 4.01 Transitional//EN\"><html><head><style 63 // type=\"text/css\"><!-- .INCLUDED { color: #000000; background-color: 64 // #FFFFFF; font-weight: bold; } .EXCLUDED { color: #666666; 65 // background-color: #999999; } .INCLUDEDINCLUSIVENAMESPACE { color: 66 // #0000FF; background-color: #FFFFFF; font-weight: bold; font-style: 67 // italic; } .EXCLUDEDINCLUSIVENAMESPACE { color: #0000FF; background-color: 68 // #999999; font-style: italic; } --> </style> </head><body 69 // bgcolor=\"#999999\"><pre>"; 70 /** The HTML Prefix* */ 71 static final String HTMLPrefix = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n" 72 + "<html>\n" 73 + "<head>\n" 74 + "<title>Caninical XML node set</title>\n" 75 + "<style type=\"text/css\">\n" 76 + "<!-- \n" 77 + ".INCLUDED { \n" 78 + " color: #000000; \n" 79 + " background-color: \n" 80 + " #FFFFFF; \n" 81 + " font-weight: bold; } \n" 82 + ".EXCLUDED { \n" 83 + " color: #666666; \n" 84 + " background-color: \n" 85 + " #999999; } \n" 86 + ".INCLUDEDINCLUSIVENAMESPACE { \n" 87 + " color: #0000FF; \n" 88 + " background-color: #FFFFFF; \n" 89 + " font-weight: bold; \n" 90 + " font-style: italic; } \n" 91 + ".EXCLUDEDINCLUSIVENAMESPACE { \n" 92 + " color: #0000FF; \n" 93 + " background-color: #999999; \n" 94 + " font-style: italic; } \n" 95 + "--> \n" 96 + "</style> \n" 97 + "</head>\n" 98 + "<body bgcolor=\"#999999\">\n" 99 + "<h1>Explanation of the output</h1>\n" 100 + "<p>The following text contains the nodeset of the given Reference before it is canonicalized. There exist four different styles to indicate how a given node is treated.</p>\n" 101 + "<ul>\n" 102 + "<li class=\"INCLUDED\">A node which is in the node set is labeled using the INCLUDED style.</li>\n" 103 + "<li class=\"EXCLUDED\">A node which is <em>NOT</em> in the node set is labeled EXCLUDED style.</li>\n" 104 + "<li class=\"INCLUDEDINCLUSIVENAMESPACE\">A namespace which is in the node set AND in the InclusiveNamespaces PrefixList is labeled using the INCLUDEDINCLUSIVENAMESPACE style.</li>\n" 105 + "<li class=\"EXCLUDEDINCLUSIVENAMESPACE\">A namespace which is in NOT the node set AND in the InclusiveNamespaces PrefixList is labeled using the INCLUDEDINCLUSIVENAMESPACE style.</li>\n" 106 + "</ul>\n" + "<h1>Output</h1>\n" + "<pre>\n"; 107 108 /** HTML Suffix * */ 109 static final String HTMLSuffix = "</pre></body></html>"; 110 111 static final String HTMLExcludePrefix = "<span class=\"EXCLUDED\">"; 112 113 static final String HTMLExcludeSuffix = "</span>"; 114 115 static final String HTMLIncludePrefix = "<span class=\"INCLUDED\">"; 116 117 static final String HTMLIncludeSuffix = "</span>"; 118 119 static final String HTMLIncludedInclusiveNamespacePrefix = "<span class=\"INCLUDEDINCLUSIVENAMESPACE\">"; 120 121 static final String HTMLIncludedInclusiveNamespaceSuffix = "</span>"; 122 123 static final String HTMLExcludedInclusiveNamespacePrefix = "<span class=\"EXCLUDEDINCLUSIVENAMESPACE\">"; 124 125 static final String HTMLExcludedInclusiveNamespaceSuffix = "</span>"; 126 127 private static final int NODE_BEFORE_DOCUMENT_ELEMENT = -1; 128 129 private static final int NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT = 0; 130 131 private static final int NODE_AFTER_DOCUMENT_ELEMENT = 1; 132 133 static final AttrCompare ATTR_COMPARE = new AttrCompare(); 134 135 // J+ 136 private XMLSignatureInputDebugger() { 137 // do nothing 138 } 139 140 /** 141 * Constructor XMLSignatureInputDebugger 142 * 143 * @param xmlSignatureInput the signatur to pretty print 144 */ 145 public XMLSignatureInputDebugger( 146 XMLSignatureInput xmlSignatureInput) { 147 148 if (!xmlSignatureInput.isNodeSet()) { 149 this._xpathNodeSet = null; 150 } else { 151 this._xpathNodeSet = xmlSignatureInput._inputNodeSet; 152 } 153 } 154 155 /** 156 * Constructor XMLSignatureInputDebugger 157 * 158 * @param xmlSignatureInput the signatur to pretty print 159 * @param inclusiveNamespace 160 */ 161 public XMLSignatureInputDebugger( 162 XMLSignatureInput xmlSignatureInput, Set<String> inclusiveNamespace) { 163 164 this(xmlSignatureInput); 165 166 this._inclusiveNamespaces = inclusiveNamespace; 167 } 168 169 /** 170 * Method getHTMLRepresentation 171 * 172 * @return The HTML Representation. 173 * @throws XMLSignatureException 174 */ 175 public String getHTMLRepresentation() throws XMLSignatureException { 176 177 if ((this._xpathNodeSet == null) || (this._xpathNodeSet.size() == 0)) { 178 return HTMLPrefix + "<blink>no node set, sorry</blink>" 179 + HTMLSuffix; 180 } 181 182 { 183 184 // get only a single node as anchor to fetch the owner document 185 Node n = this._xpathNodeSet.iterator().next(); 186 187 this._doc = XMLUtils.getOwnerDocument(n); 188 } 189 190 try { 191 this._writer = new StringWriter(); 192 193 this.canonicalizeXPathNodeSet(this._doc); 194 this._writer.close(); 195 196 return this._writer.toString(); 197 } catch (IOException ex) { 198 throw new XMLSignatureException("empty", ex); 199 } finally { 200 this._xpathNodeSet = null; 201 this._doc = null; 202 this._writer = null; 203 } 204 } 205 206 /** 207 * Method canonicalizeXPathNodeSet 208 * 209 * @param currentNode 210 * @throws XMLSignatureException 211 * @throws IOException 212 */ 213 private void canonicalizeXPathNodeSet(Node currentNode) 214 throws XMLSignatureException, IOException { 215 216 int currentNodeType = currentNode.getNodeType(); 217 switch (currentNodeType) { 218 219 case Node.DOCUMENT_TYPE_NODE: 220 default: 221 break; 222 223 case Node.ENTITY_NODE: 224 case Node.NOTATION_NODE: 225 case Node.DOCUMENT_FRAGMENT_NODE: 226 case Node.ATTRIBUTE_NODE: 227 throw new XMLSignatureException("empty"); 228 case Node.DOCUMENT_NODE: 229 this._writer.write(HTMLPrefix); 230 231 for (Node currentChild = currentNode.getFirstChild(); currentChild != null; currentChild = currentChild 232 .getNextSibling()) { 233 this.canonicalizeXPathNodeSet(currentChild); 234 } 235 236 this._writer.write(HTMLSuffix); 237 break; 238 239 case Node.COMMENT_NODE: 240 if (this._xpathNodeSet.contains(currentNode)) { 241 this._writer.write(HTMLIncludePrefix); 242 } else { 243 this._writer.write(HTMLExcludePrefix); 244 } 245 246 int position = getPositionRelativeToDocumentElement(currentNode); 247 248 if (position == NODE_AFTER_DOCUMENT_ELEMENT) { 249 this._writer.write("\n"); 250 } 251 252 this.outputCommentToWriter((Comment) currentNode); 253 254 if (position == NODE_BEFORE_DOCUMENT_ELEMENT) { 255 this._writer.write("\n"); 256 } 257 258 if (this._xpathNodeSet.contains(currentNode)) { 259 this._writer.write(HTMLIncludeSuffix); 260 } else { 261 this._writer.write(HTMLExcludeSuffix); 262 } 263 break; 264 265 case Node.PROCESSING_INSTRUCTION_NODE: 266 if (this._xpathNodeSet.contains(currentNode)) { 267 this._writer.write(HTMLIncludePrefix); 268 } else { 269 this._writer.write(HTMLExcludePrefix); 270 } 271 272 position = getPositionRelativeToDocumentElement(currentNode); 273 274 if (position == NODE_AFTER_DOCUMENT_ELEMENT) { 275 this._writer.write("\n"); 276 } 277 278 this.outputPItoWriter((ProcessingInstruction) currentNode); 279 280 if (position == NODE_BEFORE_DOCUMENT_ELEMENT) { 281 this._writer.write("\n"); 282 } 283 284 if (this._xpathNodeSet.contains(currentNode)) { 285 this._writer.write(HTMLIncludeSuffix); 286 } else { 287 this._writer.write(HTMLExcludeSuffix); 288 } 289 break; 290 291 case Node.TEXT_NODE: 292 case Node.CDATA_SECTION_NODE: 293 if (this._xpathNodeSet.contains(currentNode)) { 294 this._writer.write(HTMLIncludePrefix); 295 } else { 296 this._writer.write(HTMLExcludePrefix); 297 } 298 299 outputTextToWriter(currentNode.getNodeValue()); 300 301 for (Node nextSibling = currentNode.getNextSibling(); (nextSibling != null) 302 && ((nextSibling.getNodeType() == Node.TEXT_NODE) || (nextSibling 303 .getNodeType() == Node.CDATA_SECTION_NODE)); nextSibling = nextSibling 304 .getNextSibling()) { 305 306 /* 307 * The XPath data model allows to select only the first of a 308 * sequence of mixed text and CDATA nodes. But we must output 309 * them all, so we must search: 310 * 311 * @see http://nagoya.apache.org/bugzilla/show_bug.cgi?id=6329 312 */ 313 this.outputTextToWriter(nextSibling.getNodeValue()); 314 } 315 316 if (this._xpathNodeSet.contains(currentNode)) { 317 this._writer.write(HTMLIncludeSuffix); 318 } else { 319 this._writer.write(HTMLExcludeSuffix); 320 } 321 break; 322 323 case Node.ELEMENT_NODE: 324 Element currentElement = (Element) currentNode; 325 326 if (this._xpathNodeSet.contains(currentNode)) { 327 this._writer.write(HTMLIncludePrefix); 328 } else { 329 this._writer.write(HTMLExcludePrefix); 330 } 331 332 this._writer.write("<"); 333 this._writer.write(currentElement.getTagName()); 334 335 if (this._xpathNodeSet.contains(currentNode)) { 336 this._writer.write(HTMLIncludeSuffix); 337 } else { 338 this._writer.write(HTMLExcludeSuffix); 339 } 340 341 // we output all Attrs which are available 342 NamedNodeMap attrs = currentElement.getAttributes(); 343 int attrsLength = attrs.getLength(); 344 Attr attrs2[] = new Attr[attrsLength]; 345 346 for (int i = 0; i < attrsLength; i++) { 347 attrs2[i] = (Attr)attrs.item(i); 348 } 349 350 Arrays.sort(attrs2, ATTR_COMPARE); 351 Object attrs3[] = attrs2; 352 353 for (int i = 0; i < attrsLength; i++) { 354 Attr a = (Attr) attrs3[i]; 355 boolean included = this._xpathNodeSet.contains(a); 356 boolean inclusive = this._inclusiveNamespaces.contains(a 357 .getName()); 358 359 if (included) { 360 if (inclusive) { 361 362 // included and inclusive 363 this._writer 364 .write(HTMLIncludedInclusiveNamespacePrefix); 365 } else { 366 367 // included and not inclusive 368 this._writer.write(HTMLIncludePrefix); 369 } 370 } else { 371 if (inclusive) { 372 373 // excluded and inclusive 374 this._writer 375 .write(HTMLExcludedInclusiveNamespacePrefix); 376 } else { 377 378 // excluded and not inclusive 379 this._writer.write(HTMLExcludePrefix); 380 } 381 } 382 383 this.outputAttrToWriter(a.getNodeName(), a.getNodeValue()); 384 385 if (included) { 386 if (inclusive) { 387 388 // included and inclusive 389 this._writer 390 .write(HTMLIncludedInclusiveNamespaceSuffix); 391 } else { 392 393 // included and not inclusive 394 this._writer.write(HTMLIncludeSuffix); 395 } 396 } else { 397 if (inclusive) { 398 399 // excluded and inclusive 400 this._writer 401 .write(HTMLExcludedInclusiveNamespaceSuffix); 402 } else { 403 404 // excluded and not inclusive 405 this._writer.write(HTMLExcludeSuffix); 406 } 407 } 408 } 409 410 if (this._xpathNodeSet.contains(currentNode)) { 411 this._writer.write(HTMLIncludePrefix); 412 } else { 413 this._writer.write(HTMLExcludePrefix); 414 } 415 416 this._writer.write(">"); 417 418 if (this._xpathNodeSet.contains(currentNode)) { 419 this._writer.write(HTMLIncludeSuffix); 420 } else { 421 this._writer.write(HTMLExcludeSuffix); 422 } 423 424 // traversal 425 for (Node currentChild = currentNode.getFirstChild(); currentChild != null; currentChild = currentChild 426 .getNextSibling()) { 427 this.canonicalizeXPathNodeSet(currentChild); 428 } 429 430 if (this._xpathNodeSet.contains(currentNode)) { 431 this._writer.write(HTMLIncludePrefix); 432 } else { 433 this._writer.write(HTMLExcludePrefix); 434 } 435 436 this._writer.write("</"); 437 this._writer.write(currentElement.getTagName()); 438 this._writer.write(">"); 439 440 if (this._xpathNodeSet.contains(currentNode)) { 441 this._writer.write(HTMLIncludeSuffix); 442 } else { 443 this._writer.write(HTMLExcludeSuffix); 444 } 445 break; 446 } 447 } 448 449 /** 450 * Checks whether a Comment or ProcessingInstruction is before or after the 451 * document element. This is needed for prepending or appending "\n"s. 452 * 453 * @param currentNode 454 * comment or pi to check 455 * @return NODE_BEFORE_DOCUMENT_ELEMENT, 456 * NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT or 457 * NODE_AFTER_DOCUMENT_ELEMENT 458 * @see #NODE_BEFORE_DOCUMENT_ELEMENT 459 * @see #NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT 460 * @see #NODE_AFTER_DOCUMENT_ELEMENT 461 */ 462 private int getPositionRelativeToDocumentElement(Node currentNode) { 463 464 if (currentNode == null) { 465 return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; 466 } 467 468 Document doc = currentNode.getOwnerDocument(); 469 470 if (currentNode.getParentNode() != doc) { 471 return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; 472 } 473 474 Element documentElement = doc.getDocumentElement(); 475 476 if (documentElement == null) { 477 return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; 478 } 479 480 if (documentElement == currentNode) { 481 return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; 482 } 483 491 } 492 493 /** 494 * Normalizes an {@link Attr}ibute value 495 * 496 * The string value of the node is modified by replacing 497 * <UL> 498 * <LI>all ampersands (&) with <CODE>&amp;</CODE></LI> 499 * <LI>all open angle brackets (<) with <CODE>&lt;</CODE></LI> 500 * <LI>all quotation mark characters with <CODE>&quot;</CODE></LI> 501 * <LI>and the whitespace characters <CODE>#x9</CODE>, #xA, and #xD, 502 * with character references. The character references are written in 503 * uppercase hexadecimal with no leading zeroes (for example, <CODE>#xD</CODE> 504 * is represented by the character reference <CODE>&#xD;</CODE>)</LI> 505 * </UL> 506 * 507 * @param name 508 * @param value 509 * @throws IOException 510 */ 511 private void outputAttrToWriter(String name, String value) 512 throws IOException { 513 514 this._writer.write(" "); 515 this._writer.write(name); 516 this._writer.write("=\""); 517 518 int length = value.length(); 519 520 for (int i = 0; i < length; i++) { 521 char c = value.charAt(i); 522 523 switch (c) { 524 525 case '&': 526 this._writer.write("&amp;"); 527 break; 528 529 case '<': 530 this._writer.write("&lt;"); 531 break; 532 533 case '"': 534 this._writer.write("&quot;"); 535 break; 536 537 case 0x09: // '\t' 538 this._writer.write("&#x9;"); 539 break; 540 541 case 0x0A: // '\n' 542 this._writer.write("&#xA;"); 543 break; 544 545 case 0x0D: // '\r' 546 this._writer.write("&#xD;"); 547 break; 548 549 default: 550 this._writer.write(c); 551 break; 552 } 553 } 554 555 this._writer.write("\""); 556 } 557 558 /** 559 * Normalizes a {@link org.w3c.dom.Comment} value 560 * 561 * @param currentPI 562 * @throws IOException 563 */ 564 private void outputPItoWriter(ProcessingInstruction currentPI) 565 throws IOException { 566 567 if (currentPI == null) { 568 return; 569 } 570 571 this._writer.write("<?"); 572 573 String target = currentPI.getTarget(); 574 int length = target.length(); 575 576 for (int i = 0; i < length; i++) { 577 char c = target.charAt(i); 578 579 switch (c) { 580 581 case 0x0D: 582 this._writer.write("&#xD;"); 583 break; 584 585 case ' ': 586 this._writer.write("·"); 587 break; 588 589 case '\n': 590 this._writer.write("¶\n"); 591 break; 592 593 default: 594 this._writer.write(c); 595 break; 596 } 597 } 598 599 String data = currentPI.getData(); 600 601 length = data.length(); 602 603 if (length > 0) { 604 this._writer.write(" "); 605 606 for (int i = 0; i < length; i++) { 607 char c = data.charAt(i); 608 609 switch (c) { 610 611 case 0x0D: 612 this._writer.write("&#xD;"); 613 break; 614 615 default: 616 this._writer.write(c); 617 break; 618 } 619 } 620 } 621 622 this._writer.write("?>"); 623 } 624 625 /** 626 * Method outputCommentToWriter 627 * 628 * @param currentComment 629 * @throws IOException 630 */ 631 private void outputCommentToWriter(Comment currentComment) 632 throws IOException { 633 634 if (currentComment == null) { 635 return; 636 } 637 638 this._writer.write("<!--"); 639 640 String data = currentComment.getData(); 641 int length = data.length(); 642 643 for (int i = 0; i < length; i++) { 644 char c = data.charAt(i); 645 646 switch (c) { 647 648 case 0x0D: 649 this._writer.write("&#xD;"); 650 break; 651 652 case ' ': 653 this._writer.write("·"); 654 break; 655 656 case '\n': 657 this._writer.write("¶\n"); 658 break; 659 660 default: 661 this._writer.write(c); 662 break; 663 } 664 } 665 666 this._writer.write("-->"); 667 } 668 669 /** 670 * Method outputTextToWriter 671 * 672 * @param text 673 * @throws IOException 674 */ 675 private void outputTextToWriter(String text) throws IOException { 676 677 if (text == null) { 678 return; 679 } 680 681 int length = text.length(); 682 683 for (int i = 0; i < length; i++) { 684 char c = text.charAt(i); 685 686 switch (c) { 687 688 case '&': 689 this._writer.write("&amp;"); 690 break; 691 692 case '<': 693 this._writer.write("&lt;"); 694 break; 695 696 case '>': 697 this._writer.write("&gt;"); 698 break; 699 700 case 0xD: 701 this._writer.write("&#xD;"); 702 break; 703 704 case ' ': 705 this._writer.write("·"); 706 break; 707 708 case '\n': 709 this._writer.write("¶\n"); 710 break; 711 712 default: 713 this._writer.write(c); 714 break; 715 } 716 } 717 } 718 } | 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.signature; 24 25 import java.io.IOException; 26 import java.io.StringWriter; 27 import java.io.Writer; 28 import java.util.Arrays; 29 import java.util.Set; 30 31 import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare; 32 import com.sun.org.apache.xml.internal.security.utils.XMLUtils; 33 import org.w3c.dom.Attr; 34 import org.w3c.dom.Comment; 35 import org.w3c.dom.Document; 36 import org.w3c.dom.Element; 37 import org.w3c.dom.NamedNodeMap; 38 import org.w3c.dom.Node; 39 import org.w3c.dom.ProcessingInstruction; 40 41 /** 42 * Class XMLSignatureInputDebugger 43 */ 44 public class XMLSignatureInputDebugger { 45 46 /** Field _xmlSignatureInput */ 47 private Set<Node> xpathNodeSet; 48 49 private Set<String> inclusiveNamespaces; 50 51 /** Field doc */ 52 private Document doc = null; 53 54 /** Field writer */ 55 private Writer writer = null; 56 57 /** The HTML Prefix* */ 58 static final String HTMLPrefix = 59 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n" 60 + "<html>\n" 61 + "<head>\n" 62 + "<title>Caninical XML node set</title>\n" 63 + "<style type=\"text/css\">\n" 64 + "<!-- \n" 65 + ".INCLUDED { \n" 66 + " color: #000000; \n" 67 + " background-color: \n" 68 + " #FFFFFF; \n" 69 + " font-weight: bold; } \n" 70 + ".EXCLUDED { \n" 71 + " color: #666666; \n" 72 + " background-color: \n" 73 + " #999999; } \n" 74 + ".INCLUDEDINCLUSIVENAMESPACE { \n" 75 + " color: #0000FF; \n" 76 + " background-color: #FFFFFF; \n" 77 + " font-weight: bold; \n" 78 + " font-style: italic; } \n" 79 + ".EXCLUDEDINCLUSIVENAMESPACE { \n" 80 + " color: #0000FF; \n" 81 + " background-color: #999999; \n" 82 + " font-style: italic; } \n" 83 + "--> \n" 84 + "</style> \n" 85 + "</head>\n" 86 + "<body bgcolor=\"#999999\">\n" 87 + "<h1>Explanation of the output</h1>\n" 88 + "<p>The following text contains the nodeset of the given Reference before it is canonicalized. There exist four different styles to indicate how a given node is treated.</p>\n" 89 + "<ul>\n" 90 + "<li class=\"INCLUDED\">A node which is in the node set is labeled using the INCLUDED style.</li>\n" 91 + "<li class=\"EXCLUDED\">A node which is <em>NOT</em> in the node set is labeled EXCLUDED style.</li>\n" 92 + "<li class=\"INCLUDEDINCLUSIVENAMESPACE\">A namespace which is in the node set AND in the InclusiveNamespaces PrefixList is labeled using the INCLUDEDINCLUSIVENAMESPACE style.</li>\n" 93 + "<li class=\"EXCLUDEDINCLUSIVENAMESPACE\">A namespace which is in NOT the node set AND in the InclusiveNamespaces PrefixList is labeled using the INCLUDEDINCLUSIVENAMESPACE style.</li>\n" 94 + "</ul>\n" + "<h1>Output</h1>\n" + "<pre>\n"; 95 96 /** HTML Suffix * */ 97 static final String HTMLSuffix = "</pre></body></html>"; 98 99 static final String HTMLExcludePrefix = "<span class=\"EXCLUDED\">"; 100 101 static final String HTMLIncludePrefix = "<span class=\"INCLUDED\">"; 102 103 static final String HTMLIncludeOrExcludeSuffix = "</span>"; 104 105 static final String HTMLIncludedInclusiveNamespacePrefix = "<span class=\"INCLUDEDINCLUSIVENAMESPACE\">"; 106 107 static final String HTMLExcludedInclusiveNamespacePrefix = "<span class=\"EXCLUDEDINCLUSIVENAMESPACE\">"; 108 109 private static final int NODE_BEFORE_DOCUMENT_ELEMENT = -1; 110 111 private static final int NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT = 0; 112 113 private static final int NODE_AFTER_DOCUMENT_ELEMENT = 1; 114 115 static final AttrCompare ATTR_COMPARE = new AttrCompare(); 116 117 /** 118 * Constructor XMLSignatureInputDebugger 119 * 120 * @param xmlSignatureInput the signature to pretty print 121 */ 122 public XMLSignatureInputDebugger(XMLSignatureInput xmlSignatureInput) { 123 if (!xmlSignatureInput.isNodeSet()) { 124 this.xpathNodeSet = null; 125 } else { 126 this.xpathNodeSet = xmlSignatureInput.getInputNodeSet(); 127 } 128 } 129 130 /** 131 * Constructor XMLSignatureInputDebugger 132 * 133 * @param xmlSignatureInput the signatur to pretty print 134 * @param inclusiveNamespace 135 */ 136 public XMLSignatureInputDebugger( 137 XMLSignatureInput xmlSignatureInput, 138 Set<String> inclusiveNamespace 139 ) { 140 this(xmlSignatureInput); 141 this.inclusiveNamespaces = inclusiveNamespace; 142 } 143 144 /** 145 * Method getHTMLRepresentation 146 * 147 * @return The HTML Representation. 148 * @throws XMLSignatureException 149 */ 150 public String getHTMLRepresentation() throws XMLSignatureException { 151 if ((this.xpathNodeSet == null) || (this.xpathNodeSet.size() == 0)) { 152 return HTMLPrefix + "<blink>no node set, sorry</blink>" + HTMLSuffix; 153 } 154 155 // get only a single node as anchor to fetch the owner document 156 Node n = this.xpathNodeSet.iterator().next(); 157 158 this.doc = XMLUtils.getOwnerDocument(n); 159 160 try { 161 this.writer = new StringWriter(); 162 163 this.canonicalizeXPathNodeSet(this.doc); 164 this.writer.close(); 165 166 return this.writer.toString(); 167 } catch (IOException ex) { 168 throw new XMLSignatureException("empty", ex); 169 } finally { 170 this.xpathNodeSet = null; 171 this.doc = null; 172 this.writer = null; 173 } 174 } 175 176 /** 177 * Method canonicalizeXPathNodeSet 178 * 179 * @param currentNode 180 * @throws XMLSignatureException 181 * @throws IOException 182 */ 183 private void canonicalizeXPathNodeSet(Node currentNode) 184 throws XMLSignatureException, IOException { 185 186 int currentNodeType = currentNode.getNodeType(); 187 switch (currentNodeType) { 188 189 190 case Node.ENTITY_NODE: 191 case Node.NOTATION_NODE: 192 case Node.DOCUMENT_FRAGMENT_NODE: 193 case Node.ATTRIBUTE_NODE: 194 throw new XMLSignatureException("empty"); 195 case Node.DOCUMENT_NODE: 196 this.writer.write(HTMLPrefix); 197 198 for (Node currentChild = currentNode.getFirstChild(); 199 currentChild != null; currentChild = currentChild.getNextSibling()) { 200 this.canonicalizeXPathNodeSet(currentChild); 201 } 202 203 this.writer.write(HTMLSuffix); 204 break; 205 206 case Node.COMMENT_NODE: 207 if (this.xpathNodeSet.contains(currentNode)) { 208 this.writer.write(HTMLIncludePrefix); 209 } else { 210 this.writer.write(HTMLExcludePrefix); 211 } 212 213 int position = getPositionRelativeToDocumentElement(currentNode); 214 215 if (position == NODE_AFTER_DOCUMENT_ELEMENT) { 216 this.writer.write("\n"); 217 } 218 219 this.outputCommentToWriter((Comment) currentNode); 220 221 if (position == NODE_BEFORE_DOCUMENT_ELEMENT) { 222 this.writer.write("\n"); 223 } 224 225 this.writer.write(HTMLIncludeOrExcludeSuffix); 226 break; 227 228 case Node.PROCESSING_INSTRUCTION_NODE: 229 if (this.xpathNodeSet.contains(currentNode)) { 230 this.writer.write(HTMLIncludePrefix); 231 } else { 232 this.writer.write(HTMLExcludePrefix); 233 } 234 235 position = getPositionRelativeToDocumentElement(currentNode); 236 237 if (position == NODE_AFTER_DOCUMENT_ELEMENT) { 238 this.writer.write("\n"); 239 } 240 241 this.outputPItoWriter((ProcessingInstruction) currentNode); 242 243 if (position == NODE_BEFORE_DOCUMENT_ELEMENT) { 244 this.writer.write("\n"); 245 } 246 247 this.writer.write(HTMLIncludeOrExcludeSuffix); 248 break; 249 250 case Node.TEXT_NODE: 251 case Node.CDATA_SECTION_NODE: 252 if (this.xpathNodeSet.contains(currentNode)) { 253 this.writer.write(HTMLIncludePrefix); 254 } else { 255 this.writer.write(HTMLExcludePrefix); 256 } 257 258 outputTextToWriter(currentNode.getNodeValue()); 259 260 for (Node nextSibling = currentNode.getNextSibling(); 261 (nextSibling != null) 262 && ((nextSibling.getNodeType() == Node.TEXT_NODE) 263 || (nextSibling.getNodeType() == Node.CDATA_SECTION_NODE)); 264 nextSibling = nextSibling.getNextSibling()) { 265 /* 266 * The XPath data model allows to select only the first of a 267 * sequence of mixed text and CDATA nodes. But we must output 268 * them all, so we must search: 269 * 270 * @see http://nagoya.apache.org/bugzilla/show_bug.cgi?id=6329 271 */ 272 this.outputTextToWriter(nextSibling.getNodeValue()); 273 } 274 275 this.writer.write(HTMLIncludeOrExcludeSuffix); 276 break; 277 278 case Node.ELEMENT_NODE: 279 Element currentElement = (Element) currentNode; 280 281 if (this.xpathNodeSet.contains(currentNode)) { 282 this.writer.write(HTMLIncludePrefix); 283 } else { 284 this.writer.write(HTMLExcludePrefix); 285 } 286 287 this.writer.write("<"); 288 this.writer.write(currentElement.getTagName()); 289 290 this.writer.write(HTMLIncludeOrExcludeSuffix); 291 292 // we output all Attrs which are available 293 NamedNodeMap attrs = currentElement.getAttributes(); 294 int attrsLength = attrs.getLength(); 295 Attr attrs2[] = new Attr[attrsLength]; 296 297 for (int i = 0; i < attrsLength; i++) { 298 attrs2[i] = (Attr)attrs.item(i); 299 } 300 301 Arrays.sort(attrs2, ATTR_COMPARE); 302 Object attrs3[] = attrs2; 303 304 for (int i = 0; i < attrsLength; i++) { 305 Attr a = (Attr) attrs3[i]; 306 boolean included = this.xpathNodeSet.contains(a); 307 boolean inclusive = this.inclusiveNamespaces.contains(a.getName()); 308 309 if (included) { 310 if (inclusive) { 311 // included and inclusive 312 this.writer.write(HTMLIncludedInclusiveNamespacePrefix); 313 } else { 314 // included and not inclusive 315 this.writer.write(HTMLIncludePrefix); 316 } 317 } else { 318 if (inclusive) { 319 // excluded and inclusive 320 this.writer.write(HTMLExcludedInclusiveNamespacePrefix); 321 } else { 322 // excluded and not inclusive 323 this.writer.write(HTMLExcludePrefix); 324 } 325 } 326 327 this.outputAttrToWriter(a.getNodeName(), a.getNodeValue()); 328 this.writer.write(HTMLIncludeOrExcludeSuffix); 329 } 330 331 if (this.xpathNodeSet.contains(currentNode)) { 332 this.writer.write(HTMLIncludePrefix); 333 } else { 334 this.writer.write(HTMLExcludePrefix); 335 } 336 337 this.writer.write(">"); 338 339 this.writer.write(HTMLIncludeOrExcludeSuffix); 340 341 // traversal 342 for (Node currentChild = currentNode.getFirstChild(); 343 currentChild != null; 344 currentChild = currentChild.getNextSibling()) { 345 this.canonicalizeXPathNodeSet(currentChild); 346 } 347 348 if (this.xpathNodeSet.contains(currentNode)) { 349 this.writer.write(HTMLIncludePrefix); 350 } else { 351 this.writer.write(HTMLExcludePrefix); 352 } 353 354 this.writer.write("</"); 355 this.writer.write(currentElement.getTagName()); 356 this.writer.write(">"); 357 358 this.writer.write(HTMLIncludeOrExcludeSuffix); 359 break; 360 361 case Node.DOCUMENT_TYPE_NODE: 362 default: 363 break; 364 } 365 } 366 367 /** 368 * Checks whether a Comment or ProcessingInstruction is before or after the 369 * document element. This is needed for prepending or appending "\n"s. 370 * 371 * @param currentNode 372 * comment or pi to check 373 * @return NODE_BEFORE_DOCUMENT_ELEMENT, 374 * NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT or 375 * NODE_AFTER_DOCUMENT_ELEMENT 376 * @see #NODE_BEFORE_DOCUMENT_ELEMENT 377 * @see #NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT 378 * @see #NODE_AFTER_DOCUMENT_ELEMENT 379 */ 380 private int getPositionRelativeToDocumentElement(Node currentNode) { 381 if (currentNode == null) { 382 return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; 383 } 384 385 Document doc = currentNode.getOwnerDocument(); 386 387 if (currentNode.getParentNode() != doc) { 388 return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; 389 } 390 391 Element documentElement = doc.getDocumentElement(); 392 393 if (documentElement == null) { 394 return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; 395 } 396 397 if (documentElement == currentNode) { 398 return NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT; 399 } 400 408 } 409 410 /** 411 * Normalizes an {@link Attr}ibute value 412 * 413 * The string value of the node is modified by replacing 414 * <UL> 415 * <LI>all ampersands (&) with <CODE>&amp;</CODE></LI> 416 * <LI>all open angle brackets (<) with <CODE>&lt;</CODE></LI> 417 * <LI>all quotation mark characters with <CODE>&quot;</CODE></LI> 418 * <LI>and the whitespace characters <CODE>#x9</CODE>, #xA, and #xD, 419 * with character references. The character references are written in 420 * uppercase hexadecimal with no leading zeroes (for example, <CODE>#xD</CODE> 421 * is represented by the character reference <CODE>&#xD;</CODE>)</LI> 422 * </UL> 423 * 424 * @param name 425 * @param value 426 * @throws IOException 427 */ 428 private void outputAttrToWriter(String name, String value) throws IOException { 429 this.writer.write(" "); 430 this.writer.write(name); 431 this.writer.write("=\""); 432 433 int length = value.length(); 434 435 for (int i = 0; i < length; i++) { 436 char c = value.charAt(i); 437 438 switch (c) { 439 440 case '&': 441 this.writer.write("&amp;"); 442 break; 443 444 case '<': 445 this.writer.write("&lt;"); 446 break; 447 448 case '"': 449 this.writer.write("&quot;"); 450 break; 451 452 case 0x09: // '\t' 453 this.writer.write("&#x9;"); 454 break; 455 456 case 0x0A: // '\n' 457 this.writer.write("&#xA;"); 458 break; 459 460 case 0x0D: // '\r' 461 this.writer.write("&#xD;"); 462 break; 463 464 default: 465 this.writer.write(c); 466 break; 467 } 468 } 469 470 this.writer.write("\""); 471 } 472 473 /** 474 * Normalizes a {@link org.w3c.dom.Comment} value 475 * 476 * @param currentPI 477 * @throws IOException 478 */ 479 private void outputPItoWriter(ProcessingInstruction currentPI) throws IOException { 480 481 if (currentPI == null) { 482 return; 483 } 484 485 this.writer.write("<?"); 486 487 String target = currentPI.getTarget(); 488 int length = target.length(); 489 490 for (int i = 0; i < length; i++) { 491 char c = target.charAt(i); 492 493 switch (c) { 494 495 case 0x0D: 496 this.writer.write("&#xD;"); 497 break; 498 499 case ' ': 500 this.writer.write("·"); 501 break; 502 503 case '\n': 504 this.writer.write("¶\n"); 505 break; 506 507 default: 508 this.writer.write(c); 509 break; 510 } 511 } 512 513 String data = currentPI.getData(); 514 515 length = data.length(); 516 517 if (length > 0) { 518 this.writer.write(" "); 519 520 for (int i = 0; i < length; i++) { 521 char c = data.charAt(i); 522 523 switch (c) { 524 525 case 0x0D: 526 this.writer.write("&#xD;"); 527 break; 528 529 default: 530 this.writer.write(c); 531 break; 532 } 533 } 534 } 535 536 this.writer.write("?>"); 537 } 538 539 /** 540 * Method outputCommentToWriter 541 * 542 * @param currentComment 543 * @throws IOException 544 */ 545 private void outputCommentToWriter(Comment currentComment) throws IOException { 546 547 if (currentComment == null) { 548 return; 549 } 550 551 this.writer.write("<!--"); 552 553 String data = currentComment.getData(); 554 int length = data.length(); 555 556 for (int i = 0; i < length; i++) { 557 char c = data.charAt(i); 558 559 switch (c) { 560 561 case 0x0D: 562 this.writer.write("&#xD;"); 563 break; 564 565 case ' ': 566 this.writer.write("·"); 567 break; 568 569 case '\n': 570 this.writer.write("¶\n"); 571 break; 572 573 default: 574 this.writer.write(c); 575 break; 576 } 577 } 578 579 this.writer.write("-->"); 580 } 581 582 /** 583 * Method outputTextToWriter 584 * 585 * @param text 586 * @throws IOException 587 */ 588 private void outputTextToWriter(String text) throws IOException { 589 if (text == null) { 590 return; 591 } 592 593 int length = text.length(); 594 595 for (int i = 0; i < length; i++) { 596 char c = text.charAt(i); 597 598 switch (c) { 599 600 case '&': 601 this.writer.write("&amp;"); 602 break; 603 604 case '<': 605 this.writer.write("&lt;"); 606 break; 607 608 case '>': 609 this.writer.write("&gt;"); 610 break; 611 612 case 0xD: 613 this.writer.write("&#xD;"); 614 break; 615 616 case ' ': 617 this.writer.write("·"); 618 break; 619 620 case '\n': 621 this.writer.write("¶\n"); 622 break; 623 624 default: 625 this.writer.write(c); 626 break; 627 } 628 } 629 } 630 } |