1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.struts2.jasper.compiler;
18
19 import org.apache.struts2.jasper.Constants;
20 import org.apache.struts2.jasper.JasperException;
21 import org.apache.struts2.jasper.JspCompilationContext;
22 import org.xml.sax.Attributes;
23 import org.xml.sax.helpers.AttributesImpl;
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 import java.io.CharArrayWriter;
30 import java.io.FileNotFoundException;
31 import java.net.URL;
32 import java.util.Iterator;
33 import java.util.List;
34
35 /***
36 * This class implements a parser for a JSP page (non-xml view).
37 * JSP page grammar is included here for reference. The token '#'
38 * that appears in the production indicates the current input token
39 * location in the production.
40 *
41 * @author Kin-man Chung
42 * @author Shawn Bayern
43 * @author Mark Roth
44 */
45
46 class Parser implements TagConstants {
47
48 private ParserController parserController;
49 private JspCompilationContext ctxt;
50 private JspReader reader;
51 private String currentFile;
52 private Mark start;
53 private ErrorDispatcher err;
54 private int scriptlessCount;
55 private boolean isTagFile;
56 private boolean directivesOnly;
57 private URL jarFileUrl;
58 private PageInfo pageInfo;
59
60
61
62 private static final String JAVAX_BODY_CONTENT_PARAM =
63 "JAVAX_BODY_CONTENT_PARAM";
64 private static final String JAVAX_BODY_CONTENT_PLUGIN =
65 "JAVAX_BODY_CONTENT_PLUGIN";
66 private static final String JAVAX_BODY_CONTENT_TEMPLATE_TEXT =
67 "JAVAX_BODY_CONTENT_TEMPLATE_TEXT";
68
69 private static final boolean STRICT_QUOTE_ESCAPING = Boolean.valueOf(
70 System.getProperty(
71 "org.apache.struts2.jasper.compiler.Parser.STRICT_QUOTE_ESCAPING",
72 "true")).booleanValue();
73
74 /***
75 * The constructor
76 */
77 private Parser(ParserController pc, JspReader reader, boolean isTagFile,
78 boolean directivesOnly, URL jarFileUrl) {
79 this.parserController = pc;
80 this.ctxt = pc.getJspCompilationContext();
81 this.pageInfo = pc.getCompiler().getPageInfo();
82 this.err = pc.getCompiler().getErrorDispatcher();
83 this.reader = reader;
84 this.currentFile = reader.mark().getFile();
85 this.scriptlessCount = 0;
86 this.isTagFile = isTagFile;
87 this.directivesOnly = directivesOnly;
88 this.jarFileUrl = jarFileUrl;
89 start = reader.mark();
90 }
91
92 /***
93 * The main entry for Parser
94 *
95 * @param pc The ParseController, use for getting other objects in compiler
96 * and for parsing included pages
97 * @param reader To read the page
98 * @param parent The parent node to this page, null for top level page
99 * @return list of nodes representing the parsed page
100 */
101 public static Node.Nodes parse(ParserController pc,
102 JspReader reader,
103 Node parent,
104 boolean isTagFile,
105 boolean directivesOnly,
106 URL jarFileUrl,
107 String pageEnc,
108 String jspConfigPageEnc,
109 boolean isDefaultPageEncoding)
110 throws JasperException {
111
112 Parser parser = new Parser(pc, reader, isTagFile, directivesOnly,
113 jarFileUrl);
114
115 Node.Root root = new Node.Root(reader.mark(), parent, false);
116 root.setPageEncoding(pageEnc);
117 root.setJspConfigPageEncoding(jspConfigPageEnc);
118 root.setIsDefaultPageEncoding(isDefaultPageEncoding);
119
120 if (directivesOnly) {
121 parser.parseTagFileDirectives(root);
122 return new Node.Nodes(root);
123 }
124
125
126 PageInfo pageInfo = pc.getCompiler().getPageInfo();
127 if (parent == null) {
128 parser.addInclude(root, pageInfo.getIncludePrelude());
129 }
130 while (reader.hasMoreInput()) {
131 parser.parseElements(root);
132 }
133 if (parent == null) {
134 parser.addInclude(root, pageInfo.getIncludeCoda());
135 }
136
137 Node.Nodes page = new Node.Nodes(root);
138 return page;
139 }
140
141 /***
142 * Attributes ::= (S Attribute)* S?
143 */
144 Attributes parseAttributes() throws JasperException {
145 AttributesImpl attrs = new AttributesImpl();
146
147 reader.skipSpaces();
148 while (parseAttribute(attrs))
149 reader.skipSpaces();
150
151 return attrs;
152 }
153
154 /***
155 * Parse Attributes for a reader, provided for external use
156 */
157 public static Attributes parseAttributes(ParserController pc,
158 JspReader reader)
159 throws JasperException {
160 Parser tmpParser = new Parser(pc, reader, false, false, null);
161 return tmpParser.parseAttributes();
162 }
163
164 /***
165 * Attribute ::= Name S? Eq S?
166 * ( '"<%=' RTAttributeValueDouble
167 * | '"' AttributeValueDouble
168 * | "'<%=" RTAttributeValueSingle
169 * | "'" AttributeValueSingle
170 * }
171 * Note: JSP and XML spec does not allow while spaces around Eq. It is
172 * added to be backward compatible with Tomcat, and with other xml parsers.
173 */
174 private boolean parseAttribute(AttributesImpl attrs)
175 throws JasperException {
176
177
178 String qName = parseName();
179 if (qName == null)
180 return false;
181
182
183 String localName = qName;
184 String uri = "";
185 int index = qName.indexOf(':');
186 if (index != -1) {
187 String prefix = qName.substring(0, index);
188 uri = pageInfo.getURI(prefix);
189 if (uri == null) {
190 err.jspError(reader.mark(),
191 "jsp.error.attribute.invalidPrefix", prefix);
192 }
193 localName = qName.substring(index + 1);
194 }
195
196 reader.skipSpaces();
197 if (!reader.matches("="))
198 err.jspError(reader.mark(), "jsp.error.attribute.noequal");
199
200 reader.skipSpaces();
201 char quote = (char) reader.nextChar();
202 if (quote != '\'' && quote != '"')
203 err.jspError(reader.mark(), "jsp.error.attribute.noquote");
204
205 String watchString = "";
206 if (reader.matches("<%="))
207 watchString = "%>";
208 watchString = watchString + quote;
209
210 String attrValue = parseAttributeValue(watchString);
211 attrs.addAttribute(uri, localName, qName, "CDATA", attrValue);
212 return true;
213 }
214
215 /***
216 * Name ::= (Letter | '_' | ':') (Letter | Digit | '.' | '_' | '-' | ':')*
217 */
218 private String parseName() throws JasperException {
219 char ch = (char) reader.peekChar();
220 if (Character.isLetter(ch) || ch == '_' || ch == ':') {
221 StringBuffer buf = new StringBuffer();
222 buf.append(ch);
223 reader.nextChar();
224 ch = (char) reader.peekChar();
225 while (Character.isLetter(ch) || Character.isDigit(ch) ||
226 ch == '.' || ch == '_' || ch == '-' || ch == ':') {
227 buf.append(ch);
228 reader.nextChar();
229 ch = (char) reader.peekChar();
230 }
231 return buf.toString();
232 }
233 return null;
234 }
235
236 /***
237 * AttributeValueDouble ::= (QuotedChar - '"')*
238 * ('"' | <TRANSLATION_ERROR>)
239 * RTAttributeValueDouble ::= ((QuotedChar - '"')* - ((QuotedChar-'"')'%>"')
240 * ('%>"' | TRANSLATION_ERROR)
241 */
242 private String parseAttributeValue(String watch) throws JasperException {
243 Mark start = reader.mark();
244 Mark stop = reader.skipUntilIgnoreEsc(watch);
245 if (stop == null) {
246 err.jspError(start, "jsp.error.attribute.unterminated", watch);
247 }
248
249 String ret = parseQuoted(start, reader.getText(start, stop),
250 watch.charAt(watch.length() - 1));
251 if (watch.length() == 1)
252 return ret;
253
254
255
256 return "<%=" + ret + "%>";
257 }
258
259 /***
260 * QuotedChar ::= '''
261 * | '"'
262 * | '//'
263 * | '\"'
264 * | "\'"
265 * | '\>'
266 * | '\$'
267 * | Char
268 */
269 private String parseQuoted(Mark start, String tx, char quote)
270 throws JasperException {
271 StringBuffer buf = new StringBuffer();
272 int size = tx.length();
273 int i = 0;
274 while (i < size) {
275 char ch = tx.charAt(i);
276 if (ch == '&') {
277 if (i + 5 < size && tx.charAt(i + 1) == 'a'
278 && tx.charAt(i + 2) == 'p' && tx.charAt(i + 3) == 'o'
279 && tx.charAt(i + 4) == 's' && tx.charAt(i + 5) == ';') {
280 buf.append('\'');
281 i += 6;
282 } else if (i + 5 < size && tx.charAt(i + 1) == 'q'
283 && tx.charAt(i + 2) == 'u' && tx.charAt(i + 3) == 'o'
284 && tx.charAt(i + 4) == 't' && tx.charAt(i + 5) == ';') {
285 buf.append('"');
286 i += 6;
287 } else {
288 buf.append(ch);
289 ++i;
290 }
291 } else if (ch == '//' && i + 1 < size) {
292 ch = tx.charAt(i + 1);
293 if (ch == '//' || ch == '\"' || ch == '\'' || ch == '>') {
294 buf.append(ch);
295 i += 2;
296 } else if (ch == '$') {
297
298 buf.append(Constants.HACK_CHAR);
299 i += 2;
300 } else {
301 buf.append('//');
302 ++i;
303 }
304 } else if (ch == quote && STRICT_QUOTE_ESCAPING) {
305
306 err.jspError(start, "jsp.error.attribute.noescape", tx,
307 "" + quote);
308 } else {
309 buf.append(ch);
310 ++i;
311 }
312 }
313 return buf.toString();
314 }
315
316 private String parseScriptText(String tx) {
317 CharArrayWriter cw = new CharArrayWriter();
318 int size = tx.length();
319 int i = 0;
320 while (i < size) {
321 char ch = tx.charAt(i);
322 if (i + 2 < size && ch == '%' && tx.charAt(i + 1) == '//'
323 && tx.charAt(i + 2) == '>') {
324 cw.write('%');
325 cw.write('>');
326 i += 3;
327 } else {
328 cw.write(ch);
329 ++i;
330 }
331 }
332 cw.close();
333 return cw.toString();
334 }
335
336
337
338
339 private void processIncludeDirective(String file, Node parent)
340 throws JasperException {
341 if (file == null) {
342 return;
343 }
344
345 try {
346 parserController.parse(file, parent, jarFileUrl);
347 } catch (FileNotFoundException ex) {
348 err.jspError(start, "jsp.error.file.not.found", file);
349 } catch (Exception ex) {
350 err.jspError(start, ex.getMessage());
351 }
352 }
353
354
355
356
357
358 private void parsePageDirective(Node parent) throws JasperException {
359 Attributes attrs = parseAttributes();
360 Node.PageDirective n = new Node.PageDirective(attrs, start, parent);
361
362
363
364
365
366
367 for (int i = 0; i < attrs.getLength(); i++) {
368 if ("import".equals(attrs.getQName(i))) {
369 n.addImport(attrs.getValue(i));
370 }
371 }
372 }
373
374
375
376
377
378 private void parseIncludeDirective(Node parent) throws JasperException {
379 Attributes attrs = parseAttributes();
380
381
382 Node includeNode = new Node.IncludeDirective(attrs, start, parent);
383 processIncludeDirective(attrs.getValue("file"), includeNode);
384 }
385
386 /***
387 * Add a list of files. This is used for implementing include-prelude
388 * and include-coda of jsp-config element in web.xml
389 */
390 private void addInclude(Node parent, List files) throws JasperException {
391 if (files != null) {
392 Iterator iter = files.iterator();
393 while (iter.hasNext()) {
394 String file = (String) iter.next();
395 AttributesImpl attrs = new AttributesImpl();
396 attrs.addAttribute("", "file", "file", "CDATA", file);
397
398
399 Node includeNode = new Node.IncludeDirective(attrs,
400 reader.mark(), parent);
401 processIncludeDirective(file, includeNode);
402 }
403 }
404 }
405
406
407
408
409
410 private void parseTaglibDirective(Node parent) throws JasperException {
411
412 Attributes attrs = parseAttributes();
413 String uri = attrs.getValue("uri");
414 String prefix = attrs.getValue("prefix");
415 if (prefix != null) {
416 Mark prevMark = pageInfo.getNonCustomTagPrefix(prefix);
417 if (prevMark != null) {
418 err.jspError(reader.mark(), "jsp.error.prefix.use_before_dcl",
419 prefix, prevMark.getFile(), "" + prevMark.getLineNumber());
420 }
421 if (uri != null) {
422 String uriPrev = pageInfo.getURI(prefix);
423 if (uriPrev != null && !uriPrev.equals(uri)) {
424 err.jspError(reader.mark(), "jsp.error.prefix.refined",
425 prefix, uri, uriPrev);
426 }
427 if (pageInfo.getTaglib(uri) == null) {
428 TagLibraryInfoImpl impl = null;
429 if (ctxt.getOptions().isCaching()) {
430 impl = (TagLibraryInfoImpl) ctxt.getOptions().getCache().get(uri);
431 }
432 if (impl == null) {
433 String[] location = ctxt.getTldLocation(uri);
434 impl = new TagLibraryInfoImpl(ctxt,
435 parserController,
436 prefix,
437 uri,
438 location,
439 err);
440 if (ctxt.getOptions().isCaching()) {
441 ctxt.getOptions().getCache().put(uri, impl);
442 }
443 }
444 pageInfo.addTaglib(uri, impl);
445 }
446 pageInfo.addPrefixMapping(prefix, uri);
447 } else {
448 String tagdir = attrs.getValue("tagdir");
449 if (tagdir != null) {
450 String urnTagdir = URN_JSPTAGDIR + tagdir;
451 if (pageInfo.getTaglib(urnTagdir) == null) {
452 pageInfo.addTaglib(urnTagdir,
453 new ImplicitTagLibraryInfo(
454 ctxt,
455 parserController,
456 prefix,
457 tagdir,
458 err));
459 }
460 pageInfo.addPrefixMapping(prefix, urnTagdir);
461 }
462 }
463 }
464
465 new Node.TaglibDirective(attrs, start, parent);
466 }
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482 private void parseDirective(Node parent) throws JasperException {
483 reader.skipSpaces();
484
485 String directive = null;
486 if (reader.matches("page")) {
487 directive = "<%@ page";
488 if (isTagFile) {
489 err.jspError(reader.mark(), "jsp.error.directive.istagfile",
490 directive);
491 }
492 parsePageDirective(parent);
493 } else if (reader.matches("include")) {
494 directive = "<%@ include";
495 parseIncludeDirective(parent);
496 } else if (reader.matches("taglib")) {
497 if (directivesOnly) {
498
499
500 return;
501 }
502 directive = "<%@ taglib";
503 parseTaglibDirective(parent);
504 } else if (reader.matches("tag")) {
505 directive = "<%@ tag";
506 if (!isTagFile) {
507 err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
508 directive);
509 }
510 parseTagDirective(parent);
511 } else if (reader.matches("attribute")) {
512 directive = "<%@ attribute";
513 if (!isTagFile) {
514 err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
515 directive);
516 }
517 parseAttributeDirective(parent);
518 } else if (reader.matches("variable")) {
519 directive = "<%@ variable";
520 if (!isTagFile) {
521 err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
522 directive);
523 }
524 parseVariableDirective(parent);
525 } else {
526 err.jspError(reader.mark(), "jsp.error.invalid.directive");
527 }
528
529 reader.skipSpaces();
530 if (!reader.matches("%>")) {
531 err.jspError(start, "jsp.error.unterminated", directive);
532 }
533 }
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555 private void parseXMLDirective(Node parent) throws JasperException {
556 reader.skipSpaces();
557
558 String eTag = null;
559 if (reader.matches("page")) {
560 eTag = "jsp:directive.page";
561 if (isTagFile) {
562 err.jspError(reader.mark(), "jsp.error.directive.istagfile",
563 "<" + eTag);
564 }
565 parsePageDirective(parent);
566 } else if (reader.matches("include")) {
567 eTag = "jsp:directive.include";
568 parseIncludeDirective(parent);
569 } else if (reader.matches("tag")) {
570 eTag = "jsp:directive.tag";
571 if (!isTagFile) {
572 err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
573 "<" + eTag);
574 }
575 parseTagDirective(parent);
576 } else if (reader.matches("attribute")) {
577 eTag = "jsp:directive.attribute";
578 if (!isTagFile) {
579 err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
580 "<" + eTag);
581 }
582 parseAttributeDirective(parent);
583 } else if (reader.matches("variable")) {
584 eTag = "jsp:directive.variable";
585 if (!isTagFile) {
586 err.jspError(reader.mark(), "jsp.error.directive.isnottagfile",
587 "<" + eTag);
588 }
589 parseVariableDirective(parent);
590 } else {
591 err.jspError(reader.mark(), "jsp.error.invalid.directive");
592 }
593
594 reader.skipSpaces();
595 if (reader.matches(">")) {
596 reader.skipSpaces();
597 if (!reader.matchesETag(eTag)) {
598 err.jspError(start, "jsp.error.unterminated", "<" + eTag);
599 }
600 } else if (!reader.matches("/>")) {
601 err.jspError(start, "jsp.error.unterminated", "<" + eTag);
602 }
603 }
604
605
606
607
608
609 private void parseTagDirective(Node parent) throws JasperException {
610 Attributes attrs = parseAttributes();
611 Node.TagDirective n = new Node.TagDirective(attrs, start, parent);
612
613
614
615
616
617
618 for (int i = 0; i < attrs.getLength(); i++) {
619 if ("import".equals(attrs.getQName(i))) {
620 n.addImport(attrs.getValue(i));
621 }
622 }
623 }
624
625
626
627
628
629 private void parseAttributeDirective(Node parent) throws JasperException {
630 Attributes attrs = parseAttributes();
631 Node.AttributeDirective n =
632 new Node.AttributeDirective(attrs, start, parent);
633 }
634
635
636
637
638
639 private void parseVariableDirective(Node parent) throws JasperException {
640 Attributes attrs = parseAttributes();
641 Node.VariableDirective n =
642 new Node.VariableDirective(attrs, start, parent);
643 }
644
645
646
647
648 private void parseComment(Node parent) throws JasperException {
649 start = reader.mark();
650 Mark stop = reader.skipUntil("--%>");
651 if (stop == null) {
652 err.jspError(start, "jsp.error.unterminated", "<%--");
653 }
654
655 new Node.Comment(reader.getText(start, stop), start, parent);
656 }
657
658
659
660
661 private void parseDeclaration(Node parent) throws JasperException {
662 start = reader.mark();
663 Mark stop = reader.skipUntil("%>");
664 if (stop == null) {
665 err.jspError(start, "jsp.error.unterminated", "<%!");
666 }
667
668 new Node.Declaration(parseScriptText(reader.getText(start, stop)),
669 start, parent);
670 }
671
672
673
674
675
676
677
678
679
680
681 private void parseXMLDeclaration(Node parent) throws JasperException {
682 reader.skipSpaces();
683 if (!reader.matches("/>")) {
684 if (!reader.matches(">")) {
685 err.jspError(start, "jsp.error.unterminated",
686 "<jsp:declaration>");
687 }
688 Mark stop;
689 String text;
690 while (true) {
691 start = reader.mark();
692 stop = reader.skipUntil("<");
693 if (stop == null) {
694 err.jspError(start, "jsp.error.unterminated",
695 "<jsp:declaration>");
696 }
697 text = parseScriptText(reader.getText(start, stop));
698 new Node.Declaration(text, start, parent);
699 if (reader.matches("![CDATA[")) {
700 start = reader.mark();
701 stop = reader.skipUntil("]]>");
702 if (stop == null) {
703 err.jspError(start, "jsp.error.unterminated", "CDATA");
704 }
705 text = parseScriptText(reader.getText(start, stop));
706 new Node.Declaration(text, start, parent);
707 } else {
708 break;
709 }
710 }
711
712 if (!reader.matchesETagWithoutLessThan("jsp:declaration")) {
713 err.jspError(start, "jsp.error.unterminated",
714 "<jsp:declaration>");
715 }
716 }
717 }
718
719
720
721
722 private void parseExpression(Node parent) throws JasperException {
723 start = reader.mark();
724 Mark stop = reader.skipUntil("%>");
725 if (stop == null) {
726 err.jspError(start, "jsp.error.unterminated", "<%=");
727 }
728
729 new Node.Expression(parseScriptText(reader.getText(start, stop)),
730 start, parent);
731 }
732
733
734
735
736
737
738 private void parseXMLExpression(Node parent) throws JasperException {
739 reader.skipSpaces();
740 if (!reader.matches("/>")) {
741 if (!reader.matches(">")) {
742 err.jspError(start, "jsp.error.unterminated",
743 "<jsp:expression>");
744 }
745 Mark stop;
746 String text;
747 while (true) {
748 start = reader.mark();
749 stop = reader.skipUntil("<");
750 if (stop == null) {
751 err.jspError(start, "jsp.error.unterminated",
752 "<jsp:expression>");
753 }
754 text = parseScriptText(reader.getText(start, stop));
755 new Node.Expression(text, start, parent);
756 if (reader.matches("![CDATA[")) {
757 start = reader.mark();
758 stop = reader.skipUntil("]]>");
759 if (stop == null) {
760 err.jspError(start, "jsp.error.unterminated", "CDATA");
761 }
762 text = parseScriptText(reader.getText(start, stop));
763 new Node.Expression(text, start, parent);
764 } else {
765 break;
766 }
767 }
768 if (!reader.matchesETagWithoutLessThan("jsp:expression")) {
769 err.jspError(start, "jsp.error.unterminated",
770 "<jsp:expression>");
771 }
772 }
773 }
774
775
776
777
778
779
780
781 private void parseELExpression(Node parent) throws JasperException {
782 start = reader.mark();
783 Mark last = null;
784 boolean singleQuoted = false, doubleQuoted = false;
785 int currentChar;
786 do {
787
788 last = reader.mark();
789 currentChar = reader.nextChar();
790 if (currentChar == '//' && (singleQuoted || doubleQuoted)) {
791
792 reader.nextChar();
793 currentChar = reader.nextChar();
794 }
795 if (currentChar == -1)
796 err.jspError(start, "jsp.error.unterminated", "${");
797 if (currentChar == '"')
798 doubleQuoted = !doubleQuoted;
799 if (currentChar == '\'')
800 singleQuoted = !singleQuoted;
801 } while (currentChar != '}' || (singleQuoted || doubleQuoted));
802
803 new Node.ELExpression(reader.getText(start, last), start, parent);
804 }
805
806
807
808
809 private void parseScriptlet(Node parent) throws JasperException {
810 start = reader.mark();
811 Mark stop = reader.skipUntil("%>");
812 if (stop == null) {
813 err.jspError(start, "jsp.error.unterminated", "<%");
814 }
815
816 new Node.Scriptlet(parseScriptText(reader.getText(start, stop)),
817 start, parent);
818 }
819
820
821
822
823
824
825 private void parseXMLScriptlet(Node parent) throws JasperException {
826 reader.skipSpaces();
827 if (!reader.matches("/>")) {
828 if (!reader.matches(">")) {
829 err.jspError(start, "jsp.error.unterminated",
830 "<jsp:scriptlet>");
831 }
832 Mark stop;
833 String text;
834 while (true) {
835 start = reader.mark();
836 stop = reader.skipUntil("<");
837 if (stop == null) {
838 err.jspError(start, "jsp.error.unterminated",
839 "<jsp:scriptlet>");
840 }
841 text = parseScriptText(reader.getText(start, stop));
842 new Node.Scriptlet(text, start, parent);
843 if (reader.matches("![CDATA[")) {
844 start = reader.mark();
845 stop = reader.skipUntil("]]>");
846 if (stop == null) {
847 err.jspError(start, "jsp.error.unterminated", "CDATA");
848 }
849 text = parseScriptText(reader.getText(start, stop));
850 new Node.Scriptlet(text, start, parent);
851 } else {
852 break;
853 }
854 }
855
856 if (!reader.matchesETagWithoutLessThan("jsp:scriptlet")) {
857 err.jspError(start, "jsp.error.unterminated",
858 "<jsp:scriptlet>");
859 }
860 }
861 }
862
863 /***
864 * Param ::= '<jsp:param' S Attributes S? EmptyBody S?
865 */
866 private void parseParam(Node parent) throws JasperException {
867 if (!reader.matches("<jsp:param")) {
868 err.jspError(reader.mark(), "jsp.error.paramexpected");
869 }
870 Attributes attrs = parseAttributes();
871 reader.skipSpaces();
872
873 Node paramActionNode = new Node.ParamAction(attrs, start, parent);
874
875 parseEmptyBody(paramActionNode, "jsp:param");
876
877 reader.skipSpaces();
878 }
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898 private void parseInclude(Node parent) throws JasperException {
899 Attributes attrs = parseAttributes();
900 reader.skipSpaces();
901
902 Node includeNode = new Node.IncludeAction(attrs, start, parent);
903
904 parseOptionalBody(includeNode, "jsp:include",
905 JAVAX_BODY_CONTENT_PARAM);
906 }
907
908
909
910
911
912 private void parseForward(Node parent) throws JasperException {
913 Attributes attrs = parseAttributes();
914 reader.skipSpaces();
915
916 Node forwardNode = new Node.ForwardAction(attrs, start, parent);
917
918 parseOptionalBody(forwardNode, "jsp:forward",
919 JAVAX_BODY_CONTENT_PARAM);
920 }
921
922 private void parseInvoke(Node parent) throws JasperException {
923 Attributes attrs = parseAttributes();
924 reader.skipSpaces();
925
926 Node invokeNode = new Node.InvokeAction(attrs, start, parent);
927
928 parseEmptyBody(invokeNode, "jsp:invoke");
929 }
930
931 private void parseDoBody(Node parent) throws JasperException {
932 Attributes attrs = parseAttributes();
933 reader.skipSpaces();
934
935 Node doBodyNode = new Node.DoBodyAction(attrs, start, parent);
936
937 parseEmptyBody(doBodyNode, "jsp:doBody");
938 }
939
940 private void parseElement(Node parent) throws JasperException {
941 Attributes attrs = parseAttributes();
942 reader.skipSpaces();
943
944 Node elementNode = new Node.JspElement(attrs, start, parent);
945
946 parseOptionalBody(elementNode, "jsp:element",
947 TagInfo.BODY_CONTENT_JSP);
948 }
949
950
951
952
953
954 private void parseGetProperty(Node parent) throws JasperException {
955 Attributes attrs = parseAttributes();
956 reader.skipSpaces();
957
958 Node getPropertyNode = new Node.GetProperty(attrs, start, parent);
959
960 parseOptionalBody(getPropertyNode, "jsp:getProperty",
961 TagInfo.BODY_CONTENT_EMPTY);
962 }
963
964
965
966
967
968 private void parseSetProperty(Node parent) throws JasperException {
969 Attributes attrs = parseAttributes();
970 reader.skipSpaces();
971
972 Node setPropertyNode = new Node.SetProperty(attrs, start, parent);
973
974 parseOptionalBody(setPropertyNode, "jsp:setProperty",
975 TagInfo.BODY_CONTENT_EMPTY);
976 }
977
978
979
980
981
982
983 private void parseEmptyBody(Node parent, String tag)
984 throws JasperException {
985 if (reader.matches("/>")) {
986
987 } else if (reader.matches(">")) {
988 if (reader.matchesETag(tag)) {
989
990 } else if (reader.matchesOptionalSpacesFollowedBy(
991 "<jsp:attribute")) {
992
993 parseNamedAttributes(parent);
994 if (!reader.matchesETag(tag)) {
995
996 err.jspError(reader.mark(),
997 "jsp.error.jspbody.emptybody.only",
998 "<" + tag);
999 }
1000 } else {
1001 err.jspError(reader.mark(), "jsp.error.jspbody.emptybody.only",
1002 "<" + tag);
1003 }
1004 } else {
1005 err.jspError(reader.mark(), "jsp.error.unterminated",
1006 "<" + tag);
1007 }
1008 }
1009
1010
1011
1012
1013
1014 private void parseUseBean(Node parent) throws JasperException {
1015 Attributes attrs = parseAttributes();
1016 reader.skipSpaces();
1017
1018 Node useBeanNode = new Node.UseBean(attrs, start, parent);
1019
1020 parseOptionalBody(useBeanNode, "jsp:useBean",
1021 TagInfo.BODY_CONTENT_JSP);
1022 }
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050 private void parseOptionalBody(Node parent, String tag, String bodyType)
1051 throws JasperException {
1052 if (reader.matches("/>")) {
1053
1054 return;
1055 }
1056
1057 if (!reader.matches(">")) {
1058 err.jspError(reader.mark(), "jsp.error.unterminated",
1059 "<" + tag);
1060 }
1061
1062 if (reader.matchesETag(tag)) {
1063
1064 return;
1065 }
1066
1067 if (!parseJspAttributeAndBody(parent, tag, bodyType)) {
1068
1069 parseBody(parent, tag, bodyType);
1070 }
1071 }
1072
1073 /***
1074 * Attempts to parse 'JspAttributeAndBody' production. Returns true if
1075 * it matched, or false if not. Assumes EmptyBody is okay as well.
1076 * <p/>
1077 * JspAttributeAndBody ::=
1078 * ( '>' # S? ( '<jsp:attribute' NamedAttributes )?
1079 * '<jsp:body'
1080 * ( JspBodyBody | <TRANSLATION_ERROR> )
1081 * S? ETag
1082 * )
1083 */
1084 private boolean parseJspAttributeAndBody(Node parent, String tag,
1085 String bodyType)
1086 throws JasperException {
1087 boolean result = false;
1088
1089 if (reader.matchesOptionalSpacesFollowedBy("<jsp:attribute")) {
1090
1091
1092
1093
1094 parseNamedAttributes(parent);
1095
1096 result = true;
1097 }
1098
1099 if (reader.matchesOptionalSpacesFollowedBy("<jsp:body")) {
1100
1101 parseJspBody(parent, bodyType);
1102 reader.skipSpaces();
1103 if (!reader.matchesETag(tag)) {
1104 err.jspError(reader.mark(), "jsp.error.unterminated",
1105 "<" + tag);
1106 }
1107
1108 result = true;
1109 } else if (result && !reader.matchesETag(tag)) {
1110
1111
1112 err.jspError(reader.mark(), "jsp.error.jspbody.required",
1113 "<" + tag);
1114 }
1115
1116 return result;
1117 }
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130 private void parseJspParams(Node parent) throws JasperException {
1131 Node jspParamsNode = new Node.ParamsAction(start, parent);
1132 parseOptionalBody(jspParamsNode, "jsp:params",
1133 JAVAX_BODY_CONTENT_PARAM);
1134 }
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152 private void parseFallBack(Node parent) throws JasperException {
1153 Node fallBackNode = new Node.FallBackAction(start, parent);
1154 parseOptionalBody(fallBackNode, "jsp:fallback",
1155 JAVAX_BODY_CONTENT_TEMPLATE_TEXT);
1156 }
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175 private void parsePlugin(Node parent) throws JasperException {
1176 Attributes attrs = parseAttributes();
1177 reader.skipSpaces();
1178
1179 Node pluginNode = new Node.PlugIn(attrs, start, parent);
1180
1181 parseOptionalBody(pluginNode, "jsp:plugin",
1182 JAVAX_BODY_CONTENT_PLUGIN);
1183 }
1184
1185
1186
1187
1188
1189 private void parsePluginTags(Node parent) throws JasperException {
1190 reader.skipSpaces();
1191
1192 if (reader.matches("<jsp:params")) {
1193 parseJspParams(parent);
1194 reader.skipSpaces();
1195 }
1196
1197 if (reader.matches("<jsp:fallback")) {
1198 parseFallBack(parent);
1199 reader.skipSpaces();
1200 }
1201 }
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214 private void parseStandardAction(Node parent) throws JasperException {
1215 Mark start = reader.mark();
1216
1217 if (reader.matches(INCLUDE_ACTION)) {
1218 parseInclude(parent);
1219 } else if (reader.matches(FORWARD_ACTION)) {
1220 parseForward(parent);
1221 } else if (reader.matches(INVOKE_ACTION)) {
1222 if (!isTagFile) {
1223 err.jspError(reader.mark(), "jsp.error.action.isnottagfile",
1224 "<jsp:invoke");
1225 }
1226 parseInvoke(parent);
1227 } else if (reader.matches(DOBODY_ACTION)) {
1228 if (!isTagFile) {
1229 err.jspError(reader.mark(), "jsp.error.action.isnottagfile",
1230 "<jsp:doBody");
1231 }
1232 parseDoBody(parent);
1233 } else if (reader.matches(GET_PROPERTY_ACTION)) {
1234 parseGetProperty(parent);
1235 } else if (reader.matches(SET_PROPERTY_ACTION)) {
1236 parseSetProperty(parent);
1237 } else if (reader.matches(USE_BEAN_ACTION)) {
1238 parseUseBean(parent);
1239 } else if (reader.matches(PLUGIN_ACTION)) {
1240 parsePlugin(parent);
1241 } else if (reader.matches(ELEMENT_ACTION)) {
1242 parseElement(parent);
1243 } else if (reader.matches(ATTRIBUTE_ACTION)) {
1244 err.jspError(start, "jsp.error.namedAttribute.invalidUse");
1245 } else if (reader.matches(BODY_ACTION)) {
1246 err.jspError(start, "jsp.error.jspbody.invalidUse");
1247 } else if (reader.matches(FALLBACK_ACTION)) {
1248 err.jspError(start, "jsp.error.fallback.invalidUse");
1249 } else if (reader.matches(PARAMS_ACTION)) {
1250 err.jspError(start, "jsp.error.params.invalidUse");
1251 } else if (reader.matches(PARAM_ACTION)) {
1252 err.jspError(start, "jsp.error.param.invalidUse");
1253 } else if (reader.matches(OUTPUT_ACTION)) {
1254 err.jspError(start, "jsp.error.jspoutput.invalidUse");
1255 } else {
1256 err.jspError(start, "jsp.error.badStandardAction");
1257 }
1258 }
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284 private boolean parseCustomTag(Node parent) throws JasperException {
1285
1286 if (reader.peekChar() != '<') {
1287 return false;
1288 }
1289
1290
1291 reader.nextChar();
1292 String tagName = reader.parseToken(false);
1293 int i = tagName.indexOf(':');
1294 if (i == -1) {
1295 reader.reset(start);
1296 return false;
1297 }
1298
1299 String prefix = tagName.substring(0, i);
1300 String shortTagName = tagName.substring(i + 1);
1301
1302
1303 String uri = pageInfo.getURI(prefix);
1304 if (uri == null) {
1305 reader.reset(start);
1306
1307 pageInfo.putNonCustomTagPrefix(prefix, reader.mark());
1308 return false;
1309 }
1310
1311 TagLibraryInfo tagLibInfo = pageInfo.getTaglib(uri);
1312 TagInfo tagInfo = tagLibInfo.getTag(shortTagName);
1313 TagFileInfo tagFileInfo = tagLibInfo.getTagFile(shortTagName);
1314 if (tagInfo == null && tagFileInfo == null) {
1315 err.jspError(start, "jsp.error.bad_tag", shortTagName, prefix);
1316 }
1317 Class tagHandlerClass = null;
1318 if (tagInfo != null) {
1319
1320
1321 String handlerClassName = tagInfo.getTagClassName();
1322 try {
1323 tagHandlerClass =
1324 ctxt.getClassLoader().loadClass(handlerClassName);
1325 } catch (Exception e) {
1326 err.jspError(start, "jsp.error.loadclass.taghandler",
1327 handlerClassName, tagName);
1328 }
1329 }
1330
1331
1332
1333
1334
1335
1336 Attributes attrs = parseAttributes();
1337 reader.skipSpaces();
1338
1339
1340 if (reader.matches("/>")) {
1341 if (tagInfo != null) {
1342 new Node.CustomTag(tagName, prefix, shortTagName, uri, attrs,
1343 start, parent, tagInfo, tagHandlerClass);
1344 } else {
1345 new Node.CustomTag(tagName, prefix, shortTagName, uri, attrs,
1346 start, parent, tagFileInfo);
1347 }
1348 return true;
1349 }
1350
1351
1352
1353
1354
1355
1356
1357
1358 String bc;
1359 if (tagInfo != null) {
1360 bc = tagInfo.getBodyContent();
1361 } else {
1362 bc = tagFileInfo.getTagInfo().getBodyContent();
1363 }
1364
1365 Node tagNode = null;
1366 if (tagInfo != null) {
1367 tagNode = new Node.CustomTag(tagName, prefix, shortTagName, uri,
1368 attrs, start, parent, tagInfo,
1369 tagHandlerClass);
1370 } else {
1371 tagNode = new Node.CustomTag(tagName, prefix, shortTagName, uri,
1372 attrs, start, parent, tagFileInfo);
1373 }
1374
1375 parseOptionalBody(tagNode, tagName, bc);
1376
1377 return true;
1378 }
1379
1380
1381
1382
1383
1384 private void parseTemplateText(Node parent) throws JasperException {
1385
1386 if (!reader.hasMoreInput())
1387 return;
1388
1389 CharArrayWriter ttext = new CharArrayWriter();
1390
1391 int ch = reader.nextChar();
1392 if (ch == '//') {
1393 reader.pushChar();
1394 } else {
1395 ttext.write(ch);
1396 }
1397
1398 while (reader.hasMoreInput()) {
1399 ch = reader.nextChar();
1400 if (ch == '<') {
1401 reader.pushChar();
1402 break;
1403 } else if (ch == '$') {
1404 if (!reader.hasMoreInput()) {
1405 ttext.write('$');
1406 break;
1407 }
1408 ch = reader.nextChar();
1409 if (ch == '{') {
1410 reader.pushChar();
1411 reader.pushChar();
1412 break;
1413 }
1414 ttext.write('$');
1415 reader.pushChar();
1416 continue;
1417 } else if (ch == '//') {
1418 if (!reader.hasMoreInput()) {
1419 ttext.write('//');
1420 break;
1421 }
1422
1423
1424
1425 char next = (char) reader.peekChar();
1426 if (next == '%') {
1427 ch = reader.nextChar();
1428 } else if (next == '$') {
1429
1430 reader.nextChar();
1431 ch = Constants.HACK_CHAR;
1432 }
1433 }
1434 ttext.write(ch);
1435 }
1436 new Node.TemplateText(ttext.toString(), start, parent);
1437 }
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449 private void parseXMLTemplateText(Node parent) throws JasperException {
1450 reader.skipSpaces();
1451 if (!reader.matches("/>")) {
1452 if (!reader.matches(">")) {
1453 err.jspError(start, "jsp.error.unterminated",
1454 "<jsp:text>");
1455 }
1456 CharArrayWriter ttext = new CharArrayWriter();
1457 while (reader.hasMoreInput()) {
1458 int ch = reader.nextChar();
1459 if (ch == '<') {
1460
1461 if (!reader.matches("![CDATA[")) {
1462 break;
1463 }
1464 start = reader.mark();
1465 Mark stop = reader.skipUntil("]]>");
1466 if (stop == null) {
1467 err.jspError(start, "jsp.error.unterminated", "CDATA");
1468 }
1469 String text = reader.getText(start, stop);
1470 ttext.write(text, 0, text.length());
1471 } else if (ch == '//') {
1472 if (!reader.hasMoreInput()) {
1473 ttext.write('//');
1474 break;
1475 }
1476 ch = reader.nextChar();
1477 if (ch != '$') {
1478 ttext.write('//');
1479 }
1480 ttext.write(ch);
1481 } else if (ch == '$') {
1482 if (!reader.hasMoreInput()) {
1483 ttext.write('$');
1484 break;
1485 }
1486 ch = reader.nextChar();
1487 if (ch != '{') {
1488 ttext.write('$');
1489 reader.pushChar();
1490 continue;
1491 }
1492
1493 new Node.TemplateText(ttext.toString(), start, parent);
1494
1495
1496 start = reader.mark();
1497 parseELExpression(parent);
1498
1499 start = reader.mark();
1500 ttext = new CharArrayWriter();
1501 } else {
1502 ttext.write(ch);
1503 }
1504 }
1505
1506 new Node.TemplateText(ttext.toString(), start, parent);
1507
1508 if (!reader.hasMoreInput()) {
1509 err.jspError(start, "jsp.error.unterminated",
1510 "<jsp:text>");
1511 } else if (!reader.matchesETagWithoutLessThan("jsp:text")) {
1512 err.jspError(start, "jsp.error.jsptext.badcontent");
1513 }
1514 }
1515 }
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534 private void parseElements(Node parent)
1535 throws JasperException {
1536 if (scriptlessCount > 0) {
1537
1538
1539
1540 parseElementsScriptless(parent);
1541 return;
1542 }
1543
1544 start = reader.mark();
1545 if (reader.matches("<%--")) {
1546 parseComment(parent);
1547 } else if (reader.matches("<%@")) {
1548 parseDirective(parent);
1549 } else if (reader.matches("<jsp:directive.")) {
1550 parseXMLDirective(parent);
1551 } else if (reader.matches("<%!")) {
1552 parseDeclaration(parent);
1553 } else if (reader.matches("<jsp:declaration")) {
1554 parseXMLDeclaration(parent);
1555 } else if (reader.matches("<%=")) {
1556 parseExpression(parent);
1557 } else if (reader.matches("<jsp:expression")) {
1558 parseXMLExpression(parent);
1559 } else if (reader.matches("<%")) {
1560 parseScriptlet(parent);
1561 } else if (reader.matches("<jsp:scriptlet")) {
1562 parseXMLScriptlet(parent);
1563 } else if (reader.matches("<jsp:text")) {
1564 parseXMLTemplateText(parent);
1565 } else if (reader.matches("${")) {
1566 parseELExpression(parent);
1567 } else if (reader.matches("<jsp:")) {
1568 parseStandardAction(parent);
1569 } else if (!parseCustomTag(parent)) {
1570 checkUnbalancedEndTag();
1571 parseTemplateText(parent);
1572 }
1573 }
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592 private void parseElementsScriptless(Node parent)
1593 throws JasperException {
1594
1595
1596 scriptlessCount++;
1597
1598 start = reader.mark();
1599 if (reader.matches("<%--")) {
1600 parseComment(parent);
1601 } else if (reader.matches("<%@")) {
1602 parseDirective(parent);
1603 } else if (reader.matches("<jsp:directive.")) {
1604 parseXMLDirective(parent);
1605 } else if (reader.matches("<%!")) {
1606 err.jspError(reader.mark(), "jsp.error.no.scriptlets");
1607 } else if (reader.matches("<jsp:declaration")) {
1608 err.jspError(reader.mark(), "jsp.error.no.scriptlets");
1609 } else if (reader.matches("<%=")) {
1610 err.jspError(reader.mark(), "jsp.error.no.scriptlets");
1611 } else if (reader.matches("<jsp:expression")) {
1612 err.jspError(reader.mark(), "jsp.error.no.scriptlets");
1613 } else if (reader.matches("<%")) {
1614 err.jspError(reader.mark(), "jsp.error.no.scriptlets");
1615 } else if (reader.matches("<jsp:scriptlet")) {
1616 err.jspError(reader.mark(), "jsp.error.no.scriptlets");
1617 } else if (reader.matches("<jsp:text")) {
1618 parseXMLTemplateText(parent);
1619 } else if (reader.matches("${")) {
1620 parseELExpression(parent);
1621 } else if (reader.matches("<jsp:")) {
1622 parseStandardAction(parent);
1623 } else if (!parseCustomTag(parent)) {
1624 checkUnbalancedEndTag();
1625 parseTemplateText(parent);
1626 }
1627
1628 scriptlessCount--;
1629 }
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646 private void parseElementsTemplateText(Node parent)
1647 throws JasperException {
1648 start = reader.mark();
1649 if (reader.matches("<%--")) {
1650 parseComment(parent);
1651 } else if (reader.matches("<%@")) {
1652 parseDirective(parent);
1653 } else if (reader.matches("<jsp:directive.")) {
1654 parseXMLDirective(parent);
1655 } else if (reader.matches("<%!")) {
1656 err.jspError(reader.mark(), "jsp.error.not.in.template",
1657 "Declarations");
1658 } else if (reader.matches("<jsp:declaration")) {
1659 err.jspError(reader.mark(), "jsp.error.not.in.template",
1660 "Declarations");
1661 } else if (reader.matches("<%=")) {
1662 err.jspError(reader.mark(), "jsp.error.not.in.template",
1663 "Expressions");
1664 } else if (reader.matches("<jsp:expression")) {
1665 err.jspError(reader.mark(), "jsp.error.not.in.template",
1666 "Expressions");
1667 } else if (reader.matches("<%")) {
1668 err.jspError(reader.mark(), "jsp.error.not.in.template",
1669 "Scriptlets");
1670 } else if (reader.matches("<jsp:scriptlet")) {
1671 err.jspError(reader.mark(), "jsp.error.not.in.template",
1672 "Scriptlets");
1673 } else if (reader.matches("<jsp:text")) {
1674 err.jspError(reader.mark(), "jsp.error.not.in.template",
1675 "<jsp:text");
1676 } else if (reader.matches("${")) {
1677 err.jspError(reader.mark(), "jsp.error.not.in.template",
1678 "Expression language");
1679 } else if (reader.matches("<jsp:")) {
1680 err.jspError(reader.mark(), "jsp.error.not.in.template",
1681 "Standard actions");
1682 } else if (parseCustomTag(parent)) {
1683 err.jspError(reader.mark(), "jsp.error.not.in.template",
1684 "Custom actions");
1685 } else {
1686 checkUnbalancedEndTag();
1687 parseTemplateText(parent);
1688 }
1689 }
1690
1691
1692
1693
1694 private void checkUnbalancedEndTag() throws JasperException {
1695
1696 if (!reader.matches("</")) {
1697 return;
1698 }
1699
1700
1701 if (reader.matches("jsp:")) {
1702 err.jspError(start, "jsp.error.unbalanced.endtag", "jsp:");
1703 }
1704
1705
1706 String tagName = reader.parseToken(false);
1707 int i = tagName.indexOf(':');
1708 if (i == -1 || pageInfo.getURI(tagName.substring(0, i)) == null) {
1709 reader.reset(start);
1710 return;
1711 }
1712
1713 err.jspError(start, "jsp.error.unbalanced.endtag", tagName);
1714 }
1715
1716 /***
1717 * TagDependentBody :=
1718 */
1719 private void parseTagDependentBody(Node parent, String tag)
1720 throws JasperException {
1721 Mark bodyStart = reader.mark();
1722 Mark bodyEnd = reader.skipUntilETag(tag);
1723 if (bodyEnd == null) {
1724 err.jspError(start, "jsp.error.unterminated", "<" + tag);
1725 }
1726 new Node.TemplateText(reader.getText(bodyStart, bodyEnd), bodyStart,
1727 parent);
1728 }
1729
1730
1731
1732
1733 private void parseJspBody(Node parent, String bodyType)
1734 throws JasperException {
1735 Mark start = reader.mark();
1736 Node bodyNode = new Node.JspBody(start, parent);
1737
1738 reader.skipSpaces();
1739 if (!reader.matches("/>")) {
1740 if (!reader.matches(">")) {
1741 err.jspError(start, "jsp.error.unterminated",
1742 "<jsp:body");
1743 }
1744 parseBody(bodyNode, "jsp:body", bodyType);
1745 }
1746 }
1747
1748
1749
1750
1751
1752
1753 private void parseBody(Node parent, String tag, String bodyType)
1754 throws JasperException {
1755 if (bodyType.equalsIgnoreCase(TagInfo.BODY_CONTENT_TAG_DEPENDENT)) {
1756 parseTagDependentBody(parent, tag);
1757 } else if (bodyType.equalsIgnoreCase(TagInfo.BODY_CONTENT_EMPTY)) {
1758 if (!reader.matchesETag(tag)) {
1759 err.jspError(start, "jasper.error.emptybodycontent.nonempty",
1760 tag);
1761 }
1762 } else if (bodyType == JAVAX_BODY_CONTENT_PLUGIN) {
1763
1764
1765 parsePluginTags(parent);
1766 if (!reader.matchesETag(tag)) {
1767 err.jspError(reader.mark(), "jsp.error.unterminated",
1768 "<" + tag);
1769 }
1770 } else if (bodyType.equalsIgnoreCase(TagInfo.BODY_CONTENT_JSP) ||
1771 bodyType.equalsIgnoreCase(TagInfo.BODY_CONTENT_SCRIPTLESS) ||
1772 (bodyType == JAVAX_BODY_CONTENT_PARAM) ||
1773 (bodyType == JAVAX_BODY_CONTENT_TEMPLATE_TEXT)) {
1774 while (reader.hasMoreInput()) {
1775 if (reader.matchesETag(tag)) {
1776 return;
1777 }
1778
1779
1780 if (tag.equals("jsp:body") || tag.equals("jsp:attribute")) {
1781 if (reader.matches("<jsp:attribute")) {
1782 err.jspError(reader.mark(), "jsp.error.nested.jspattribute");
1783 } else if (reader.matches("<jsp:body")) {
1784 err.jspError(reader.mark(), "jsp.error.nested.jspbody");
1785 }
1786 }
1787
1788 if (bodyType.equalsIgnoreCase(TagInfo.BODY_CONTENT_JSP)) {
1789 parseElements(parent);
1790 } else if (bodyType.equalsIgnoreCase(
1791 TagInfo.BODY_CONTENT_SCRIPTLESS)) {
1792 parseElementsScriptless(parent);
1793 } else if (bodyType == JAVAX_BODY_CONTENT_PARAM) {
1794
1795
1796 reader.skipSpaces();
1797 parseParam(parent);
1798 } else if (bodyType == JAVAX_BODY_CONTENT_TEMPLATE_TEXT) {
1799 parseElementsTemplateText(parent);
1800 }
1801 }
1802 err.jspError(start, "jsp.error.unterminated", "<" + tag);
1803 } else {
1804 err.jspError(start, "jasper.error.bad.bodycontent.type");
1805 }
1806 }
1807
1808
1809
1810
1811 private void parseNamedAttributes(Node parent) throws JasperException {
1812 do {
1813 Mark start = reader.mark();
1814 Attributes attrs = parseAttributes();
1815 Node.NamedAttribute namedAttributeNode =
1816 new Node.NamedAttribute(attrs, start, parent);
1817
1818 reader.skipSpaces();
1819 if (!reader.matches("/>")) {
1820 if (!reader.matches(">")) {
1821 err.jspError(start, "jsp.error.unterminated",
1822 "<jsp:attribute");
1823 }
1824 if (namedAttributeNode.isTrim()) {
1825 reader.skipSpaces();
1826 }
1827 parseBody(namedAttributeNode, "jsp:attribute",
1828 getAttributeBodyType(parent,
1829 attrs.getValue("name")));
1830 if (namedAttributeNode.isTrim()) {
1831 Node.Nodes subElems = namedAttributeNode.getBody();
1832 if (subElems != null) {
1833 Node lastNode = subElems.getNode(subElems.size() - 1);
1834 if (lastNode instanceof Node.TemplateText) {
1835 ((Node.TemplateText) lastNode).rtrim();
1836 }
1837 }
1838 }
1839 }
1840 reader.skipSpaces();
1841 } while (reader.matches("<jsp:attribute"));
1842 }
1843
1844 /***
1845 * Determine the body type of <jsp:attribute> from the enclosing node
1846 */
1847 private String getAttributeBodyType(Node n, String name) {
1848
1849 if (n instanceof Node.CustomTag) {
1850 TagInfo tagInfo = ((Node.CustomTag) n).getTagInfo();
1851 TagAttributeInfo[] tldAttrs = tagInfo.getAttributes();
1852 for (int i = 0; i < tldAttrs.length; i++) {
1853 if (name.equals(tldAttrs[i].getName())) {
1854 if (tldAttrs[i].isFragment()) {
1855 return TagInfo.BODY_CONTENT_SCRIPTLESS;
1856 }
1857 if (tldAttrs[i].canBeRequestTime()) {
1858 return TagInfo.BODY_CONTENT_JSP;
1859 }
1860 }
1861 }
1862 if (tagInfo.hasDynamicAttributes()) {
1863 return TagInfo.BODY_CONTENT_JSP;
1864 }
1865 } else if (n instanceof Node.IncludeAction) {
1866 if ("page".equals(name)) {
1867 return TagInfo.BODY_CONTENT_JSP;
1868 }
1869 } else if (n instanceof Node.ForwardAction) {
1870 if ("page".equals(name)) {
1871 return TagInfo.BODY_CONTENT_JSP;
1872 }
1873 } else if (n instanceof Node.SetProperty) {
1874 if ("value".equals(name)) {
1875 return TagInfo.BODY_CONTENT_JSP;
1876 }
1877 } else if (n instanceof Node.UseBean) {
1878 if ("beanName".equals(name)) {
1879 return TagInfo.BODY_CONTENT_JSP;
1880 }
1881 } else if (n instanceof Node.PlugIn) {
1882 if ("width".equals(name) || "height".equals(name)) {
1883 return TagInfo.BODY_CONTENT_JSP;
1884 }
1885 } else if (n instanceof Node.ParamAction) {
1886 if ("value".equals(name)) {
1887 return TagInfo.BODY_CONTENT_JSP;
1888 }
1889 } else if (n instanceof Node.JspElement) {
1890 return TagInfo.BODY_CONTENT_JSP;
1891 }
1892
1893 return JAVAX_BODY_CONTENT_TEMPLATE_TEXT;
1894 }
1895
1896 private void parseTagFileDirectives(Node parent)
1897 throws JasperException {
1898 reader.setSingleFile(true);
1899 reader.skipUntil("<");
1900 while (reader.hasMoreInput()) {
1901 start = reader.mark();
1902 if (reader.matches("%--")) {
1903 parseComment(parent);
1904 } else if (reader.matches("%@")) {
1905 parseDirective(parent);
1906 } else if (reader.matches("jsp:directive.")) {
1907 parseXMLDirective(parent);
1908 }
1909 reader.skipUntil("<");
1910 }
1911 }
1912 }
1913