1 28 29 package com.caucho.xsl; 30 31 import com.caucho.log.Log; 32 import com.caucho.util.CharBuffer; 33 import com.caucho.util.CharCursor; 34 import com.caucho.util.CharScanner; 35 import com.caucho.util.L10N; 36 import com.caucho.util.StringCharCursor; 37 import com.caucho.vfs.Encoding; 38 import com.caucho.vfs.Path; 39 import com.caucho.vfs.ReadStream; 40 import com.caucho.xml.*; 41 42 import org.w3c.dom.DOMException ; 43 import org.w3c.dom.Document ; 44 import org.w3c.dom.Element ; 45 import org.w3c.dom.Node ; 46 import org.w3c.dom.ProcessingInstruction ; 47 import org.w3c.dom.Text ; 48 49 import java.io.IOException ; 50 import java.util.ArrayList ; 51 import java.util.HashMap ; 52 import java.util.logging.Logger ; 53 54 64 class XslParser { 65 static final Logger log = Log.open(XslParser.class); 66 static final L10N L = new L10N(XslParser.class); 67 68 private static final String XSLNS = Generator.XSLNS; 69 private static final String XTPNS = Generator.XTPNS; 70 71 static final String XMLNS = "http://www.w3.org/2000/xmlns/"; 73 74 static HashMap <String ,String > _xslCommands; 75 static HashMap <String ,String > _xtpCommands; 76 77 public boolean strictXsl; 78 boolean rawText; 79 80 int line; 81 int textLine; 82 ReadStream is; 83 CharBuffer tag = new CharBuffer(); 84 CharBuffer text = new CharBuffer(); 85 QDocument xsl; 86 private int peek = -1; 87 private boolean seenCr; 88 private String defaultMode; 89 private HashMap <String ,String > _namespaces; 90 private HashMap macros = new HashMap (); 91 private boolean inTemplate; 92 93 XslParser() 94 { 95 } 96 97 100 Document parse(ReadStream is) 101 throws IOException , XslParseException 102 { 103 this.is = is; 104 105 line = 1; 106 defaultMode = null; 107 xsl = (QDocument) Xml.createDocument(); 108 if (is.getPath().getLastModified() > 0) { 109 ArrayList <Path> depends = new ArrayList <Path>(); 110 depends.add(is.getPath()); 111 xsl.setProperty(xsl.DEPENDS, depends); 112 } 113 114 xsl.setRootFilename(is.getPath().getURL()); 115 116 _namespaces = new HashMap <String ,String >(); 117 118 QNode top = (QNode) xsl.createDocumentFragment(); 119 top.setLocation(is.getPath().getURL(), is.getUserPath(), line, 0); 120 rawText = false; 121 122 String encoding = null; 123 int ch = read(); 124 125 if (ch != 0xef) { 126 } else if ((ch = read()) != 0xbb) { 127 peek = 0xbb; 128 ch = 0xef; 129 } else if ((ch = read()) != 0xbf) { 130 throw error(L.l("Expected 0xbf in UTF-8 header")); 131 } else { 132 is.setEncoding("UTF-8"); 133 ch = read(); 134 } 135 136 if (ch == '<') { 137 ch = read(); 138 if (ch == '?') { 139 ProcessingInstruction pi = parsePi(); 140 if (pi.getNodeName().equals("xml")) { 141 encoding = XmlUtil.getPIAttribute(pi.getNodeValue(), "encoding"); 142 if (encoding != null) 143 is.setEncoding(encoding); 144 } 145 else 146 top.appendChild(pi); 147 ch = read(); 148 } else { 149 peek = ch; 150 ch = '<'; 151 } 152 } 153 154 parseNode(top, "", true, ch); 155 156 QElement elt = null; 157 for (Node node = top.getFirstChild(); 158 node != null; 159 node = node.getNextSibling()) { 160 if (node.getNodeType() == Node.ELEMENT_NODE && 161 node.getNodeName().equals("xsl:stylesheet")) { 162 if (elt != null) 163 throw error(L.l("xsl:stylesheet must be sole top element")); 164 elt = (QElement) node; 165 } 166 } 167 if (elt == null) { 168 elt = (QElement) xsl.createElementNS(XSLNS, "xsl:stylesheet"); 169 elt.setAttribute("version", "1.0"); 170 elt.setLocation(is.getURL(), is.getUserPath(), 1, 0); 171 elt.setAttribute("resin:stylescript", "true"); 172 173 Element out = xsl.createElementNS(XSLNS, "xsl:output"); 174 178 elt.appendChild(out); 179 elt.appendChild(top); 180 } 181 182 184 if (encoding != null) { 185 Element out = xsl.createElementNS(XSLNS, "xsl:output"); 186 out.setAttribute("encoding", encoding); 187 elt.insertBefore(out, elt.getFirstChild()); 188 } 189 190 xsl.appendChild(elt); 191 192 197 198 return xsl; 199 } 200 201 206 private void parseNode(Node parent, String tagEnd, 207 boolean isSpecial, int ch) 208 throws IOException , XslParseException 209 { 210 boolean hasContent = false; 211 212 text.clear(); 213 if (tagEnd == ">>" && (ch == '\n' || ch == '\r')) 214 ch = read(); 215 216 while (ch >= 0) { 217 switch (ch) { 218 case '\\': 219 hasContent = true; 220 ch = read(); 221 if (ch == '<') { 222 addText('<'); 223 ch = read(); 224 } 225 else 226 addText('\\'); 227 break; 228 229 case '<': 230 hasContent = true; 231 ch = read(); 232 233 if (ch == '/') { 234 ch = readTag(read()); 235 String tag = this.tag.toString(); 236 if (tag.equals(tagEnd)) { 237 ch = skipWhitespace(ch); 238 if (ch != '>') 239 throw error(L.l("expected `{0}' at {1}", ">", badChar(ch))); 240 addText(parent); 241 if (tag.equals("xsl:template")) 242 inTemplate = false; 243 return; 244 } 245 else if (rawText) { 246 addText("</" + tag + ">"); 247 ch = read(); 248 break; 249 } 250 else { 251 throw error(L.l("`</{0}>' has no matching open tag", tag)); 252 } 253 } else if (ch == '#') { 254 addText(parent); 255 ch = parseScriptlet(parent); 256 break; 257 } else if (ch == '?') { 258 addText(parent); 259 ProcessingInstruction pi = parsePi(); 260 parent.appendChild(pi); 261 ch = read(); 262 break; 263 } else if (ch == '!') { 264 addText(parent); 265 ch = parseDecl(parent); 266 break; 267 } else if (ch == '{') { 268 addText(parent); 269 parseValueOf(parent); 270 ch = read(); 271 break; 272 } 273 274 ch = readTag(ch); 275 String tag = this.tag.toString(); 276 277 if (! rawText && ! tag.equals("") || 280 tag.startsWith("xsl:") || 281 tag.startsWith("jsp:") || tag.startsWith("xtp:") || 282 macros.get(tag) != null) { 283 addText(parent); 284 285 parseElement(parent, tag, ch, isSpecial); 286 287 ch = read(); 288 } 289 else { 291 addText("<"); 292 addText(tag); 293 } 294 break; 295 296 case '>': 297 int ch1 = read(); 298 if (ch1 == '>' && tagEnd == ">>") { 299 if (text.length() > 0 && text.charAt(text.length() - 1) == '\n') 300 text.setLength(text.length() - 1); 301 if (text.length() > 0 && text.charAt(text.length() - 1) == '\r') 302 text.setLength(text.length() - 1); 303 if (! hasContent) { 304 Element elt = xsl.createElementNS(XSLNS, "xsl:text"); 305 parent.appendChild(elt); 306 addText(elt); 307 } 308 else 309 addText(parent); 310 return; 311 } 312 else { 313 hasContent = true; 314 addText('>'); 315 ch = ch1; 316 } 317 break; 318 319 case '$': 320 hasContent = true; 321 ch = read(); 322 if (ch == '$') { 323 addText('$'); 324 ch = read(); 325 } 326 else if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z') { 327 String name = parseName(ch); 328 addText(parent); 329 text.clear(); 330 331 ch = parseExtension(parent, name); 332 } 333 else if (ch == '(') { 334 addText(parent); 335 Element elt = xsl.createElementNS(XSLNS, "xsl:value-of"); 336 CharBuffer test = CharBuffer.allocate(); 337 lexToRparen(test); 338 elt.setAttribute("select", test.close()); 339 parent.appendChild(elt); 340 ch = read(); 341 } 342 else { 343 addText('$'); 344 ch = read(); 345 } 346 break; 347 348 case ' ': case '\t': case '\n': case '\r': 349 addText((char) ch); 350 ch = read(); 351 break; 352 353 case '&': 354 ch = parseEntityReference(); 355 break; 356 357 default: 358 hasContent = true; 359 if (isSpecial) { 360 parseSpecial(parent, ch); 361 ch = read(); 362 } 363 else { 364 addText((char) ch); 365 ch = read(); 366 } 367 break; 368 } 369 } 370 371 addText(parent); 372 373 if (! tagEnd.equals("")) 374 throw error(L.l("expected close of `{0}' (open at {1})", 375 tagEnd, ((CauchoNode) parent).getLine())); 376 } 377 378 388 private Element parseElement(Node parent, String name, 389 int ch, boolean isSpecial) 390 throws IOException , XslParseException 391 { 392 HashMap <String ,String > oldNamespaces = _namespaces; 393 394 QElement element = null; 395 396 int p = name.indexOf(':'); 397 if (p >= 0) { 398 String prefix = name.substring(0, p); 399 String uri = _namespaces.get(prefix); 400 401 if (uri != null) 402 element = (QElement) xsl.createElementNS(uri, name); 403 else if (prefix.equals("xsl")) 404 element = (QElement) xsl.createElementNS(XSLNS, name); 405 } 406 407 try { 408 if (element == null) 409 element = (QElement) xsl.createElement(name); 410 } catch (DOMException e) { 411 throw error(e); 412 } 413 414 element.setLocation(is.getURL(), is.getUserPath(), line, 0); 415 416 ch = parseAttributes(null, element, ch, false); 417 418 if (name.equals("xsl:stylesheet")) { 419 if (element.getAttribute("parsed-content").equals("false")) { 420 rawText = true; 421 Element child = xsl.createElementNS(XSLNS, "xsl:output"); 422 child.setAttribute("disable-output-escaping", "yes"); 423 element.appendChild(child); 424 } 425 } 426 427 if (rawText && (name.startsWith("xsl") || name.startsWith("xtp")) && 428 element.getAttribute("xml:space").equals("")) 429 element.setAttribute("xml:space", "default"); 430 431 if (name.equals("xsl:template")) { 432 inTemplate = true; 433 String macroName = element.getAttribute("name"); 434 if (! macroName.equals("")) 435 macros.put(macroName, macroName); 436 } 437 438 String oldMode = defaultMode; 439 if (name.equals("xtp:mode")) { 440 defaultMode = element.getAttribute("mode"); 441 } 442 else { 443 parent.appendChild(element); 444 parent = element; 445 } 446 447 if (ch == '>') { 448 parseNode(parent, name, isSpecial && name.equals("xsl:stylesheet"), 449 read()); 450 } else if (ch == '/') { 451 if ((ch = read()) != '>') 452 throw error(L.l("expected `{0}' at {1}", ">", badChar(ch))); 453 } else 454 throw error(L.l("expected `{0}' at {1}", ">", badChar(ch))); 455 456 defaultMode = oldMode; 457 _namespaces = oldNamespaces; 458 459 return element; 460 } 461 462 465 private int parseEntityReference() 466 throws IOException , XslParseException 467 { 468 int ch = read(); 469 470 if (ch == '#') { 471 int code = 0; 472 473 ch = read(); 474 475 if (ch == 'x') { 476 for (ch = read(); ch > 0 && ch != ';'; ch = read()) { 477 if (ch >= '0' && ch <= '9') 478 code = 16 * code + ch - '0'; 479 else if (ch >= 'a' && ch <= 'f') 480 code = 16 * code + ch - 'a' + 10; 481 else if (ch >= 'A' && ch <= 'F') 482 code = 16 * code + ch - 'A' + 10; 483 else 484 break; 485 } 486 487 if (ch == ';') { 488 addText((char) code); 489 return read(); 490 } 491 else { 492 addText("&#x"); 493 addText(String.valueOf(code)); 494 return ch; 495 } 496 } 497 else { 498 for (; ch >= '0' && ch <= '9'; ch = read()) { 499 code = 10 * code + ch - '0'; 500 } 501 } 502 503 if (ch == ';') { 504 addText((char) code); 505 return read(); 506 } 507 else { 508 addText("&#"); 509 addText(String.valueOf(code)); 510 return ch; 511 } 512 } 513 514 CharBuffer cb = CharBuffer.allocate(); 515 for (; ch >= 'a' && ch <= 'z'; ch = read()) 516 cb.append((char) ch); 517 518 if (ch != ';') { 519 addText('&'); 520 addText(cb.close()); 521 } 522 else if (cb.matches("lt")) { 523 addText('<'); 524 return read(); 525 } 526 else if (cb.matches("gt")) { 527 addText('>'); 528 return read(); 529 } 530 else if (cb.matches("amp")) { 531 addText('&'); 532 return read(); 533 } 534 else if (cb.matches("quot")) { 535 addText('"'); 536 return read(); 537 } 538 else if (cb.matches("apos")) { 539 addText('\''); 540 return read(); 541 } 542 else { 543 addText('&'); 544 addText(cb.close()); 545 } 546 547 return ch; 548 } 549 552 private int parseScriptlet(Node parent) 553 throws IOException , XslParseException 554 { 555 String filename = is.getUserPath(); 556 int line = this.line; 557 int ch = read(); 558 559 QNode node; 560 if (ch == '=') { 561 node = (QNode) xsl.createElementNS(XTPNS, "xtp:expression"); 562 ch = read(); 563 } 564 else if (ch == '!') { 565 node = (QNode) xsl.createElementNS(XTPNS, "xtp:declaration"); 566 ch = read(); 567 } 568 else if (ch == '@') { 569 parseDirective(parent); 570 return read(); 571 } else 572 node = (QNode) xsl.createElementNS(XTPNS, "xtp:scriptlet"); 573 node.setLocation(is.getURL(), is.getUserPath(), line, 0); 574 parent.appendChild(node); 575 576 text.clear(); 577 while (ch >= 0) { 578 if (ch == '#') { 579 ch = read(); 580 if (ch == '>') 581 break; 582 else 583 addText('#'); 584 } else { 585 addText((char) ch); 586 ch = read(); 587 } 588 } 589 590 node.appendChild(xsl.createTextNode(text.toString())); 591 text.clear(); 592 593 return read(); 594 } 595 596 599 private void parseDirective(Node parent) 600 throws IOException , XslParseException 601 { 602 int ch; 603 604 ch = skipWhitespace(read()); 605 ch = readTag(ch); 606 String name = tag.toString(); 607 if (! name.equals("page") && ! name.equals("cache")) 608 throw error(L.l("unknown directive `{0}'", name)); 609 610 QElement elt = (QElement) xsl.createElementNS(XTPNS, "xtp:directive." + name); 611 elt.setLocation(is.getURL(), is.getUserPath(), line, 0); 612 parent.appendChild(elt); 613 614 ch = parseAttributes(parent, elt, ch, true); 615 616 if (ch != '#') 617 throw error(L.l("expected `{0}' at {1}", "#", badChar(ch))); 618 if ((ch = read()) != '>') 619 throw error(L.l("expected `{0}' at {1}", ">", badChar(ch))); 620 621 if (name.equals("page")) { 622 String contentType = elt.getAttribute("contentType"); 623 if (! contentType.equals("")) 624 parseContentType(parent, contentType); 625 } 626 } 627 628 private int parseStatement(Node parent, int ch) 629 throws IOException , XslParseException 630 { 631 ch = skipWhitespace(ch); 632 633 if (ch == '$') { 634 ch = read(); 635 636 if (XmlChar.isNameStart(ch)) { 637 String name = parseName(ch); 638 639 return parseExtension(parent, name); 640 } 641 else if (ch == '(') { 642 Element elt = xsl.createElementNS(XSLNS, "xsl:value-of"); 643 CharBuffer test = CharBuffer.allocate(); 644 lexToRparen(test); 645 elt.setAttribute("select", test.close()); 646 parent.appendChild(elt); 647 return read(); 648 } 649 else 650 throw error(L.l("expected statement at {0}", badChar(ch))); 651 } 652 else if (ch == '<') { 653 parseBlock(parent, ch); 654 return read(); 655 } 656 else if (ch == ';') 657 return read(); 658 else 659 throw error(L.l("expected statement at {0}", badChar(ch))); 660 } 661 662 private int parseExtension(Node parent, String name) 663 throws IOException , XslParseException 664 { 665 int ch = read(); 666 667 if (name.equals("if")) 668 return parseIf(parent, ch); 669 670 String arg = (String ) _xslCommands.get(name); 671 672 if (arg != null) { 673 QElement elt = (QElement) xsl.createElementNS(XSLNS, "xsl:" + name); 674 elt.setLocation(is.getURL(), is.getUserPath(), line, 0); 675 parent.appendChild(elt); 676 677 ch = skipWhitespace(ch); 678 679 if (ch == '(') { 680 parseArgs(elt, arg); 681 682 ch = skipWhitespace(read()); 683 } 684 685 return parseStatement(elt, ch); 686 } 687 688 arg = (String ) _xtpCommands.get(name); 689 if (arg != null) { 690 QElement elt = (QElement) xsl.createElement("xtp:" + name); 691 elt.setLocation(is.getURL(), is.getUserPath(), line, 0); 692 parent.appendChild(elt); 693 694 ch = skipWhitespace(ch); 695 696 if (ch == '(') { 697 parseArgs(elt, arg); 698 699 ch = skipWhitespace(read()); 700 } 701 702 return parseStatement(elt, ch); 703 } 704 705 ch = skipWhitespace(ch); 706 707 if (ch == '=') { 708 QElement elt = (QElement) xsl.createElement("xtp:assign"); 709 elt.setLocation(is.getURL(), is.getUserPath(), line, 0); 710 elt.setAttribute("name", name.intern()); 711 parent.appendChild(elt); 712 ch = skipWhitespace(read()); 713 714 if (ch != '$') 715 return parseStatement(elt, ch); 716 else if ((ch = read()) != '(') { 717 peek = ch; 718 return parseStatement(elt, ch); 719 } 720 else { 721 CharBuffer test = CharBuffer.allocate(); 722 lexToRparen(test); 723 elt.setAttribute("select", test.close()); 724 return read(); 725 } 726 } 727 728 QElement elt = (QElement) xsl.createElement(name); 729 elt.setLocation(is.getURL(), is.getUserPath(), line, 0); 730 parent.appendChild(elt); 731 732 if (ch == '(') { 733 parseArgs(elt, arg); 734 735 ch = skipWhitespace(read()); 736 } 737 738 return parseStatement(elt, ch); 739 } 740 741 private int parseIf(Node parent, int ch) 742 throws IOException , XslParseException 743 { 744 QElement choose = (QElement) xsl.createElementNS(XSLNS, "xsl:choose"); 745 choose.setLocation(is.getURL(), is.getUserPath(), line, 0); 746 parent.appendChild(choose); 747 748 while (true) { 749 lexExpect(ch, '('); 750 CharBuffer test = CharBuffer.allocate(); 751 lexToRparen(test); 752 753 QElement elt = (QElement) xsl.createElementNS(XSLNS, "xsl:when"); 754 choose.appendChild(elt); 755 elt.setLocation(is.getURL(), is.getUserPath(), line, 0); 756 elt.setAttribute("test", test.close()); 757 758 ch = parseStatement(elt, skipWhitespace(read())); 759 760 ch = skipWhitespace(ch); 761 if (ch != '$') 762 return ch; 763 764 ch = read(); 765 if (! XmlChar.isNameStart(ch)) { 766 peek = ch; 767 return '$'; 768 } 769 770 String name = parseName(ch); 771 772 if (! name.equals("else")) 773 return parseExtension(parent, name); 774 775 ch = skipWhitespace(read()); 776 777 if (ch == '<') { 778 elt = (QElement) xsl.createElementNS(XSLNS, "xsl:otherwise"); 779 choose.appendChild(elt); 780 elt.setLocation(is.getURL(), is.getUserPath(), line, 0); 781 782 return parseStatement(elt, skipWhitespace(ch)); 783 } 784 785 name = parseName(read()); 786 if (! name.equals("if")) 787 throw error(L.l("expected $if at `${0}'", name)); 788 789 ch = read(); 790 } 791 } 792 793 private String parseName(int ch) 794 throws IOException , XslParseException 795 { 796 CharBuffer cb = CharBuffer.allocate(); 797 798 for (; XmlChar.isNameChar(ch); ch = read()) 799 cb.append((char) ch); 800 801 peek = ch; 802 803 return cb.close(); 804 } 805 806 private void parseArgs(Element elt, String arg) 807 throws IOException , XslParseException 808 { 809 CharBuffer cb = CharBuffer.allocate(); 810 String key = null; 811 boolean isFirst = true; 812 int ch; 813 814 for (ch = read(); ch >= 0 && ch != ')'; ch = read()) { 815 cb.append((char) ch); 816 817 switch (ch) { 818 case '(': 819 lexToRparen(cb); 820 cb.append(')'); 821 break; 822 823 case '"': case '\'': 824 lexString(cb, ch); 825 break; 826 827 case '=': 828 ch = read(); 829 if (ch == '>') { 830 cb.setLength(cb.length() - 1); 831 key = cb.toString().trim(); 832 cb.clear(); 833 } 834 else { 835 peek = ch; 836 } 837 break; 838 839 case ',': 840 cb.setLength(cb.length() - 1); 841 if (key != null) 842 elt.setAttribute(key, cb.toString()); 843 else if (arg != null && isFirst) 844 elt.setAttribute(arg, cb.toString()); 845 else 846 throw error(L.l("unexpected arg `{0}'", cb)); 847 cb.clear(); 848 isFirst = false; 849 key = null; 850 break; 851 } 852 853 } 854 855 if (ch != ')') 856 throw error(L.l("expected `{0}' at {1}", ")", badChar(ch))); 857 858 if (key != null) 859 elt.setAttribute(key, cb.close()); 860 else if (arg != null && cb.length() > 0 && isFirst) 861 elt.setAttribute(arg, cb.close()); 862 else if (cb.length() > 0) 863 throw error(L.l("unexpected arg `{0}'", cb)); 864 } 865 866 871 private void lexToRparen(CharBuffer cb) 872 throws IOException , XslParseException 873 { 874 String filename = getFilename(); 875 int line = getLine(); 876 int ch; 877 878 for (ch = read(); ch >= 0 && ch != ')'; ch = read()) { 879 cb.append((char) ch); 880 881 switch (ch) { 882 case '(': 883 lexToRparen(cb); 884 cb.append(')'); 885 break; 886 887 case '"': case '\'': 888 lexString(cb, ch); 889 break; 890 } 891 } 892 893 if (ch != ')') 894 throw error(L.l("expected `{0}' at {1}. Open at {2}", 895 ")", badChar(ch), filename + ":" + line)); 896 } 897 898 private void lexString(CharBuffer cb, int end) 899 throws IOException , XslParseException 900 { 901 int ch; 902 903 for (ch = read(); ch >= 0 && ch != end; ch = read()) { 904 cb.append((char) ch); 905 } 906 907 if (ch != end) 908 throw error(L.l("expected `{0}' at {1}", "" + (char) end, badChar(ch))); 909 910 cb.append((char) end); 911 } 912 913 private void lexExpect(int ch, int match) 914 throws IOException , XslParseException 915 { 916 for (; XmlChar.isWhitespace((char) ch); ch = read()) { 917 } 918 919 if (ch != match) 920 throw error(L.l("expected `{0}' at {1}", 921 "" + (char) match, badChar(ch))); 922 } 923 924 private CharScanner wsScanner = new CharScanner(" \t"); 925 private CharScanner delimScanner = new CharScanner(" \t;="); 926 927 930 private void parseContentType(Node parent, String contentType) 931 throws IOException , XslParseException 932 { 933 CharCursor cursor = new StringCharCursor(contentType); 934 935 CharBuffer buf = new CharBuffer(); 936 wsScanner.skip(cursor); 937 delimScanner.scan(cursor, buf); 938 939 if (buf.length() <= 0) 940 return; 941 942 Element output = xsl.createElementNS(XSLNS, "xsl:output"); 943 parent.appendChild(output); 944 output.setAttribute("media-type", buf.toString()); 945 delimScanner.skip(cursor); 946 947 buf.clear(); 948 delimScanner.scan(cursor, buf); 949 wsScanner.skip(cursor); 950 if (cursor.current() == '=' && buf.toString().equals("charset")) { 951 delimScanner.skip(cursor); 952 buf.clear(); 953 delimScanner.scan(cursor, buf); 954 if (buf.length() > 0) { 955 output.setAttribute("encoding", Encoding.getMimeName(buf.toString())); 956 is.setEncoding(buf.toString()); 957 } 958 } 959 } 960 961 971 private int parseAttributes(Node parent, Element elt, 972 int ch, boolean isDirective) 973 throws IOException , XslParseException 974 { 975 HashMap <String ,String > newNamespaces = null; 976 977 ch = skipWhitespace(ch); 978 while (XmlChar.isNameStart(ch)) { 979 ch = readTag(ch); 980 String name = tag.toString(); 981 982 ch = skipWhitespace(ch); 983 String value = null; 984 if (ch == '=') { 985 ch = skipWhitespace(read()); 986 ch = readValue(ch); 987 ch = skipWhitespace(ch); 988 989 value = tag.toString(); 990 } 991 992 int p; 993 if (isDirective && name.equals("import")) { 994 Element copy = (Element ) elt.cloneNode(false); 995 copy.setAttribute(name, value); 996 parent.appendChild(copy); 997 } 998 else if (name.startsWith("xmlns")) { 999 QElement qElt = (QElement) elt; 1000 if (newNamespaces == null) { 1001 newNamespaces = new HashMap <String ,String >(_namespaces); 1002 _namespaces = newNamespaces; 1003 } 1004 1005 String prefix; 1006 if (name.startsWith("xmlns:")) 1007 prefix = name.substring(6); 1008 else 1009 prefix = ""; 1010 1011 _namespaces.put(prefix, value); 1012 qElt.setAttributeNS(XMLNS, name, value); 1013 1014 if (prefix != "" && qElt.getNodeName().startsWith(prefix)) { 1016 QDocument doc = (QDocument) xsl; 1017 QName newName = doc.createName(value, qElt.getNodeName()); 1018 qElt.setName(newName); 1019 } 1020 } 1021 else if ((p = name.indexOf(':')) >= 0) { 1022 String prefix = name.substring(0, p); 1023 String uri = _namespaces.get(prefix); 1024 1025 if (uri != null) { 1026 QElement qElt = (QElement) elt; 1027 qElt.setAttributeNS(uri, name, value); 1028 } 1029 else 1030 elt.setAttribute(name, value); 1031 } 1032 else { 1033 elt.setAttribute(name, value); 1034 } 1035 } 1036 1037 return ch; 1038 } 1039 1040 1043 private ProcessingInstruction parsePi() 1044 throws IOException , XslParseException 1045 { 1046 int ch = read(); 1047 1048 if (! XmlChar.isNameStart(ch)) 1049 throw error(L.l("expected name at {0}", badChar(ch))); 1050 1051 ch = readTag(ch); 1052 String name = tag.toString(); 1053 1054 text.clear(); 1055 while (ch >= 0) { 1056 if (ch == '?') { 1057 if ((ch = read()) == '>') { 1058 ProcessingInstruction pi; 1059 pi = xsl.createProcessingInstruction(name, text.toString()); 1060 text.clear(); 1061 return pi; 1062 } 1063 else 1064 addText('?'); 1065 } else { 1066 addText((char) ch); 1067 ch = read(); 1068 } 1069 } 1070 1071 throw error(L.l("expected `{0}' at {1}", ">", badChar(-1))); 1072 } 1073 1074 private int parseDecl(Node parent) 1075 throws IOException , XslParseException 1076 { 1077 int ch = read(); 1078 1079 if (ch == '[') { 1080 if ((ch = read()) != 'C') { 1081 addText("<!["); 1082 return ch; 1083 } else if ((ch = read()) != 'D') { 1084 addText("<![C"); 1085 return ch; 1086 } else if ((ch = read()) != 'A') { 1087 addText("<![CD"); 1088 return ch; 1089 } else if ((ch = read()) != 'T') { 1090 addText("<![CDA"); 1091 return ch; 1092 } else if ((ch = read()) != 'A') { 1093 addText("<![CDAT"); 1094 return ch; 1095 } else if ((ch = read()) != '[') { 1096 addText("<![CDATA"); 1097 return ch; 1098 } else { 1099 ch = read(); 1100 1101 while (ch > 0) { 1102 if (ch == ']') { 1103 ch = read(); 1104 1105 while (ch == ']') { 1106 if ((ch = read()) == '>') 1107 return read(); 1108 else 1109 addText(']'); 1110 } 1111 1112 addText(']'); 1113 } 1114 else { 1115 addText((char) ch); 1116 ch = read(); 1117 } 1118 } 1119 1120 return ch; 1121 } 1122 } 1123 1124 if (ch != '-') { 1125 addText("<!"); 1126 return ch; 1127 } 1128 1129 if ((ch = read()) != '-') { 1130 addText("<!-"); 1131 return ch; 1132 } 1133 1134 while (ch >= 0) { 1135 if ((ch = read()) == '-') { 1136 ch = read(); 1137 while (ch == '-') { 1138 if ((ch = read()) == '>') 1139 return read(); 1140 } 1141 } 1142 } 1143 1144 throw error(L.l("expected `{0}' at {1}", "-->", badChar(-1))); 1145 } 1146 1147 1150 private void parseValueOf(Node parent) 1151 throws IOException , XslParseException 1152 { 1153 int ch = read(); 1154 while (ch >= 0) { 1155 if (ch == '}') { 1156 ch = read(); 1157 if (ch == '>') { 1158 QElement elt; 1159 elt = (QElement) xsl.createElementNS(XSLNS, "xsl:value-of"); 1160 elt.setAttribute("select", text.toString()); 1161 elt.setLocation(is.getURL(), is.getUserPath(), line, 0); 1162 parent.appendChild(elt); 1163 text.clear(); 1164 return; 1165 } 1166 else 1167 addText('}'); 1168 } 1169 else { 1170 addText((char) ch); 1171 ch = read(); 1172 } 1173 } 1174 } 1175 1176 1187 private void parseSpecial(Node parent, int ch) 1188 throws IOException , XslParseException 1189 { 1190 char tail = '#'; 1191 String element = "xtp:scriptlet"; 1192 1193 text.clear(); 1194 String filename = is.getUserPath(); 1195 int line = this.line; 1196 while (ch >= 0) { 1197 if (ch == '<') { 1198 filename = is.getUserPath(); 1199 line = this.line; 1200 ch = read(); 1201 if (ch == '#') { 1202 tail = '#'; 1203 ch = read(); 1204 if (ch == '=') { 1205 ch = read(); 1206 element = "xtp:expression"; 1207 } 1208 break; 1209 } 1210 else if (ch == '<') { 1211 tail = '>'; 1212 break; 1213 } 1214 else if (ch == '\\') { 1215 addText((char) read()); 1216 ch = read(); 1217 } 1218 } else { 1219 addText((char) ch); 1220 ch = read(); 1221 } 1222 } 1223 1224 while (text.length() > 0 && 1225 Character.isSpace(text.charAt(text.length() - 1))) { 1226 text.setLength(text.length() - 1); 1227 } 1228 1229 QElement template = (QElement) xsl.createElementNS(XSLNS, "xsl:template"); 1230 parent.appendChild(template); 1231 String match = text.toString(); 1232 template.setAttribute("match", match); 1233 boolean isName = true; 1234 1235 for (int i = 0; i < match.length(); i++) { 1236 if (! XmlChar.isNameChar(match.charAt(i))) { 1237 isName = false; 1238 break; 1239 } 1240 } 1241 1242 if (isName && false) template.setAttribute("name", match); 1244 if (defaultMode != null) 1245 template.setAttribute("mode", defaultMode); 1246 template.setLocation(filename, filename, line, 0); 1247 1248 text.clear(); 1249 inTemplate = true; 1250 1251 if (tail == '>') { 1252 if (rawText) 1253 template.setAttribute("xml:space", "preserve"); 1254 parseNode(template, ">>", false, read()); 1255 inTemplate = false; 1256 return; 1257 } 1258 1259 QNode scriptlet = (QNode) xsl.createElementNS(XTPNS, element); 1260 scriptlet.setLocation(filename, filename, line, 0); 1261 1262 while (ch >= 0) { 1263 if (ch == tail) { 1264 ch = read(); 1265 if (ch == '>') 1266 break; 1267 else 1268 addText(tail); 1269 } else { 1270 addText((char) ch); 1271 ch = read(); 1272 } 1273 } 1274 1275 scriptlet.appendChild(xsl.createTextNode(text.toString())); 1276 template.appendChild(scriptlet); 1277 text.clear(); 1278 inTemplate = false; 1279 } 1280 1281 private void parseBlock(Node parent, int ch) 1282 throws IOException , XslParseException 1283 { 1284 char tail = '#'; 1285 String element = "xtp:scriptlet"; 1286 1287 for (; XmlChar.isWhitespace((char) ch); ch = read()) { 1288 } 1289 1290 if (ch == ';') 1291 return; 1292 1293 if (ch != '<') 1294 throw error(L.l("expected `{0}' at {1}", "<", badChar(ch))); 1295 1296 String filename = is.getUserPath(); 1297 int line = this.line; 1298 ch = read(); 1299 if (ch == '#') { 1300 tail = '#'; 1301 ch = read(); 1302 if (ch == '=') { 1303 ch = read(); 1304 element = "xtp:expression"; 1305 } 1306 } 1307 else if (ch == '<') { 1308 tail = '>'; 1309 } 1310 else 1311 throw error(L.l("expected block at {1}", "block", badChar(ch))); 1312 1313 if (tail == '>') { 1314 if (rawText) 1315 ((Element ) parent).setAttribute("xml:space", "preserve"); 1316 parseNode(parent, ">>", false, read()); 1317 return; 1318 } 1319 1320 QNode scriptlet = (QNode) xsl.createElementNS(XTPNS, element); 1321 scriptlet.setLocation(filename, filename, line, 0); 1322 1323 while (ch >= 0) { 1324 if (ch == tail) { 1325 ch = read(); 1326 if (ch == '>') 1327 break; 1328 else 1329 addText(tail); 1330 } else { 1331 addText((char) ch); 1332 ch = read(); 1333 } 1334 } 1335 1336 scriptlet.appendChild(xsl.createTextNode(text.toString())); 1337 parent.appendChild(scriptlet); 1338 text.clear(); 1339 } 1340 1341 private void addText(char ch) 1342 { 1343 if (text.length() == 0) { 1344 if (ch == '\n') 1345 textLine = line - 1; 1346 else 1347 textLine = line; 1348 } 1349 text.append(ch); 1350 } 1351 1352 private void addText(String s) 1353 { 1354 if (text.length() == 0) 1355 textLine = line; 1356 text.append(s); 1357 } 1358 1359 private int skipWhitespace(int ch) throws IOException 1360 { 1361 for (; XmlChar.isWhitespace(ch); ch = read()) { 1362 } 1363 1364 return ch; 1365 } 1366 1367 private int readTag(int ch) throws IOException 1368 { 1369 tag.clear(); 1370 for (; XmlChar.isNameChar(ch); ch = read()) 1371 tag.append((char) ch); 1372 1373 return ch; 1374 } 1375 1376 1382 private int readValue(int ch) throws IOException , XslParseException 1383 { 1384 tag.clear(); 1385 1386 if (ch == '\'') { 1387 for (ch = read(); ch >= 0 && ch != '\''; ch = read()) { 1388 if (ch == '&') { 1389 ch = parseEntityReference(); 1390 tag.append(text); 1391 text.clear(); 1392 unread(ch); 1393 } 1394 else 1395 tag.append((char) ch); 1396 } 1397 1398 if (ch != '\'') 1399 throw error(L.l("expected `{0}' at {1}", "'", badChar(ch))); 1400 return read(); 1401 } else if (ch == '"') { 1402 for (ch = read(); ch >= 0 && ch != '"'; ch = read()) { 1403 if (ch == '&') { 1404 ch = parseEntityReference(); 1405 tag.append(text); 1406 text.clear(); 1407 unread(ch); 1408 } 1409 else 1410 tag.append((char) ch); 1411 } 1412 1413 if (ch != '\"') 1414 throw error(L.l("expected `{0}' at {1}", "\"", badChar(ch))); 1415 1416 return read(); 1417 } else if (XmlChar.isNameChar(ch)) { 1418 for (; XmlChar.isNameChar(ch); ch = read()) 1419 tag.append((char) ch); 1420 1421 return ch; 1422 } else 1423 throw error(L.l("expected attribute value at {0}", badChar(ch))); 1424 } 1425 1426 1431 private void addText(Node parent) 1432 { 1433 if (text.getLength() == 0) { 1434 } 1435 else { 1436 Text textNode = (Text ) xsl.createTextNode(text.toString()); 1437 QAbstractNode node = (QAbstractNode) textNode; 1438 1439 node.setLocation(is.getURL(), is.getUserPath(), textLine, 0); 1440 parent.appendChild(textNode); 1441 } 1442 text.clear(); 1443 } 1444 1445 1450 private XslParseException error(String message) 1451 { 1452 return new XslParseException(getFilename() + ":" + getLine() + ": " + 1453 message); 1454 } 1455 1456 1461 private XslParseException error(Exception e) 1462 { 1463 if (e.getMessage() != null) 1464 return new XslParseException(getFilename() + ":" + getLine() + ": " + 1465 e.getMessage()); 1466 else 1467 return new XslParseException(getFilename() + ":" + getLine() + ": " + 1468 e); 1469 } 1470 1471 1474 private String getFilename() 1475 { 1476 return is.getPath().getUserPath(); 1477 } 1478 1479 1482 private int getLine() 1483 { 1484 return line; 1485 } 1486 1487 1490 private String badChar(int ch) 1491 { 1492 if (ch < 0) 1493 return L.l("end of file"); 1494 else if (ch == '\n' || ch == '\r') 1495 return L.l("end of line"); 1496 else 1497 return "`" + (char) ch + "'"; 1498 } 1499 1500 1503 public int read() throws IOException 1504 { 1505 if (peek >= 0) { 1506 int ch = peek; 1507 peek = -1; 1508 return ch; 1509 } 1510 1511 int ch = is.readChar(); 1512 if (ch == '\r') { 1513 if ((ch = is.readChar()) != '\n') { 1514 if (ch >= 0) { 1515 if (ch == '\r') 1516 peek = '\n'; 1517 else 1518 peek = ch; 1519 } 1520 } 1521 ch = '\n'; 1522 } 1523 1524 if (ch == '\n') 1525 line++; 1526 1527 return ch; 1528 } 1529 1530 void unread(int ch) 1531 { 1532 peek = ch; 1533 } 1534 1535 static { 1536 _xslCommands = new HashMap <String ,String >(); 1537 _xslCommands.put("apply-templates", "select"); 1538 _xslCommands.put("call-template", "name"); 1539 _xslCommands.put("apply-imports", ""); 1540 _xslCommands.put("for-each", "select"); 1541 _xslCommands.put("value-of", "select"); 1542 _xslCommands.put("copy-of", "select"); 1543 _xslCommands.put("number", "value"); 1544 _xslCommands.put("choose", ""); 1545 _xslCommands.put("when", "test"); 1546 _xslCommands.put("otherwise", ""); 1547 _xslCommands.put("if", "test"); 1548 _xslCommands.put("text", ""); 1549 _xslCommands.put("copy", ""); 1550 _xslCommands.put("variable", "name"); 1551 _xslCommands.put("param", "name"); 1552 _xslCommands.put("with-param", "name"); 1553 _xslCommands.put("message", ""); 1554 _xslCommands.put("fallback", ""); 1555 _xslCommands.put("processing-instruction", "name"); 1556 _xslCommands.put("comment", ""); 1557 _xslCommands.put("element", "name"); 1558 _xslCommands.put("attribute", "name"); 1559 _xslCommands.put("import", "href"); 1560 _xslCommands.put("include", "href"); 1561 _xslCommands.put("strip-space", "elements"); 1562 _xslCommands.put("preserve-space", "elements"); 1563 _xslCommands.put("output", ""); 1564 _xslCommands.put("key", ""); 1565 _xslCommands.put("decimal-format", ""); 1566 _xslCommands.put("attribute-set", "name"); 1567 _xslCommands.put("variable", "name"); 1568 _xslCommands.put("param", "name"); 1569 _xslCommands.put("template", "match"); 1570 _xslCommands.put("namespace-alias", ""); _xslCommands.put("result-document", "href"); 1573 1574 _xtpCommands = new HashMap <String ,String >(); 1575 _xtpCommands.put("while", "test"); 1576 _xtpCommands.put("expression", "expr"); 1577 _xtpCommands.put("expr", "expr"); 1578 _xtpCommands.put("scriptlet", ""); 1579 _xtpCommands.put("declaration", ""); 1580 _xtpCommands.put("directive.page", ""); 1581 _xtpCommands.put("directive.cache", ""); 1582 } 1583} 1584
| Popular Tags
|