| 1 17 package org.apache.jasper.compiler; 18 19 import java.io.CharArrayWriter ; 20 import java.io.FileNotFoundException ; 21 import java.net.URL ; 22 import java.util.Iterator ; 23 import java.util.List ; 24 25 import javax.servlet.jsp.tagext.TagAttributeInfo ; 26 import javax.servlet.jsp.tagext.TagFileInfo ; 27 import javax.servlet.jsp.tagext.TagInfo ; 28 import javax.servlet.jsp.tagext.TagLibraryInfo ; 29 30 import org.apache.jasper.Constants; 31 import org.apache.jasper.JasperException; 32 import org.apache.jasper.JspCompilationContext; 33 import org.xml.sax.Attributes ; 34 import org.xml.sax.helpers.AttributesImpl ; 35 36 45 46 class Parser implements TagConstants { 47 48 private ParserController parserController; 49 50 private JspCompilationContext ctxt; 51 52 private JspReader reader; 53 54 private String currentFile; 55 56 private Mark start; 57 58 private ErrorDispatcher err; 59 60 private int scriptlessCount; 61 62 private boolean isTagFile; 63 64 private boolean directivesOnly; 65 66 private URL jarFileUrl; 67 68 private PageInfo pageInfo; 69 70 private static final String JAVAX_BODY_CONTENT_PARAM = "JAVAX_BODY_CONTENT_PARAM"; 73 74 private static final String JAVAX_BODY_CONTENT_PLUGIN = "JAVAX_BODY_CONTENT_PLUGIN"; 75 76 private static final String JAVAX_BODY_CONTENT_TEMPLATE_TEXT = "JAVAX_BODY_CONTENT_TEMPLATE_TEXT"; 77 78 81 private Parser(ParserController pc, JspReader reader, boolean isTagFile, 82 boolean directivesOnly, URL jarFileUrl) { 83 this.parserController = pc; 84 this.ctxt = pc.getJspCompilationContext(); 85 this.pageInfo = pc.getCompiler().getPageInfo(); 86 this.err = pc.getCompiler().getErrorDispatcher(); 87 this.reader = reader; 88 this.currentFile = reader.mark().getFile(); 89 this.scriptlessCount = 0; 90 this.isTagFile = isTagFile; 91 this.directivesOnly = directivesOnly; 92 this.jarFileUrl = jarFileUrl; 93 start = reader.mark(); 94 } 95 96 108 public static Node.Nodes parse(ParserController pc, JspReader reader, 109 Node parent, boolean isTagFile, boolean directivesOnly, 110 URL jarFileUrl, String pageEnc, String jspConfigPageEnc, 111 boolean isDefaultPageEncoding, boolean isBomPresent) throws JasperException { 112 113 Parser parser = new Parser(pc, reader, isTagFile, directivesOnly, 114 jarFileUrl); 115 116 Node.Root root = new Node.Root(reader.mark(), parent, false); 117 root.setPageEncoding(pageEnc); 118 root.setJspConfigPageEncoding(jspConfigPageEnc); 119 root.setIsDefaultPageEncoding(isDefaultPageEncoding); 120 root.setIsBomPresent(isBomPresent); 121 122 if (directivesOnly) { 123 parser.parseTagFileDirectives(root); 124 return new Node.Nodes(root); 125 } 126 127 PageInfo pageInfo = pc.getCompiler().getPageInfo(); 129 if (parent == null) { 130 parser.addInclude(root, pageInfo.getIncludePrelude()); 131 } 132 while (reader.hasMoreInput()) { 133 parser.parseElements(root); 134 } 135 if (parent == null) { 136 parser.addInclude(root, pageInfo.getIncludeCoda()); 137 } 138 139 Node.Nodes page = new Node.Nodes(root); 140 return page; 141 } 142 143 146 Attributes parseAttributes() throws JasperException { 147 AttributesImpl attrs = new AttributesImpl (); 148 149 reader.skipSpaces(); 150 while (parseAttribute(attrs)) 151 reader.skipSpaces(); 152 153 return attrs; 154 } 155 156 159 public static Attributes parseAttributes(ParserController pc, 160 JspReader reader) throws JasperException { 161 Parser tmpParser = new Parser(pc, reader, false, false, null); 162 return tmpParser.parseAttributes(); 163 } 164 165 172 private boolean parseAttribute(AttributesImpl attrs) throws JasperException { 173 174 String qName = parseName(); 176 if (qName == null) 177 return false; 178 179 String localName = qName; 181 String uri = ""; 182 int index = qName.indexOf(':'); 183 if (index != -1) { 184 String prefix = qName.substring(0, index); 185 uri = pageInfo.getURI(prefix); 186 if (uri == null) { 187 err.jspError(reader.mark(), 188 "jsp.error.attribute.invalidPrefix", prefix); 189 } 190 localName = qName.substring(index + 1); 191 } 192 193 reader.skipSpaces(); 194 if (!reader.matches("=")) 195 err.jspError(reader.mark(), "jsp.error.attribute.noequal"); 196 197 reader.skipSpaces(); 198 char quote = (char) reader.nextChar(); 199 if (quote != '\'' && quote != '"') 200 err.jspError(reader.mark(), "jsp.error.attribute.noquote"); 201 202 String watchString = ""; 203 if (reader.matches("<%=")) 204 watchString = "%>"; 205 watchString = watchString + quote; 206 207 String attrValue = parseAttributeValue(watchString); 208 attrs.addAttribute(uri, localName, qName, "CDATA", attrValue); 209 return true; 210 } 211 212 215 private String parseName() throws JasperException { 216 char ch = (char) reader.peekChar(); 217 if (Character.isLetter(ch) || ch == '_' || ch == ':') { 218 StringBuffer buf = new StringBuffer (); 219 buf.append(ch); 220 reader.nextChar(); 221 ch = (char) reader.peekChar(); 222 while (Character.isLetter(ch) || Character.isDigit(ch) || ch == '.' 223 || ch == '_' || ch == '-' || ch == ':') { 224 buf.append(ch); 225 reader.nextChar(); 226 ch = (char) reader.peekChar(); 227 } 228 return buf.toString(); 229 } 230 return null; 231 } 232 233 238 private String parseAttributeValue(String watch) throws JasperException { 239 Mark start = reader.mark(); 240 Mark stop = reader.skipUntilIgnoreEsc(watch); 241 if (stop == null) { 242 err.jspError(start, "jsp.error.attribute.unterminated", watch); 243 } 244 245 String ret = parseQuoted(reader.getText(start, stop)); 246 if (watch.length() == 1) return ret; 248 249 return "<%=" + ret + "%>"; 252 } 253 254 258 private String parseQuoted(String tx) { 259 StringBuffer buf = new StringBuffer (); 260 int size = tx.length(); 261 int i = 0; 262 while (i < size) { 263 char ch = tx.charAt(i); 264 if (ch == '&') { 265 if (i + 5 < size && tx.charAt(i + 1) == 'a' 266 && tx.charAt(i + 2) == 'p' && tx.charAt(i + 3) == 'o' 267 && tx.charAt(i + 4) == 's' && tx.charAt(i + 5) == ';') { 268 buf.append('\''); 269 i += 6; 270 } else if (i + 5 < size && tx.charAt(i + 1) == 'q' 271 && tx.charAt(i + 2) == 'u' && tx.charAt(i + 3) == 'o' 272 && tx.charAt(i + 4) == 't' && tx.charAt(i + 5) == ';') { 273 buf.append('"'); 274 i += 6; 275 } else { 276 buf.append(ch); 277 ++i; 278 } 279 } else if (ch == '\\' && i + 1 < size) { 280 ch = tx.charAt(i + 1); 281 if (ch == '\\' || ch == '\"' || ch == '\'' || ch == '>') { 282 buf.append(ch); 283 i += 2; 284 } else if (ch == '$') { 285 buf.append(Constants.ESC); 287 i += 2; 288 } else { 289 buf.append('\\'); 290 ++i; 291 } 292 } else { 293 buf.append(ch); 294 ++i; 295 } 296 } 297 return buf.toString(); 298 } 299 300 private String parseScriptText(String tx) { 301 CharArrayWriter cw = new CharArrayWriter (); 302 int size = tx.length(); 303 int i = 0; 304 while (i < size) { 305 char ch = tx.charAt(i); 306 if (i + 2 < size && ch == '%' && tx.charAt(i + 1) == '\\' 307 && tx.charAt(i + 2) == '>') { 308 cw.write('%'); 309 cw.write('>'); 310 i += 3; 311 } else { 312 cw.write(ch); 313 ++i; 314 } 315 } 316 cw.close(); 317 return cw.toString(); 318 } 319 320 323 private void processIncludeDirective(String file, Node parent) 324 throws JasperException { 325 if (file == null) { 326 return; 327 } 328 329 try { 330 parserController.parse(file, parent, jarFileUrl); 331 } catch (FileNotFoundException ex) { 332 err.jspError(start, "jsp.error.file.not.found", file); 333 } catch (Exception ex) { 334 err.jspError(start, ex.getMessage()); 335 } 336 } 337 338 342 private void parsePageDirective(Node parent) throws JasperException { 343 Attributes attrs = parseAttributes(); 344 Node.PageDirective n = new Node.PageDirective(attrs, start, parent); 345 346 351 for (int i = 0; i < attrs.getLength(); i++) { 352 if ("import".equals(attrs.getQName(i))) { 353 n.addImport(attrs.getValue(i)); 354 } 355 } 356 } 357 358 362 private void parseIncludeDirective(Node parent) throws JasperException { 363 Attributes attrs = parseAttributes(); 364 365 Node includeNode = new Node.IncludeDirective(attrs, start, parent); 367 processIncludeDirective(attrs.getValue("file"), includeNode); 368 } 369 370 374 private void addInclude(Node parent, List files) throws JasperException { 375 if (files != null) { 376 Iterator iter = files.iterator(); 377 while (iter.hasNext()) { 378 String file = (String ) iter.next(); 379 AttributesImpl attrs = new AttributesImpl (); 380 attrs.addAttribute("", "file", "file", "CDATA", file); 381 382 Node includeNode = new Node.IncludeDirective(attrs, reader 384 .mark(), parent); 385 processIncludeDirective(file, includeNode); 386 } 387 } 388 } 389 390 394 private void parseTaglibDirective(Node parent) throws JasperException { 395 396 Attributes attrs = parseAttributes(); 397 String uri = attrs.getValue("uri"); 398 String prefix = attrs.getValue("prefix"); 399 if (prefix != null) { 400 Mark prevMark = pageInfo.getNonCustomTagPrefix(prefix); 401 if (prevMark != null) { 402 err.jspError(reader.mark(), "jsp.error.prefix.use_before_dcl", 403 prefix, prevMark.getFile(), "" 404 + prevMark.getLineNumber()); 405 } 406 if (uri != null) { 407 String uriPrev = pageInfo.getURI(prefix); 408 if (uriPrev != null && !uriPrev.equals(uri)) { 409 err.jspError(reader.mark(), "jsp.error.prefix.refined", 410 prefix, uri, uriPrev); 411 } 412 if (pageInfo.getTaglib(uri) == null) { 413 TagLibraryInfoImpl impl = null; 414 if (ctxt.getOptions().isCaching()) { 415 impl = (TagLibraryInfoImpl) ctxt.getOptions() 416 .getCache().get(uri); 417 } 418 if (impl == null) { 419 String [] location = ctxt.getTldLocation(uri); 420 impl = new TagLibraryInfoImpl(ctxt, parserController, pageInfo, 421 prefix, uri, location, err); 422 if (ctxt.getOptions().isCaching()) { 423 ctxt.getOptions().getCache().put(uri, impl); 424 } 425 } 426 pageInfo.addTaglib(uri, impl); 427 } 428 pageInfo.addPrefixMapping(prefix, uri); 429 } else { 430 String tagdir = attrs.getValue("tagdir"); 431 if (tagdir != null) { 432 String urnTagdir = URN_JSPTAGDIR + tagdir; 433 if (pageInfo.getTaglib(urnTagdir) == null) { 434 pageInfo.addTaglib(urnTagdir, 435 new ImplicitTagLibraryInfo(ctxt, 436 parserController, pageInfo, prefix, tagdir, err)); 437 } 438 pageInfo.addPrefixMapping(prefix, urnTagdir); 439 } 440 } 441 } 442 443 new Node.TaglibDirective(attrs, start, parent); 444 } 445 446 455 private void parseDirective(Node parent) throws JasperException { 456 reader.skipSpaces(); 457 458 String directive = null; 459 if (reader.matches("page")) { 460 directive = "<%@ page"; 461 if (isTagFile) { 462 err.jspError(reader.mark(), "jsp.error.directive.istagfile", 463 directive); 464 } 465 parsePageDirective(parent); 466 } else if (reader.matches("include")) { 467 directive = "<%@ include"; 468 parseIncludeDirective(parent); 469 } else if (reader.matches("taglib")) { 470 if (directivesOnly) { 471 return; 474 } 475 directive = "<%@ taglib"; 476 parseTaglibDirective(parent); 477 } else if (reader.matches("tag")) { 478 directive = "<%@ tag"; 479 if (!isTagFile) { 480 err.jspError(reader.mark(), "jsp.error.directive.isnottagfile", 481 directive); 482 } 483 parseTagDirective(parent); 484 } else if (reader.matches("attribute")) { 485 directive = "<%@ attribute"; 486 if (!isTagFile) { 487 err.jspError(reader.mark(), "jsp.error.directive.isnottagfile", 488 directive); 489 } 490 parseAttributeDirective(parent); 491 } else if (reader.matches("variable")) { 492 directive = "<%@ variable"; 493 if (!isTagFile) { 494 err.jspError(reader.mark(), "jsp.error.directive.isnottagfile", 495 directive); 496 } 497 parseVariableDirective(parent); 498 } else { 499 err.jspError(reader.mark(), "jsp.error.invalid.directive"); 500 } 501 502 reader.skipSpaces(); 503 if (!reader.matches("%>")) { 504 err.jspError(start, "jsp.error.unterminated", directive); 505 } 506 } 507 508 521 private void parseXMLDirective(Node parent) throws JasperException { 522 reader.skipSpaces(); 523 524 String eTag = null; 525 if (reader.matches("page")) { 526 eTag = "jsp:directive.page"; 527 if (isTagFile) { 528 err.jspError(reader.mark(), "jsp.error.directive.istagfile", 529 "<" + eTag); 530 } 531 parsePageDirective(parent); 532 } else if (reader.matches("include")) { 533 eTag = "jsp:directive.include"; 534 parseIncludeDirective(parent); 535 } else if (reader.matches("tag")) { 536 eTag = "jsp:directive.tag"; 537 if (!isTagFile) { 538 err.jspError(reader.mark(), "jsp.error.directive.isnottagfile", 539 "<" + eTag); 540 } 541 parseTagDirective(parent); 542 } else if (reader.matches("attribute")) { 543 eTag = "jsp:directive.attribute"; 544 if (!isTagFile) { 545 err.jspError(reader.mark(), "jsp.error.directive.isnottagfile", 546 "<" + eTag); 547 } 548 parseAttributeDirective(parent); 549 } else if (reader.matches("variable")) { 550 eTag = "jsp:directive.variable"; 551 if (!isTagFile) { 552 err.jspError(reader.mark(), "jsp.error.directive.isnottagfile", 553 "<" + eTag); 554 } 555 parseVariableDirective(parent); 556 } else { 557 err.jspError(reader.mark(), "jsp.error.invalid.directive"); 558 } 559 560 reader.skipSpaces(); 561 if (reader.matches(">")) { 562 reader.skipSpaces(); 563 if (!reader.matchesETag(eTag)) { 564 err.jspError(start, "jsp.error.unterminated", "<" + eTag); 565 } 566 } else if (!reader.matches("/>")) { 567 err.jspError(start, "jsp.error.unterminated", "<" + eTag); 568 } 569 } 570 571 575 private void parseTagDirective(Node parent) throws JasperException { 576 Attributes attrs = parseAttributes(); 577 Node.TagDirective n = new Node.TagDirective(attrs, start, parent); 578 579 584 for (int i = 0; i < attrs.getLength(); i++) { 585 if ("import".equals(attrs.getQName(i))) { 586 n.addImport(attrs.getValue(i)); 587 } 588 } 589 } 590 591 595 private void parseAttributeDirective(Node parent) throws JasperException { 596 Attributes attrs = parseAttributes(); 597 Node.AttributeDirective n = new Node.AttributeDirective(attrs, start, 598 parent); 599 } 600 601 605 private void parseVariableDirective(Node parent) throws JasperException { 606 Attributes attrs = parseAttributes(); 607 Node.VariableDirective n = new Node.VariableDirective(attrs, start, 608 parent); 609 } 610 611 614 private void parseComment(Node parent) throws JasperException { 615 start = reader.mark(); 616 Mark stop = reader.skipUntil("--%>"); 617 if (stop == null) { 618 err.jspError(start, "jsp.error.unterminated", "<%--"); 619 } 620 621 new Node.Comment(reader.getText(start, stop), start, parent); 622 } 623 624 627 private void parseDeclaration(Node parent) throws JasperException { 628 start = reader.mark(); 629 Mark stop = reader.skipUntil("%>"); 630 if (stop == null) { 631 err.jspError(start, "jsp.error.unterminated", "<%!"); 632 } 633 634 new Node.Declaration(parseScriptText(reader.getText(start, stop)), 635 start, parent); 636 } 637 638 644 private void parseXMLDeclaration(Node parent) throws JasperException { 645 reader.skipSpaces(); 646 if (!reader.matches("/>")) { 647 if (!reader.matches(">")) { 648 err.jspError(start, "jsp.error.unterminated", 649 "<jsp:declaration>"); 650 } 651 Mark stop; 652 String text; 653 while (true) { 654 start = reader.mark(); 655 stop = reader.skipUntil("<"); 656 if (stop == null) { 657 err.jspError(start, "jsp.error.unterminated", 658 "<jsp:declaration>"); 659 } 660 text = parseScriptText(reader.getText(start, stop)); 661 new Node.Declaration(text, start, parent); 662 if (reader.matches("![CDATA[")) { 663 start = reader.mark(); 664 stop = reader.skipUntil("]]>"); 665 if (stop == null) { 666 err.jspError(start, "jsp.error.unterminated", "CDATA"); 667 } 668 text = parseScriptText(reader.getText(start, stop)); 669 new Node.Declaration(text, start, parent); 670 } else { 671 break; 672 } 673 } 674 675 if (!reader.matchesETagWithoutLessThan("jsp:declaration")) { 676 err.jspError(start, "jsp.error.unterminated", 677 "<jsp:declaration>"); 678 } 679 } 680 } 681 682 685 private void parseExpression(Node parent) throws JasperException { 686 start = reader.mark(); 687 Mark stop = reader.skipUntil("%>"); 688 if (stop == null) { 689 err.jspError(start, "jsp.error.unterminated", "<%="); 690 } 691 692 new Node.Expression(parseScriptText(reader.getText(start, stop)), 693 start, parent); 694 } 695 696 700 private void parseXMLExpression(Node parent) throws JasperException { 701 reader.skipSpaces(); 702 if (!reader.matches("/>")) { 703 if (!reader.matches(">")) { 704 err.jspError(start, "jsp.error.unterminated", 705 "<jsp:expression>"); 706 } 707 Mark stop; 708 String text; 709 while (true) { 710 start = reader.mark(); 711 stop = reader.skipUntil("<"); 712 if (stop == null) { 713 err.jspError(start, "jsp.error.unterminated", 714 "<jsp:expression>"); 715 } 716 text = parseScriptText(reader.getText(start, stop)); 717 new Node.Expression(text, start, parent); 718 if (reader.matches("![CDATA[")) { 719 start = reader.mark(); 720 stop = reader.skipUntil("]]>"); 721 if (stop == null) { 722 err.jspError(start, "jsp.error.unterminated", "CDATA"); 723 } 724 text = parseScriptText(reader.getText(start, stop)); 725 new Node.Expression(text, start, parent); 726 } else { 727 break; 728 } 729 } 730 if (!reader.matchesETagWithoutLessThan("jsp:expression")) { 731 err.jspError(start, "jsp.error.unterminated", 732 "<jsp:expression>"); 733 } 734 } 735 } 736 737 741 private void parseELExpression(Node parent, char type) throws JasperException { 742 start = reader.mark(); 743 |