1 29 30 package com.caucho.jsp; 31 32 import com.caucho.java.LineMap; 33 import com.caucho.jsp.java.JspNode; 34 import com.caucho.log.Log; 35 import com.caucho.util.CharBuffer; 36 import com.caucho.util.L10N; 37 import com.caucho.util.LineCompileException; 38 import com.caucho.vfs.Path; 39 import com.caucho.vfs.ReadStream; 40 import com.caucho.xml.QName; 41 import com.caucho.xml.XmlChar; 42 43 import java.io.IOException ; 44 import java.util.ArrayList ; 45 import java.util.HashSet ; 46 import java.util.logging.Level ; 47 import java.util.logging.Logger ; 48 49 53 public class JspParser { 54 static L10N L = new L10N(JspParser.class); 55 static final Logger log = Log.open(JspParser.class); 56 57 public static final String JSP_NS = "http://java.sun.com/JSP/Page"; 58 public static final String JSTL_CORE_URI = "http://java.sun.com/jsp/jstl/core"; 59 public static final String JSTL_FMT_URI = "http://java.sun.com/jsp/jstl/fmt"; 60 61 public static final QName PREFIX = new QName("prefix"); 62 public static final QName TAGLIB = new QName("taglib"); 63 public static final QName TAGDIR = new QName("tagdir"); 64 public static final QName URI = new QName("uri"); 65 66 public static final QName JSP_DECLARATION = 67 new QName("jsp", "declaration", JSP_NS); 68 69 public static final QName JSP_SCRIPTLET = 70 new QName("jsp", "scriptlet", JSP_NS); 71 72 public static final QName JSP_EXPRESSION = 73 new QName("jsp", "expression", JSP_NS); 74 75 public static final QName JSP_DIRECTIVE_PAGE = 76 new QName("jsp", "directive.page", JSP_NS); 77 78 public static final QName JSP_DIRECTIVE_INCLUDE = 79 new QName("jsp", "directive.include", JSP_NS); 80 81 public static final QName JSP_DIRECTIVE_CACHE = 82 new QName("jsp", "directive.cache", JSP_NS); 83 84 public static final QName JSP_DIRECTIVE_TAGLIB = 85 new QName("jsp", "directive.taglib", JSP_NS); 86 87 public static final QName JSP_DIRECTIVE_ATTRIBUTE = 88 new QName("jsp", "directive.attribute", JSP_NS); 89 90 public static final QName JSP_DIRECTIVE_VARIABLE = 91 new QName("jsp", "directive.variable", JSP_NS); 92 93 public static final QName JSP_DIRECTIVE_TAG = 94 new QName("jsp", "directive.tag", JSP_NS); 95 96 public static final QName JSTL_CORE_OUT = 97 new QName("resin-c", "out", "urn:jsptld:" + JSTL_CORE_URI); 98 99 public static final QName JSTL_CORE_CHOOSE = 100 new QName("resin-c", "choose", "urn:jsptld:" + JSTL_CORE_URI); 101 102 public static final QName JSTL_CORE_WHEN = 103 new QName("resin-c", "when", "urn:jsptld:" + JSTL_CORE_URI); 104 105 public static final QName JSTL_CORE_OTHERWISE = 106 new QName("resin-c", "otherwise", "urn:jsptld:" + JSTL_CORE_URI); 107 108 public static final QName JSTL_CORE_FOREACH = 109 new QName("resin-c", "forEach", "urn:jsptld:" + JSTL_CORE_URI); 110 111 private static final int TAG_UNKNOWN = 0; 112 private static final int TAG_JSP = 1; 113 private static final int TAG_RAW = 2; 114 115 private ParseState _parseState; 116 private JspBuilder _jspBuilder; 117 private ParseTagManager _tagManager; 118 119 private LineMap _lineMap; 120 121 private ArrayList <String > _preludeList = new ArrayList <String >(); 122 private ArrayList <String > _codaList = new ArrayList <String >(); 123 124 private ArrayList <Include> _includes = new ArrayList <Include>(); 125 126 private HashSet <String > _prefixes = new HashSet <String >(); 127 128 private Path _jspPath; 129 private ReadStream _stream; 130 private String _uriPwd; 131 132 private String _contextPath = ""; 133 private String _filename = ""; 134 private int _line; 135 private int _lineStart; 136 137 private int _charCount; 138 private int _startText; 139 140 private int _peek = -1; 141 private boolean _seenCr = false; 142 143 private Namespace _namespaces; 144 145 private boolean _isXml; 146 private boolean _isTop = true; 147 148 private CharBuffer _tag = new CharBuffer(); 149 private CharBuffer _value = new CharBuffer(); 150 151 private CharBuffer _text = new CharBuffer(); 152 153 157 void setJspBuilder(JspBuilder builder) 158 { 159 _jspBuilder = builder; 160 } 161 162 165 void setContextPath(String contextPath) 166 { 167 _contextPath = contextPath; 168 } 169 170 173 void setParseState(ParseState parseState) 174 { 175 _parseState = parseState; 176 } 177 178 181 ParseState getParseState() 182 { 183 return _parseState; 184 } 185 186 189 void setTagManager(ParseTagManager manager) 190 { 191 _tagManager = manager; 192 } 193 194 197 private boolean isELIgnored() 198 { 199 return _parseState.isELIgnored(); 200 } 201 202 205 private boolean isDeferredSyntaxAllowedAsLiteral() 206 { 207 return _parseState.isDeferredSyntaxAllowedAsLiteral(); 208 } 209 210 213 public void addPrelude(String prelude) 214 { 215 _preludeList.add(prelude); 216 } 217 218 221 public void addCoda(String coda) 222 { 223 _codaList.add(coda); 224 } 225 226 232 void parse(Path path, String uri) 233 throws Exception 234 { 235 _namespaces = new Namespace(null, "jsp", JSP_NS); 236 _parseState.pushNamespace("jsp", JSP_NS); 237 238 _isXml = _parseState.isXml(); 239 240 _filename = _contextPath + uri; 241 242 if (uri != null) { 243 int p = uri.lastIndexOf('/'); 244 _uriPwd = p <= 0 ? "/" : uri.substring(0, p + 1); 245 } 246 else { 247 _uriPwd = "/"; 248 } 249 _parseState.setUriPwd(_uriPwd); 250 251 ReadStream is = path.openRead(); 252 path.setUserPath(uri); 253 254 try { 255 parseJsp(is); 256 } finally { 257 is.close(); 258 for (int i = 0; i < _includes.size(); i++) { 259 Include inc = _includes.get(i); 260 inc._stream.close(); 261 } 262 } 263 } 264 265 271 void parseTag(Path path, String uri) 272 throws Exception 273 { 274 _parseState.setTag(true); 275 276 parse(path, uri); 277 } 278 279 286 private void parseJsp(ReadStream stream) 287 throws Exception 288 { 289 _text.clear(); 290 _includes.clear(); 291 292 String uriPwd = _uriPwd; 293 294 for (int i = _codaList.size() - 1; i >= 0; i--) 295 pushInclude(_codaList.get(i), true); 296 297 addInclude(stream, uriPwd); 298 299 for (int i = _preludeList.size() - 1; i >= 0; i--) 300 pushInclude(_preludeList.get(i), true); 301 302 setLocation(); 303 _jspBuilder.startDocument(); 304 305 String pageEncoding = _parseState.getPageEncoding(); 306 307 int ch; 308 309 if (pageEncoding != null) { 310 _parseState.setPageEncoding(pageEncoding); 311 312 stream.setEncoding(pageEncoding); 313 } 314 315 switch ((ch = stream.read())) { 316 case 0xfe: 317 if ((ch = stream.read()) != 0xff) { 318 throw error(L.l("Expected 0xff in UTF-16 header. UTF-16 pages with the initial byte 0xfe expect 0xff immediately following. The 0xfe 0xff sequence is used by some application to suggest UTF-16 encoding without a directive.")); 319 } 320 else { 321 _parseState.setContentType("text/html; charset=UTF-16BE"); 322 _parseState.setPageEncoding("UTF-16BE"); 323 stream.setEncoding("UTF-16BE"); 324 } 325 break; 326 327 case 0xff: 328 if ((ch = stream.read()) != 0xfe) { 329 throw error(L.l("Expected 0xfe in UTF-16 header. UTF-16 pages with the initial byte 0xff expect 0xfe immediately following. The 0xff 0xfe sequence is used by some application to suggest UTF-16 encoding without a directive.")); 330 } 331 else { 332 _parseState.setContentType("text/html; charset=UTF-16LE"); 333 _parseState.setPageEncoding("UTF-16LE"); 334 stream.setEncoding("UTF-16LE"); 335 } 336 break; 337 338 case 0xef: 339 if ((ch = stream.read()) != 0xbb) { 340 stream.unread(); 341 stream.unread(); 342 } 343 else if ((ch = stream.read()) != 0xbf) { 344 throw error(L.l("Expected 0xbf in UTF-8 header. UTF-8 pages with the initial byte 0xbb expect 0xbf immediately following. The 0xbb 0xbf sequence is used by some application to suggest UTF-8 encoding without a directive.")); 345 } 346 else { 347 _parseState.setContentType("text/html; charset=UTF-8"); 348 _parseState.setPageEncoding("UTF-8"); 349 stream.setEncoding("UTF-8"); 350 } 351 break; 352 353 default: 354 stream.unread(); 355 } 356 357 ch = read(); 358 359 ch = parseXmlDeclaration(ch); 360 361 try { 362 parseNode(ch); 363 } finally { 364 for (int i = 0; i < _includes.size(); i++) { 365 Include inc = _includes.get(i); 366 inc._stream.close(); 367 } 368 } 369 370 setLocation(); 371 _jspBuilder.endDocument(); 372 } 373 374 private int parseXmlDeclaration(int ch) 375 throws IOException , JspParseException 376 { 377 if (ch != '<') 378 return ch; 379 else if ((ch = read()) != '?') { 380 unread(ch); 381 return '<'; 382 } 383 else if ((ch = read()) != 'x') { 384 addText("<?"); 385 return ch; 386 } 387 else if ((ch = read()) != 'm') { 388 addText("<?x"); 389 return ch; 390 } 391 else if ((ch = read()) != 'l') { 392 addText("<?xm"); 393 return ch; 394 } 395 else if (! XmlChar.isWhitespace((ch = read()))) { 396 addText("<?xml"); 397 return ch; 398 } 399 400 String encoding = null; 401 402 addText("<?xml "); 403 ch = skipWhitespace(ch); 404 while (XmlChar.isNameStart(ch)) { 405 ch = readName(ch); 406 String name = _tag.toString(); 407 408 addText(name); 409 if (XmlChar.isWhitespace(ch)) 410 addText(' '); 411 412 ch = skipWhitespace(ch); 413 if (ch != '=') 414 return ch; 415 416 readValue(name, ch, true); 417 String value = _value.toString(); 418 419 addText("=\""); 420 addText(value); 421 addText("\""); 422 423 if (name.equals("encoding")) 424 encoding = value; 425 426 ch = read(); 427 if (XmlChar.isWhitespace(ch)) 428 addText(' '); 429 ch = skipWhitespace(ch); 430 } 431 432 if (ch != '?') 433 return ch; 434 else if ((ch = read()) != '>') { 435 addText('?'); 436 return ch; 437 } 438 else { 439 addText("?>"); 440 441 if (encoding != null) { 442 _stream.setEncoding(encoding); 443 _parseState.setPageEncoding(encoding); 444 } 445 446 return read(); 447 } 448 } 449 450 private void parseNode(int ch) 451 throws IOException , JspParseException 452 { 453 while (ch != -1) { 454 switch (ch) { 455 case '<': 456 { 457 switch ((ch = read())) { 458 case '%': 459 if (_isXml) 460 throw error(L.l("'<%' syntax is not allowed in JSP/XML syntax.")); 461 462 parseScriptlet(); 463 _startText = _charCount; 464 465 if ((ch = read()) == '\\') { 467 if ((ch = read()) == '\n') { 468 ch = read(); 469 } 470 else if (ch == '\r') { 471 if ((ch = read()) == '\n') 472 ch = read(); 473 } 474 else 475 addText('\\'); 476 } 477 break; 478 479 case '/': 480 ch = parseCloseTag(); 481 break; 482 483 case '\\': 484 if ((ch = read()) == '%') { 485 addText("<%"); 486 ch = read(); 487 } 488 else 489 addText("<\\"); 490 break; 491 492 case '!': 493 if (! _isXml) 494 addText("<!"); 495 else if ((ch = read()) == '[') 496 parseCdata(); 497 else if (ch == '-' && (ch = read()) == '-') 498 parseXmlComment(); 499 else 500 throw error(L.l("'{0}' was not expected after '<!'. In the XML syntax, only <!-- ... --> and <![CDATA[ ... ]> are legal. You can use '&!' to escape '<!'.", 501 badChar(ch))); 502 503 ch = read(); 504 break; 505 506 default: 507 if (! XmlChar.isNameStart(ch)) { 508 addText('<'); 509 break; 510 } 511 512 ch = readName(ch); 513 String name = _tag.toString(); 514 int tagCode = getTag(name); 515 if (! _isXml && tagCode == TAG_UNKNOWN) { 516 addText("<"); 517 addText(name); 518 break; 519 } 520 521 if (_isTop && name.equals("jsp:root")) { 522 if (_parseState.isForbidXml()) 523 throw error(L.l("jsp:root must be in a JSP (XML) document, not a plain JSP.")); 524 525 _text.clear(); 526 _isXml = true; 527 _parseState.setELIgnoredDefault(false); 528 _parseState.setXml(true); 529 530 } 531 _isTop = false; 532 parseOpenTag(name, ch, tagCode == TAG_UNKNOWN); 533 534 ch = read(); 535 536 if (! _isXml && ch == '\\') { 538 if ((ch = read()) == '\n') { 539 ch = read(); 540 } 541 else if (ch == '\r') { 542 if ((ch = read()) == '\n') 543 ch = read(); 544 } 545 } 546 } 547 break; 548 } 549 550 case '&': 551 if (! _isXml) 552 addText((char) ch); 553 else { 554 addText((char) parseEntity()); 555 } 556 ch = read(); 557 break; 558 559 case '$': 560 ch = read(); 561 562 if (ch == '{' && ! isELIgnored()) 563 ch = parseJspExpression(); 564 else 565 addText('$'); 566 break; 567 568 case '#': 569 ch = read(); 570 571 if (ch != '{' || isELIgnored()) { 572 addText('#'); 573 } 574 else if (isDeferredSyntaxAllowedAsLiteral()) { 575 addText('#'); 576 } 577 else 578 throw error(L.l("Deferred syntax ('#{...}') not allowed as literal.")); 579 break; 580 581 case '\\': 582 switch (ch = read()) { 583 case '$': 584 if (! isELIgnored()) { 585 addText('$'); 586 ch = read(); 587 } 588 else 589 addText('\\'); 590 break; 591 592 case '#': 593 if (! isELIgnored()) { 594 addText('#'); 595 ch = read(); 596 } 597 else 598 addText('\\'); 599 break; 600 601 case '\\': 602 addText('\\'); 603 break; 604 605 default: 606 addText('\\'); 607 break; 608 } 609 break; 610 611 default: 612 addText((char) ch); 613 ch = read(); 614 break; 615 } 616 } 617 618 addText(); 619 620 625 } 626 627 634 private int parseJspExpression() 635 throws IOException , JspParseException 636 { 637 addText(); 638 639 Path jspPath = _jspPath; 640 String filename = _filename; 641 int line = _line; 642 643 CharBuffer cb = CharBuffer.allocate(); 644 int ch; 645 cb.append("${"); 646 for (ch = read(); ch >= 0 && ch != '}'; ch = read()) 647 cb.append((char) ch); 648 cb.append("}"); 649 650 ch = read(); 651 652 processTaglib("resin-c", JSTL_CORE_URI); 653 654 setLocation(jspPath, filename, line); 655 _jspBuilder.startElement(JSTL_CORE_OUT); 656 _jspBuilder.attribute(new QName("value"), cb.close()); 657 _jspBuilder.attribute(new QName("escapeXml"), "false"); 658 _jspBuilder.endAttributes(); 659 _jspBuilder.endElement(JSTL_CORE_OUT.getName()); 660 661 return ch; 662 } 663 664 667 private void parseCdata() 668 throws IOException , JspParseException 669 { 670 int ch; 671 672 ch = readName(read()); 673 674 String name = _tag.toString(); 675 676 if (! name.equals("CDATA")) 677 throw error(L.l("Expected <![CDATA[ at <!['{0}'.", name, 678 "XML only recognizes the <![CDATA directive.")); 679 680 if (ch != '[') 681 throw error(L.l("Expected '[' at '{0}'. The XML CDATA syntax is <![CDATA[...]]>.", 682 String.valueOf(ch))); 683 684 String filename = _filename; 685 int line = _line; 686 687 while ((ch = read()) >= 0) { 688 while (ch == ']') { 689 if ((ch = read()) != ']') 690 addText(']'); 691 else if ((ch = read()) != '>') 692 addText("]]"); 693 else 694 return; 695 } 696 697 addText((char) ch); 698 } 699 700 throw error(L.l("Expected closing ]]> at end of file to match <![[CDATA at {0}.", filename + ":" + line)); 701 } 702 703 711 private int readName(int ch) 712 throws IOException , JspParseException 713 { 714 _tag.clear(); 715 716 for (; XmlChar.isNameChar((char) ch); ch = read()) 717 _tag.append((char) ch); 718 719 return ch; 720 } 721 722 private void parsePageDirective(String name, String value) 723 throws IOException , JspParseException 724 { 725 if ("isELIgnored".equals(name)) { 726 if ("true".equals(value)) 727 _parseState.setELIgnored(true); 728 } 729 } 730 731 734 private void parseScriptlet() 735 throws IOException , JspParseException 736 { 737 addText(); 738 739 _lineStart = _line; 740 741 int ch = read(); 742 743 QName eltName = null; 745 746 switch (ch) { 747 case '=': 748 eltName = JSP_EXPRESSION; 749 ch = read(); 750 break; 751 752 case '!': 753 eltName = JSP_DECLARATION; 754 ch = read(); 755 break; 756 757 case '@': 758 parseDirective(); 759 return; 760 761 case '-': 762 if ((ch = read()) == '-') { 763 parseComment(); 764 return; 765 } 766 else { 767 eltName = JSP_SCRIPTLET; 768 addText('-'); 769 } 770 break; 771 772 default: 773 eltName = JSP_SCRIPTLET; 774 break; 775 } 776 777 setLocation(_jspPath, _filename, _lineStart); 778 _jspBuilder.startElement(eltName); 779 _jspBuilder.endAttributes(); 780 781 while (ch >= 0) { 782 switch (ch) { 783 case '\\': 784 addText('\\'); 785 ch = read(); 786 if (ch >= 0) 787 addText((char) ch); 788 ch = read(); 789 break; 790 791 case '%': 792 ch = read(); 793 if (ch == '>') { 794 createText(); 795 setLocation(); 796 _jspBuilder.endElement(eltName.getName()); 797 return; 798 } 799 else if (ch == '\\') { 800 ch = read(); 801 if (ch == '>') { 802 addText("%"); 803 } 804 else 805 addText("%\\"); 806 } 807 else 808 addText('%'); 809 break; 810 811 default: 812 addText((char) ch); 813 ch = read(); 814 break; 815 } 816 } 817 818 createText(); 819 setLocation(); 820 _jspBuilder.endElement(eltName.getName()); 821 } 822 823 826 private void parseDirective() 827 throws IOException , JspParseException 828 { 829 String language = null; 830 831 int ch = skipWhitespace(read()); 832 String directive = ""; 833 if (XmlChar.isNameStart(ch)) { 834 ch = readName(ch); 835 directive = _tag.toString(); 836 } 837 else 838 throw error(L.l("Expected jsp directive name at '{0}'. JSP directive syntax is <%@ name attr1='value1' ... %>", 839 badChar(ch))); 840 841 QName qname; 842 843 if (directive.equals("page")) 844 qname = JSP_DIRECTIVE_PAGE; 845 else if (directive.equals("include")) 846 qname = JSP_DIRECTIVE_INCLUDE; 847 else if (directive.equals("taglib")) 848 qname = JSP_DIRECTIVE_TAGLIB; 849 else if (directive.equals("cache")) 850 qname = JSP_DIRECTIVE_CACHE; 851 else if (directive.equals("attribute")) 852 qname = JSP_DIRECTIVE_ATTRIBUTE; 853 else if (directive.equals("variable")) 854 qname = JSP_DIRECTIVE_VARIABLE; 855 else if (directive.equals("tag")) 856 qname = JSP_DIRECTIVE_TAG; 857 else 858 throw error(L.l("'{0}' is an unknown jsp directive. Only <%@ page ... %>, <%@ include ... %>, <%@ taglib ... %>, and <%@ cache ... %> are known.", directive)); 859 860 unread(ch); 861 862 ArrayList <QName> keys = new ArrayList <QName>(); 863 ArrayList <String > values = new ArrayList <String >(); 864 865 parseAttributes(keys, values); 866 867 ch = skipWhitespace(read()); 868 869 if (ch != '%' || (ch = read()) != '>') { 870 throw error(L.l("expected '%>' at {0}. JSP directive syntax is '<%@ name attr1='value1' ... %>'. (Started at line {1})", 871 badChar(ch), _lineStart)); 872 } 873 874 setLocation(_jspPath, _filename, _lineStart); 875 _lineStart = _line; 876 _jspBuilder.startElement(qname); 877 878 for (int i = 0; i < keys.size(); i++) { 879 _jspBuilder.attribute(keys.get(i), values.get(i)); 880 } 881 _jspBuilder.endAttributes(); 882 883 if (qname.equals(JSP_DIRECTIVE_TAGLIB)) 884 processTaglibDirective(keys, values); 885 886 setLocation(); 887 _jspBuilder.endElement(qname.getName()); 888 889 if (qname.equals(JSP_DIRECTIVE_PAGE)) { 890 String contentEncoding = _parseState.getPageEncoding(); 891 if (contentEncoding == null) 892 contentEncoding = _parseState.getCharEncoding(); 893 894 if (contentEncoding != null) { 895 try { 896 _stream.setEncoding(contentEncoding); 897 } catch (Exception e) { 898 log.log(Level.FINER, e.toString(), e); 899 900 throw error(L.l("unknown content encoding '{0}'", contentEncoding), 901 e); 902 } 903 } 904 } 905 911 } 912 913 916 private void parseComment() 917 throws IOException , JspParseException 918 { 919 int ch = read(); 920 921 while (ch >= 0) { 922 if (ch == '-') { 923 ch = read(); 924 while (ch == '-') { 925 if ((ch = read()) == '-') 926 continue; 927 else if (ch == '%' && (ch = read()) == '>') 928 return; 929 else if (ch == '-') 930 ch = read(); 931 } 932 } 933 else 934 ch = read(); 935 } 936 } 937 938 private void parseXmlComment() 939 throws IOException , JspParseException 940 { 941 int ch; 942 943 while ((ch = read()) >= 0) { 944 while (ch == '-') { 945 if ((ch = read()) == '-' && (ch = read()) == '>') 946 return; 947 } 948 } 949 } 950 951 954 private void parseOpenTag(String name, int ch, boolean isXml) 955 throws IOException , JspParseException 956 { 957 addText(); 958 959 ch = skipWhitespace(ch); 960 961 ArrayList <QName> keys = new ArrayList <QName>(); 962 ArrayList <String > values = new ArrayList <String >(); 963 964 unread(ch); 965 966 parseAttributes(keys, values); 967 968 QName qname = getElementQName(name); 969 970 setLocation(_jspPath, _filename, _lineStart); 971 _lineStart = _line; 972 973 _jspBuilder.startElement(qname); 974 975 for (int i = 0; i < keys.size(); i++) { 976 QName key = keys.get(i); 977 String value = values.get(i); 978 979 _jspBuilder.attribute(key, value); 980 } 981 982 _jspBuilder.endAttributes(); 983 984 if (qname.equals(JSP_DIRECTIVE_TAGLIB)) 985 processTaglibDirective(keys, values); 986 987 ch = skipWhitespace(read()); 988 989 JspNode node = _jspBuilder.getCurrentNode(); 990 991 if (ch == '/') { 992 if ((ch = read()) != '>') 993 throw error(L.l("expected '/>' at '{0}' (for tag '<{1}>' at line {2}). The XML empty tag syntax is: <tag attr1='value1'/>", 994 badChar(ch), name, String.valueOf(_lineStart))); 995 996 setLocation(); 997 _jspBuilder.endElement(qname.getName()); 998 } 999 else if (ch != '>') 1000 throw error(L.l("expected '>' at '{0}' (for tag '<{1}>' at line {2}). The XML tag syntax is: <tag attr1='value1'>", 1001 badChar(ch), name, String.valueOf(_lineStart))); 1002 else if ("tagdependent".equals(node.getBodyContent()) && ! _isXml) { 1004 String tail = "</" + name + ">"; 1005 for (ch = read(); ch >= 0; ch = read()) { 1006 _text.append((char) ch); 1007 if (_text.endsWith(tail)) { 1008 _text.setLength(_text.length() - tail.length()); 1009 addText(); 1010 _jspBuilder.endElement(qname.getName()); 1011 return; 1012 } 1013 } 1014 throw error(L.l("expected '{0}' at end of file (for tag <{1}> at line {2}). Tags with 'tagdependent' content need close tags.", 1015 tail, String.valueOf(_lineStart))); 1016 } 1017 } 1018 1019 1022 private QName getElementQName(String name) 1023 { 1024 int p = name.lastIndexOf(':'); 1025 1026 if (p > 0) { 1027 String prefix = name.substring(0, p); 1028 String url = Namespace.find(_namespaces, prefix); 1029 1030 _prefixes.add(prefix); 1031 1032 if (url != null) 1033 return new QName(prefix, name.substring(p + 1), url); 1034 else 1035 return new QName("", name, ""); 1036 } 1037 else { 1038 String url = Namespace.find(_namespaces, ""); 1039 1040 if (url != null) 1041 return new QName("", name, url); 1042 else 1043 return new QName("", name, ""); 1044 } 1045 } 1046 1047 1050 private QName getAttributeQName(String name) 1051 { 1052 int p = name.lastIndexOf(':'); 1053 1054 if (p > 0) { 1055 String prefix = name.substring(0, p); 1056 String url = Namespace.find(_namespaces, prefix); 1057 1058 if (url != null) 1059 return new QName(prefix, name.substring(p + 1), url); 1060 else 1061 return new QName("", name, ""); 1062 } 1063 else 1064 return new QName("", name, ""); 1065 } 1066 1067 1070 private void parseAttributes(ArrayList <QName> names, 1071 ArrayList <String > values) 1072 throws IOException , JspParseException 1073 { 1074 names.clear(); 1075 values.clear(); 1076 1077 int ch = skipWhitespace(read()); 1078 1079 while (XmlChar.isNameStart(ch)) { 1080 ch = readName(ch); 1081 String key = _tag.toString(); 1082 1083 readValue(key, ch, _isXml); 1084 String value = _value.toString(); 1085 1086 if (key.startsWith("xmlns:")) { 1087 String prefix = key.substring(6); 1088 1089 _jspBuilder.startPrefixMapping(prefix, value); 1090 _namespaces = new Namespace(_namespaces, prefix, value); 1092 } 1093 else if (key.equals("xmlns")) { 1094 _jspBuilder.startPrefixMapping("", value); 1095 _namespaces = new Namespace(_namespaces, "", value); 1098 } 1099 else { 1100 names.add(getAttributeQName(key)); 1101 values.add(value); 1102 } 1103 1104 ch = skipWhitespace(read()); 1105 } 1106 1107 unread(ch); 1108 } 1109 1110 1113 private void readValue(String attribute, int ch, boolean isXml) 1114 throws IOException , JspParseException 1115 { 1116 boolean isRuntimeAttribute = false; 1117 1118 _value.clear(); 1119 1120 ch = skipWhitespace(ch); 1121 1122 if (ch != '=') { 1123 unread(ch); 1124 return; 1125 } 1126 1127 ch = skipWhitespace(read()); 1128 1129 if (ch != '\'' && ch != '"') { 1130 if (XmlChar.isNameChar(ch)) { 1131 ch = readName(ch); 1132 1133 throw error(L.l("'{0}' attribute value must be quoted at '{1}'. JSP attribute syntax is either attr=\"value\" or attr='value'.", 1134 attribute, _tag)); 1135 } 1136 else 1137 throw error(L.l("'{0}' attribute value must be quoted at '{1}'. JSP attribute syntax is either attr=\"value\" or attr='value'.", 1138 attribute, badChar(ch))); 1139 } 1140 1141 int end = ch; 1142 int lastCh = 0; 1143 1144 ch = read(); 1145 if (ch != '<') { 1146 } 1147 else if ((ch = read()) != '%') 1148 _value.append('<'); 1149 else if ((ch = read()) != '=') 1150 _value.append("<%"); 1151 else { 1152 _value.append("<%"); 1153 isRuntimeAttribute = true; 1154 } 1155 1156 while (ch != -1 && (ch != end || isRuntimeAttribute)) { 1157 if (ch == '\\') { 1158 switch ((ch = read())) { 1159 case '\\': 1160 case '\'': 1161 case '\"': 1162 _value.append((char) ch); 1165 ch = read(); 1166 break; 1167 1168 case '>': 1169 if (lastCh == '%') { 1170 _value.append('>'); 1171 ch = read(); 1172 } 1173 else 1174 _value.append('\\'); 1175 break; 1176 1177 case '%': 1178 if (lastCh == '<') { 1179 _value.append('%'); 1180 ch = read(); 1181 } 1182 else 1183 _value.append('\\'); 1184 break; 1185 1186 default: 1187 _value.append('\\'); 1188 break; 1189 } 1190 } 1191 else if (ch == '%' && isRuntimeAttribute) { 1192 _value.append('%'); 1193 ch = read(); 1194 if (ch == '>') 1195 isRuntimeAttribute = false; 1196 } 1197 else if (ch == '&' && isXml) { 1198 lastCh = -1; 1199 _value.append((char) parseEntity()); 1200 ch = read(); 1201 } 1202 else if (ch == '&') { 1203 if ((ch = read()) == 'a') { 1204 if ((ch = read()) != 'p') 1205 _value.append("&a"); 1206 else if ((ch = read()) != 'o') 1207 _value.append("&ap"); 1208 else if ((ch = read()) != 's') 1209 _value.append("&apo"); 1210 else if ((ch = read()) != ';') 1211 _value.append("&apos"); 1212 else { 1213 _value.append('\''); 1214 ch = read(); 1215 } 1216 } 1217 else if (ch == 'q') { 1218 if ((ch = read()) != 'u') 1219 _value.append("&q"); 1220 else if ((ch = read()) != 'o') 1221 _value.append("&qu"); 1222 else if ((ch = read()) != 't') 1223 _value.append("&quo"); 1224 else if ((ch = read()) != ';') 1225 _value.append("""); 1226 else { 1227 _value.append('"'); 1228 ch = read(); 1229 } 1230 } 1231 else 1232 _value.append('&'); 1233 } 1234 else { 1235 _value.append((char) ch); 1236 lastCh = ch; 1237 ch = read(); 1238 } 1239 } 1240 } 1241 1242 1245 int parseEntity() 1246 throws IOException , JspParseException 1247 { 1248 int ch = read(); 1249 1250 if (_isXml && ch == '#') { 1251 int value = 0; 1252 1253 for (ch = read(); ch >= '0' && ch <= '9'; ch = read()) 1254 value = 10 * value + ch - '0'; 1255 1256 if (ch != ';') 1257 throw error(L.l("expected ';' at '{0}' in character entity. The XML character entities syntax is &#nn;", 1258 badChar(ch))); 1259 1260 return (char) value; 1261 } 1262 1263 CharBuffer cb = CharBuffer.allocate(); 1264 for (; ch >= 'a' && ch <= 'z'; ch = read()) 1265 cb.append((char) ch); 1266 1267 if (ch != ';') { 1268 1269 log.warning(L.l("expected ';' at '{0}' in entity '&{1}'. The XML entity syntax is &name;", 1270 badChar(ch), cb)); 1271 } 1272 1273 String entity = cb.close(); 1274 if (entity.equals("lt")) 1275 return '<'; 1276 else if (entity.equals("gt")) 1277 return '>'; 1278 else if (entity.equals("amp")) 1279 return '&'; 1280 else if (entity.equals("apos")) 1281 return '\''; 1282 else if (entity.equals("quot")) 1283 return '"'; 1284 else 1285 throw error(L.l("unknown entity '&{0};'. XML only recognizes the special entities <, >, &, ' and "", entity)); 1286 } 1287 1288 private int parseCloseTag() 1289 throws IOException , JspParseException 1290 { 1291 int ch; 1292 1293 if (! XmlChar.isNameStart(ch = read())) { 1294 addText("</"); 1295 return ch; 1296 } 1297 1298 ch = readName(ch); 1299 String name = _tag.toString(); 1300 if (! _isXml && getTag(name) == TAG_UNKNOWN) { 1301 addText("</"); 1302 addText(name); 1303 return ch; 1304 } 1305 1306 ch = skipWhitespace(ch); 1307 if (ch != '>') 1308 throw error(L.l("expected '>' at {0}. The XML close tag syntax is </name>.", badChar(ch))); 1309 1310 JspNode node = _jspBuilder.getCurrentNode(); 1311 String nodeName = node.getTagName(); 1312 if (nodeName.equals(name)) { 1313 } 1314 else if (nodeName.equals("resin-c:when")) { 1315 throw error(L.l("#if expects closing #end before </{0}> (#if at {1}). #if statements require #end before the enclosing tag closes.", 1316 name, String.valueOf(node.getStartLine()))); 1317 } 1318 else if (nodeName.equals("resin-c:otherwise")) { 1319 throw error(L.l("#else expects closing #end before </{0}> (#else at {1}). #if statements require #end before the enclosing tag closes.", 1320 name, String.valueOf(node.getStartLine()))); 1321 } 1322 else { 1323 throw error(L.l("expected </{0}> at </{1}>. Closing tags must match opened tags.", 1324 nodeName, name)); 1325 } 1326 1327 addText(); 1328 1329 setLocation(); 1330 _jspBuilder.endElement(name); 1331 1332 return read(); 1333 } 1334 1335 private void processTaglibDirective(ArrayList <QName> keys, 1336 ArrayList <String > values) 1337 throws IOException , JspParseException 1338 { 1339 int p = keys.indexOf(PREFIX); 1340 if (p < 0) 1341 throw error(L.l("The taglib directive requires a 'prefix' attribute. 'prefix' is the XML prefix for all tags in the taglib.")); 1342 String prefix = values.get(p); 1343 1344 if (_prefixes.contains(prefix)) 1345 throw error(L.l("The taglib prefix '{0}' must be defined before any matching prefixes are used.", 1346 prefix)); 1347 1348 String uri = null; 1349 p = keys.indexOf(URI); 1350 if (p >= 0) 1351 uri = values.get(p); 1352 1353 String tagdir = null; 1354 p = keys.indexOf(TAGDIR); 1355 if (p >= 0) 1356 tagdir = values.get(p); 1357 1358 if (uri != null) 1359 processTaglib(prefix, uri); 1360 else if (tagdir != null) 1361 processTaglibDir(prefix, tagdir); 1362 } 1363 1364 1367 private void processTaglib(String prefix, String uri) 1368 throws JspParseException 1369 { 1370 Taglib taglib = null; 1371 1372 int colon = uri.indexOf(':'); 1373 int slash = uri.indexOf('/'); 1374 1375 String location = null; 1376 1377 if (colon > 0 && colon < slash) 1378 location = uri; 1379 else if (slash == 0) 1380 location = uri; 1381 else 1382 location = _uriPwd + uri; 1383 1384 try { 1385 taglib = _tagManager.addTaglib(prefix, uri, location); 1386 String tldURI = "urn:jsptld:" + uri; 1387 1388 _parseState.pushNamespace(prefix, tldURI); 1389 _namespaces = new Namespace(_namespaces, prefix, tldURI); 1390 return; 1391 } catch (JspParseException e) { 1392 throw error(e); 1393 } catch (Exception e) { 1394 log.log(Level.WARNING, e.toString(), e); 1395 } 1396 1397 if (colon > 0 && colon < slash) 1398 throw error(L.l("Unknown taglib '{0}'. Taglibs specified with an absolute URI must either be:\n1) specified in the web.xml\n2) defined in a jar's .tld in META-INF\n3) defined in a .tld in WEB-INF\n4) predefined by Resin", 1399 uri)); 1400 } 1401 1402 1405 private void processTaglibDir(String prefix, String tagDir) 1406 throws JspParseException 1407 { 1408 Taglib taglib = null; 1409 1410 try { 1411 taglib = _tagManager.addTaglibDir(prefix, tagDir); 1412 String tagURI = "urn:jsptagdir:" + tagDir; 1413 _parseState.pushNamespace(prefix, tagURI); 1414 _namespaces = new Namespace(_namespaces, prefix, tagURI); 1415 return; 1416 } catch (JspParseException e) { 1417 throw error(e); 1418 } catch (Exception e) { 1419 log.log(Level.WARNING, e.toString(), e); 1420 } 1421 } 1422 1423 private void processIncludeDirective(ArrayList keys, ArrayList values) 1424 throws IOException , JspParseException 1425 { 1426 int p = keys.indexOf("file"); 1427 if (p < 0) 1428 throw error(L.l("The include directive requires a 'file' attribute.")); 1429 String file = (String ) values.get(p); 1430 1431 pushInclude(file); 1432 } 1433 1434 public void pushInclude(String value) 1435 throws IOException , JspParseException 1436 { 1437 pushInclude(value, false); 1438 } 1439 1440 public void pushInclude(String value, boolean allowDuplicate) 1441 throws IOException , JspParseException 1442 { 1443 if (value.equals("")) 1444 throw error("include directive needs 'file' attribute. Use either <%@ include file='myfile.jsp' %> or <jsp:directive.include file='myfile.jsp'/>"); 1445 1446 Path include; 1447 if (value.length() > 0 && value.charAt(0) == '/') 1448 include = _parseState.resolvePath(value); 1449 else 1450 include = _parseState.resolvePath(_uriPwd + value); 1451 1452 String newUrl = _uriPwd; 1453 1454 if (value.startsWith("/")) 1455 newUrl = value; 1456 else 1457 newUrl = _uriPwd + value; 1458 1459 include.setUserPath(newUrl); 1460 1461 String newUrlPwd; 1462 int p = newUrl.lastIndexOf('/'); 1463 newUrlPwd = newUrl.substring(0, p + 1); 1464 1465 if (_jspPath != null && _jspPath.equals(include) && ! allowDuplicate) 1466 throw error(L.l("circular include of '{0}' forbidden. A JSP file may not include itself.", include)); 1467 for (int i = 0; i < _includes.size(); i++) { 1468 Include inc = _includes.get(i); 1469 if (inc._stream.getPath().equals(include) && ! allowDuplicate) 1470 throw error(L.l("circular include of '{0}'. A JSP file may not include itself.", include)); 1471 } 1472 1473 try { 1474 addInclude(include.openRead(), newUrlPwd); 1475 } catch (IOException e) { 1476 log.log(Level.WARNING, e.toString(), e); 1477 1478 if (include.exists()) 1479 throw error(L.l("can't open include of '{0}'. '{1}' exists but it's not readable.", 1480 value, include.getNativePath())); 1481 else 1482 throw error(L.l("can't open include of '{0}'. '{1}' does not exist.", 1483 value, include.getNativePath())); 1484 } 1485 } 1486 1487 private void addInclude(ReadStream stream, String newUrlPwd) 1488 throws IOException , JspParseException 1489 { 1490 addText(); 1491 1492 readLf(); 1493 1494 Include inc = null; 1495 1496 if (_stream != null) { 1497 inc = new Include(_stream, _line, _uriPwd); 1498 1499 _includes.add(inc); 1500 } 1501 1502 _parseState.addDepend(stream.getPath()); 1503 1504 try { 1505 String encoding = _stream.getEncoding(); 1506 if (encoding != null) 1507 stream.setEncoding(encoding); 1508 } catch (Exception e) { 1509 } 1510 _stream = stream; 1511 1512 _filename = stream.getUserPath(); 1513 _jspPath = stream.getPath(); 1514 _line = 1; 1515 _lineStart = _line; 1516 _uriPwd = newUrlPwd; 1517 _parseState.setUriPwd(_uriPwd); 1518 } 1519 1520 1527 private int skipWhitespace(int ch) 1528 throws IOException , JspParseException 1529 { 1530 for (; XmlChar.isWhitespace(ch); ch = read()) { 1531 } 1532 1533 return ch; 1534 } 1535 1536 1543 private int skipWhitespaceToEndOfLine(int ch) 1544 throws IOException , JspParseException 1545 { 1546 for (; XmlChar.isWhitespace(ch); ch = read()) { 1547 if (ch == '\n') 1548 return read(); 1549 else if (ch == '\r') { 1550 ch = read(); 1551 if (ch == '\n') 1552 return read(); 1553 else 1554 return ch; 1555 } 1556 } 1557 1558 return ch; 1559 } 1560 1561 private void addText(char ch) 1562 { 1563 _text.append(ch); 1564 } 1565 1566 private void addText(String s) 1567 { 1568 _text.append(s); 1569 } 1570 1571 private void addText() 1572 throws JspParseException 1573 { 1574 if (_text.length() > 0) 1575 createText(); 1576 1577 _startText = _charCount; 1578 _lineStart = _line; 1579 } 1580 1581 private void createText() 1582 throws JspParseException 1583 { 1584 String string = _text.toString(); 1585 1586 setLocation(_jspPath, _filename, _lineStart); 1587 1588 if (_parseState.isTrimWhitespace() && isWhitespace(string)) { 1589 } 1590 else 1591 _jspBuilder.text(string, _filename, _lineStart, _line); 1592 1593 _lineStart = _line; 1594 _text.clear(); 1595 _startText = _charCount; 1596 } 1597 1598 private boolean isWhitespace(String s) 1599 { 1600 int length = s.length(); 1601 1602 for (int i = 0; i < length; i++) { 1603 if (! Character.isWhitespace(s.charAt(i))) 1604 return false; 1605 } 1606 1607 return true; 1608 } 1609 1610 1611 1614 private int getTag(String name) 1615 throws JspParseException 1616 { 1617 int p = name.indexOf(':'); 1618 if (p < 0) 1619 return TAG_UNKNOWN; 1620 1621 String prefix = name.substring(0, p); 1622 String local = name.substring(p + 1); 1623 1624 _prefixes.add(prefix); 1625 1626 String url = Namespace.find(_namespaces, prefix); 1627 1628 if (url != null) 1629 return TAG_JSP; 1630 else 1631 return TAG_UNKNOWN; 1632 1633 1648 } 1649 1650 private void unread(int ch) 1651 { 1652 _peek = ch; 1653 } 1654 1655 1658 private void readLf() throws IOException , JspParseException 1659 { 1660 if (_seenCr) { 1661 int ch = read(); 1662 1663 if (ch != '\n') 1664 _peek = ch; 1665 } 1666 } 1667 1668 1671 private int read() throws IOException , JspParseException 1672 { 1673 int ch; 1674 1675 if (_peek >= 0) { 1676 ch = _peek; 1677 _peek = -1; 1678 return ch; 1679 } 1680 1681 try { 1682 if ((ch = _stream.readChar()) >= 0) { 1683 _charCount++; 1684 1685 if (ch == '\r') { 1686 _line++; 1687 _charCount = 0; 1688 _seenCr = true; 1689 } 1690 else if (ch == '\n' && _seenCr) { 1691 _seenCr = false; 1692 _charCount = 0; 1693 } 1694 else if (ch == '\n') { 1695 _line++; 1696 _charCount = 0; 1697 } 1698 else { 1699 _seenCr = false; 1700 } 1701 1702 return ch; 1703 } 1704 } catch (IOException e) { 1705 throw error(e.toString()); 1706 } 1707 1708 _stream.close(); 1709 _seenCr = false; 1710 1711 if (_includes.size() > 0) { 1712 setLocation(_jspPath, _filename, _line); 1713 1714 Include include = _includes.get(_includes.size() - 1); 1715 _includes.remove(_includes.size() - 1); 1716 1717 _stream = include._stream; 1718 _filename = _stream.getUserPath(); 1719 _jspPath = _stream.getPath(); 1720 _line = include._line; 1721 _lineStart = _line; 1722 _uriPwd = include._uriPwd; 1723 _parseState.setUriPwd(_uriPwd); 1724 1725 setLocation(_jspPath, _filename, _line); 1726 1727 return read(); 1728 } 1729 1730 return -1; 1731 } 1732 1733 void clear(Path appDir, String errorPage) 1734 { 1735 } 1736 1737 1742 public JspParseException error(Exception e) 1743 { 1744 String message = e.getMessage(); 1745 1746 if (e instanceof JspParseException) { 1747 log.log(Level.FINE, e.toString(), e); 1748 } 1749 1750 if (e instanceof JspLineParseException) 1751 return (JspLineParseException) e; 1752 else if (e instanceof LineCompileException) 1753 return new JspLineParseException(e); 1754 1755 if (_lineMap == null) 1756 return new JspLineParseException(_filename + ":" + _line + ": " + message, 1757 e); 1758 else { 1759 LineMap.Line line = _lineMap.getLine(_line); 1760 1761 return new JspLineParseException(line.getSourceFilename() + ":" + 1762 line.getSourceLine(_line) + ": " + 1763 message, 1764 e); 1765 } 1766 } 1767 1768 1773 public JspParseException error(String message) 1774 { 1775 JspGenerator gen = _jspBuilder.getGenerator(); 1776 1777 if (_lineMap == null) { 1778 if (gen != null) 1779 return new JspLineParseException(_filename + ":" + _line + ": " + message + gen.getSourceLines(_jspPath, _line)); 1780 else 1781 return new JspLineParseException(_filename + ":" + _line + ": " + message); 1782 } 1783 else { 1784 LineMap.Line line = _lineMap.getLine(_line); 1785 1786 return new JspLineParseException(line.getSourceFilename() + ":" + 1787 line.getSourceLine(_line) + ": " + 1788 message); 1789 } 1790 } 1791 1792 1797 public JspParseException error(String message, Throwable e) 1798 { 1799 if (_lineMap == null) 1800 return new JspLineParseException(_filename + ":" + _line + ": " + message, e); 1801 else { 1802 LineMap.Line line = _lineMap.getLine(_line); 1803 1804 return new JspLineParseException(line.getSourceFilename() + ":" + 1805 line.getSourceLine(_line) + ": " + 1806 message, 1807 e); 1808 } 1809 } 1810 1811 1814 private void setLocation() 1815 { 1816 setLocation(_jspPath, _filename, _line); 1817 } 1818 1819 1825 private void setLocation(Path jspPath, String filename, int line) 1826 { 1827 if (_lineMap == null) { 1828 _jspBuilder.setLocation(jspPath, filename, line); 1829 } 1830 else { 1831 LineMap.Line srcLine = _lineMap.getLine(line); 1832 1833 if (srcLine != null) { 1834 _jspBuilder.setLocation(jspPath, 1835 srcLine.getSourceFilename(), 1836 srcLine.getSourceLine(line)); 1837 } 1838 } 1839 } 1840 1841 private String badChar(int ch) 1842 { 1843 if (ch < 0) 1844 return "end of file"; 1845 else if (ch == '\n' || ch == '\r') 1846 return "end of line"; 1847 else if (ch >= 0x20 && ch <= 0x7f) 1848 return "'" + (char) ch + "'"; 1849 else 1850 return "'" + (char) ch + "' (\\u" + hex(ch) + ")"; 1851 } 1852 1853 private String hex(int value) 1854 { 1855 CharBuffer cb = new CharBuffer(); 1856 1857 for (int b = 3; b >= 0; b--) { 1858 int v = (value >> (4 * b)) & 0xf; 1859 if (v < 10) 1860 cb.append((char) (v + '0')); 1861 else 1862 cb.append((char) (v - 10 + 'a')); 1863 } 1864 1865 return cb.toString(); 1866 } 1867 1868 class Include { 1869 ReadStream _stream; 1870 int _line; 1871 String _uriPwd; 1872 1873 Include(ReadStream stream, int line, String uriPwd) 1874 { 1875 _stream = stream; 1876 _line = line; 1877 _uriPwd = uriPwd; 1878 } 1879 } 1880} 1881 | Popular Tags |