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.JasperException;
20 import org.apache.struts2.jasper.JspCompilationContext;
21 import org.xml.sax.*;
22 import org.xml.sax.ext.LexicalHandler;
23 import org.xml.sax.helpers.AttributesImpl;
24 import org.xml.sax.helpers.DefaultHandler;
25
26 import javax.servlet.jsp.tagext.TagFileInfo;
27 import javax.servlet.jsp.tagext.TagInfo;
28 import javax.servlet.jsp.tagext.TagLibraryInfo;
29 import javax.xml.parsers.SAXParser;
30 import javax.xml.parsers.SAXParserFactory;
31 import java.io.CharArrayWriter;
32 import java.io.FileNotFoundException;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.jar.JarFile;
38
39 /***
40 * Class implementing a parser for a JSP document, that is, a JSP page in XML
41 * syntax.
42 *
43 * @author Jan Luehe
44 * @author Kin-man Chung
45 */
46
47 class JspDocumentParser
48 extends DefaultHandler
49 implements LexicalHandler, TagConstants {
50
51 private static final String JSP_VERSION = "version";
52 private static final String LEXICAL_HANDLER_PROPERTY =
53 "http://xml.org/sax/properties/lexical-handler";
54 private static final String JSP_URI = "http://java.sun.com/JSP/Page";
55
56 private static final EnableDTDValidationException ENABLE_DTD_VALIDATION_EXCEPTION =
57 new EnableDTDValidationException(
58 "jsp.error.enable_dtd_validation",
59 null);
60
61 private ParserController parserController;
62 private JspCompilationContext ctxt;
63 private PageInfo pageInfo;
64 private String path;
65 private StringBuffer charBuffer;
66
67
68 private Node current;
69
70
71
72
73
74
75 private Node scriptlessBodyNode;
76
77 private Locator locator;
78
79
80
81
82
83
84
85
86
87
88
89 private Mark startMark;
90
91
92 private boolean inDTD;
93
94 private boolean isValidating;
95
96 private ErrorDispatcher err;
97 private boolean isTagFile;
98 private boolean directivesOnly;
99 private boolean isTop;
100
101
102 private int tagDependentNesting = 0;
103
104
105 private boolean tagDependentPending = false;
106
107
108
109
110 public JspDocumentParser(
111 ParserController pc,
112 String path,
113 boolean isTagFile,
114 boolean directivesOnly) {
115 this.parserController = pc;
116 this.ctxt = pc.getJspCompilationContext();
117 this.pageInfo = pc.getCompiler().getPageInfo();
118 this.err = pc.getCompiler().getErrorDispatcher();
119 this.path = path;
120 this.isTagFile = isTagFile;
121 this.directivesOnly = directivesOnly;
122 this.isTop = true;
123 }
124
125
126
127
128
129
130 public static Node.Nodes parse(
131 ParserController pc,
132 String path,
133 JarFile jarFile,
134 Node parent,
135 boolean isTagFile,
136 boolean directivesOnly,
137 String pageEnc,
138 String jspConfigPageEnc,
139 boolean isEncodingSpecifiedInProlog)
140 throws JasperException {
141
142 JspDocumentParser jspDocParser =
143 new JspDocumentParser(pc, path, isTagFile, directivesOnly);
144 Node.Nodes pageNodes = null;
145
146 try {
147
148
149 Node.Root dummyRoot = new Node.Root(null, parent, true);
150 dummyRoot.setPageEncoding(pageEnc);
151 dummyRoot.setJspConfigPageEncoding(jspConfigPageEnc);
152 dummyRoot.setIsEncodingSpecifiedInProlog(
153 isEncodingSpecifiedInProlog);
154 jspDocParser.current = dummyRoot;
155 if (parent == null) {
156 jspDocParser.addInclude(
157 dummyRoot,
158 jspDocParser.pageInfo.getIncludePrelude());
159 } else {
160 jspDocParser.isTop = false;
161 }
162
163
164 SAXParser saxParser = getSAXParser(false, jspDocParser);
165 InputStream inStream = null;
166 try {
167 inStream = JspUtil.getInputStream(path, jarFile,
168 jspDocParser.ctxt,
169 jspDocParser.err);
170 saxParser.parse(new InputSource(inStream), jspDocParser);
171 } catch (EnableDTDValidationException e) {
172 saxParser = getSAXParser(true, jspDocParser);
173 jspDocParser.isValidating = true;
174 if (inStream != null) {
175 try {
176 inStream.close();
177 } catch (Exception any) {
178 }
179 }
180 inStream = JspUtil.getInputStream(path, jarFile,
181 jspDocParser.ctxt,
182 jspDocParser.err);
183 saxParser.parse(new InputSource(inStream), jspDocParser);
184 } finally {
185 if (inStream != null) {
186 try {
187 inStream.close();
188 } catch (Exception any) {
189 }
190 }
191 }
192
193 if (parent == null) {
194 jspDocParser.addInclude(
195 dummyRoot,
196 jspDocParser.pageInfo.getIncludeCoda());
197 }
198
199
200 pageNodes = new Node.Nodes(dummyRoot);
201
202 } catch (IOException ioe) {
203 jspDocParser.err.jspError("jsp.error.data.file.read", path, ioe);
204 } catch (SAXParseException e) {
205 jspDocParser.err.jspError
206 (new Mark(jspDocParser.ctxt, path, e.getLineNumber(),
207 e.getColumnNumber()),
208 e.getMessage());
209 } catch (Exception e) {
210 jspDocParser.err.jspError(e);
211 }
212
213 return pageNodes;
214 }
215
216
217
218
219
220
221
222 private void addInclude(Node parent, List files) throws SAXException {
223 if (files != null) {
224 Iterator iter = files.iterator();
225 while (iter.hasNext()) {
226 String file = (String) iter.next();
227 AttributesImpl attrs = new AttributesImpl();
228 attrs.addAttribute("", "file", "file", "CDATA", file);
229
230
231 Node includeDir =
232 new Node.IncludeDirective(attrs, null,
233 parent);
234 processIncludeDirective(file, includeDir);
235 }
236 }
237 }
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252 public void startElement(
253 String uri,
254 String localName,
255 String qName,
256 Attributes attrs)
257 throws SAXException {
258
259 AttributesImpl taglibAttrs = null;
260 AttributesImpl nonTaglibAttrs = null;
261 AttributesImpl nonTaglibXmlnsAttrs = null;
262
263 processChars();
264
265 checkPrefixes(uri, qName, attrs);
266
267 if (directivesOnly &&
268 !(JSP_URI.equals(uri) && localName.startsWith(DIRECTIVE_ACTION))) {
269 return;
270 }
271
272 String currentPrefix = getPrefix(current.getQName());
273
274
275 if (JSP_URI.equals(uri) && TEXT_ACTION.equals(current.getLocalName())
276 && "jsp".equals(currentPrefix)) {
277 throw new SAXParseException(
278 Localizer.getMessage("jsp.error.text.has_subelement"),
279 locator);
280 }
281
282 startMark = new Mark(ctxt, path, locator.getLineNumber(),
283 locator.getColumnNumber());
284
285 if (attrs != null) {
286
287
288
289
290 boolean isTaglib = false;
291 for (int i = attrs.getLength() - 1; i >= 0; i--) {
292 isTaglib = false;
293 String attrQName = attrs.getQName(i);
294 if (!attrQName.startsWith("xmlns")) {
295 if (nonTaglibAttrs == null) {
296 nonTaglibAttrs = new AttributesImpl();
297 }
298 nonTaglibAttrs.addAttribute(
299 attrs.getURI(i),
300 attrs.getLocalName(i),
301 attrs.getQName(i),
302 attrs.getType(i),
303 attrs.getValue(i));
304 } else {
305 if (attrQName.startsWith("xmlns:jsp")) {
306 isTaglib = true;
307 } else {
308 String attrUri = attrs.getValue(i);
309
310
311 isTaglib = pageInfo.hasTaglib(attrUri);
312 }
313 if (isTaglib) {
314 if (taglibAttrs == null) {
315 taglibAttrs = new AttributesImpl();
316 }
317 taglibAttrs.addAttribute(
318 attrs.getURI(i),
319 attrs.getLocalName(i),
320 attrs.getQName(i),
321 attrs.getType(i),
322 attrs.getValue(i));
323 } else {
324 if (nonTaglibXmlnsAttrs == null) {
325 nonTaglibXmlnsAttrs = new AttributesImpl();
326 }
327 nonTaglibXmlnsAttrs.addAttribute(
328 attrs.getURI(i),
329 attrs.getLocalName(i),
330 attrs.getQName(i),
331 attrs.getType(i),
332 attrs.getValue(i));
333 }
334 }
335 }
336 }
337
338 Node node = null;
339
340 if (tagDependentPending && JSP_URI.equals(uri) &&
341 localName.equals(BODY_ACTION)) {
342 tagDependentPending = false;
343 tagDependentNesting++;
344 current =
345 parseStandardAction(
346 qName,
347 localName,
348 nonTaglibAttrs,
349 nonTaglibXmlnsAttrs,
350 taglibAttrs,
351 startMark,
352 current);
353 return;
354 }
355
356 if (tagDependentPending && JSP_URI.equals(uri) &&
357 localName.equals(ATTRIBUTE_ACTION)) {
358 current =
359 parseStandardAction(
360 qName,
361 localName,
362 nonTaglibAttrs,
363 nonTaglibXmlnsAttrs,
364 taglibAttrs,
365 startMark,
366 current);
367 return;
368 }
369
370 if (tagDependentPending) {
371 tagDependentPending = false;
372 tagDependentNesting++;
373 }
374
375 if (tagDependentNesting > 0) {
376 node =
377 new Node.UninterpretedTag(
378 qName,
379 localName,
380 nonTaglibAttrs,
381 nonTaglibXmlnsAttrs,
382 taglibAttrs,
383 startMark,
384 current);
385 } else if (JSP_URI.equals(uri)) {
386 node =
387 parseStandardAction(
388 qName,
389 localName,
390 nonTaglibAttrs,
391 nonTaglibXmlnsAttrs,
392 taglibAttrs,
393 startMark,
394 current);
395 } else {
396 node =
397 parseCustomAction(
398 qName,
399 localName,
400 uri,
401 nonTaglibAttrs,
402 nonTaglibXmlnsAttrs,
403 taglibAttrs,
404 startMark,
405 current);
406 if (node == null) {
407 node =
408 new Node.UninterpretedTag(
409 qName,
410 localName,
411 nonTaglibAttrs,
412 nonTaglibXmlnsAttrs,
413 taglibAttrs,
414 startMark,
415 current);
416 } else {
417
418 String bodyType = getBodyType((Node.CustomTag) node);
419
420 if (scriptlessBodyNode == null
421 && bodyType.equalsIgnoreCase(TagInfo.BODY_CONTENT_SCRIPTLESS)) {
422 scriptlessBodyNode = node;
423 } else if (TagInfo.BODY_CONTENT_TAG_DEPENDENT.equalsIgnoreCase(bodyType)) {
424 tagDependentPending = true;
425 }
426 }
427 }
428
429 current = node;
430 }
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448 public void characters(char[] buf, int offset, int len) {
449
450 if (charBuffer == null) {
451 charBuffer = new StringBuffer();
452 }
453 charBuffer.append(buf, offset, len);
454 }
455
456 private void processChars() throws SAXException {
457
458 if (charBuffer == null || directivesOnly) {
459 return;
460 }
461
462
463
464
465
466
467
468
469
470 boolean isAllSpace = true;
471 if (!(current instanceof Node.JspText)
472 && !(current instanceof Node.NamedAttribute)) {
473 for (int i = 0; i < charBuffer.length(); i++) {
474 if (!(charBuffer.charAt(i) == ' '
475 || charBuffer.charAt(i) == '\n'
476 || charBuffer.charAt(i) == '\r'
477 || charBuffer.charAt(i) == '\t')) {
478 isAllSpace = false;
479 break;
480 }
481 }
482 }
483
484 if (!isAllSpace && tagDependentPending) {
485 tagDependentPending = false;
486 tagDependentNesting++;
487 }
488
489 if (tagDependentNesting > 0) {
490 if (charBuffer.length() > 0) {
491 new Node.TemplateText(charBuffer.toString(), startMark, current);
492 }
493 startMark = new Mark(ctxt, path, locator.getLineNumber(),
494 locator.getColumnNumber());
495 charBuffer = null;
496 return;
497 }
498
499 if ((current instanceof Node.JspText)
500 || (current instanceof Node.NamedAttribute)
501 || !isAllSpace) {
502
503 int line = startMark.getLineNumber();
504 int column = startMark.getColumnNumber();
505
506 CharArrayWriter ttext = new CharArrayWriter();
507 int lastCh = 0;
508 for (int i = 0; i < charBuffer.length(); i++) {
509
510 int ch = charBuffer.charAt(i);
511 if (ch == '\n') {
512 column = 1;
513 line++;
514 } else {
515 column++;
516 }
517 if (lastCh == '$' && ch == '{') {
518 if (ttext.size() > 0) {
519 new Node.TemplateText(
520 ttext.toString(),
521 startMark,
522 current);
523 ttext = new CharArrayWriter();
524
525
526 startMark = new Mark(ctxt, path, line, column - 2);
527 }
528
529 i++;
530 boolean singleQ = false;
531 boolean doubleQ = false;
532 lastCh = 0;
533 for (; ; i++) {
534 if (i >= charBuffer.length()) {
535 throw new SAXParseException(
536 Localizer.getMessage(
537 "jsp.error.unterminated",
538 "${"),
539 locator);
540
541 }
542 ch = charBuffer.charAt(i);
543 if (ch == '\n') {
544 column = 1;
545 line++;
546 } else {
547 column++;
548 }
549 if (lastCh == '//' && (singleQ || doubleQ)) {
550 ttext.write(ch);
551 lastCh = 0;
552 continue;
553 }
554 if (ch == '}') {
555 new Node.ELExpression(
556 ttext.toString(),
557 startMark,
558 current);
559 ttext = new CharArrayWriter();
560 startMark = new Mark(ctxt, path, line, column);
561 break;
562 }
563 if (ch == '"')
564 doubleQ = !doubleQ;
565 else if (ch == '\'')
566 singleQ = !singleQ;
567
568 ttext.write(ch);
569 lastCh = ch;
570 }
571 } else if (lastCh == '//' && ch == '$') {
572 ttext.write('$');
573 ch = 0;
574 } else {
575 if (lastCh == '$' || lastCh == '//') {
576 ttext.write(lastCh);
577 }
578 if (ch != '$' && ch != '//') {
579 ttext.write(ch);
580 }
581 }
582 lastCh = ch;
583 }
584 if (lastCh == '$' || lastCh == '//') {
585 ttext.write(lastCh);
586 }
587 if (ttext.size() > 0) {
588 new Node.TemplateText(ttext.toString(), startMark, current);
589 }
590 }
591 startMark = new Mark(ctxt, path, locator.getLineNumber(),
592 locator.getColumnNumber());
593
594 charBuffer = null;
595 }
596
597
598
599
600 public void endElement(String uri, String localName, String qName)
601 throws SAXException {
602
603 processChars();
604
605 if (directivesOnly &&
606 !(JSP_URI.equals(uri) && localName.startsWith(DIRECTIVE_ACTION))) {
607 return;
608 }
609
610 if (current instanceof Node.NamedAttribute) {
611 boolean isTrim = ((Node.NamedAttribute) current).isTrim();
612 Node.Nodes subElems = ((Node.NamedAttribute) current).getBody();
613 for (int i = 0; subElems != null && i < subElems.size(); i++) {
614 Node subElem = subElems.getNode(i);
615 if (!(subElem instanceof Node.TemplateText)) {
616 continue;
617 }
618
619
620
621
622
623
624
625
626
627 if (i == 0) {
628 if (isTrim) {
629 ((Node.TemplateText) subElem).ltrim();
630 }
631 } else if (i == subElems.size() - 1) {
632 if (isTrim) {
633 ((Node.TemplateText) subElem).rtrim();
634 }
635 } else {
636 if (((Node.TemplateText) subElem).isAllSpace()) {
637 subElems.remove(subElem);
638 }
639 }
640 }
641 } else if (current instanceof Node.ScriptingElement) {
642 checkScriptingBody((Node.ScriptingElement) current);
643 }
644
645 if (isTagDependent(current)) {
646 tagDependentNesting--;
647 }
648
649 if (scriptlessBodyNode != null
650 && current.equals(scriptlessBodyNode)) {
651 scriptlessBodyNode = null;
652 }
653
654 if (current.getParent() != null) {
655 current = current.getParent();
656 }
657 }
658
659
660
661
662
663
664 public void setDocumentLocator(Locator locator) {
665 this.locator = locator;
666 }
667
668
669
670
671 public void comment(char[] buf, int offset, int len) throws SAXException {
672
673 processChars();
674
675
676 if (!inDTD) {
677 startMark =
678 new Mark(
679 ctxt,
680 path,
681 locator.getLineNumber(),
682 locator.getColumnNumber());
683 new Node.Comment(new String(buf, offset, len), startMark, current);
684 }
685 }
686
687
688
689
690 public void startCDATA() throws SAXException {
691
692 processChars();
693 startMark = new Mark(ctxt, path, locator.getLineNumber(),
694 locator.getColumnNumber());
695 }
696
697
698
699
700 public void endCDATA() throws SAXException {
701 processChars();
702 }
703
704
705
706
707 public void startEntity(String name) throws SAXException {
708
709 }
710
711
712
713
714 public void endEntity(String name) throws SAXException {
715
716 }
717
718
719
720
721 public void startDTD(String name, String publicId, String systemId)
722 throws SAXException {
723 if (!isValidating) {
724 fatalError(ENABLE_DTD_VALIDATION_EXCEPTION);
725 }
726
727 inDTD = true;
728 }
729
730
731
732
733 public void endDTD() throws SAXException {
734 inDTD = false;
735 }
736
737
738
739
740 public void fatalError(SAXParseException e) throws SAXException {
741 throw e;
742 }
743
744
745
746
747 public void error(SAXParseException e) throws SAXException {
748 throw e;
749 }
750
751
752
753
754 public void startPrefixMapping(String prefix, String uri)
755 throws SAXException {
756 TagLibraryInfo taglibInfo;
757
758 if (directivesOnly && !(JSP_URI.equals(uri))) {
759 return;
760 }
761
762 try {
763 taglibInfo = getTaglibInfo(prefix, uri);
764 } catch (JasperException je) {
765 throw new SAXParseException(
766 Localizer.getMessage("jsp.error.could.not.add.taglibraries"),
767 locator,
768 je);
769 }
770
771 if (taglibInfo != null) {
772 if (pageInfo.getTaglib(uri) == null) {
773 pageInfo.addTaglib(uri, taglibInfo);
774 }
775 pageInfo.pushPrefixMapping(prefix, uri);
776 } else {
777 pageInfo.pushPrefixMapping(prefix, null);
778 }
779 }
780
781
782
783
784 public void endPrefixMapping(String prefix) throws SAXException {
785
786 if (directivesOnly) {
787 String uri = pageInfo.getURI(prefix);
788 if (!JSP_URI.equals(uri)) {
789 return;
790 }
791 }
792
793 pageInfo.popPrefixMapping(prefix);
794 }
795
796 //**********************************************************************
797 // Private utility methods
798
799 private Node parseStandardAction(
800 String qName,
801 String localName,
802 Attributes nonTaglibAttrs,
803 Attributes nonTaglibXmlnsAttrs,
804 Attributes taglibAttrs,
805 Mark start,
806 Node parent)
807 throws SAXException {
808
809 Node node = null;
810
811 if (localName.equals(ROOT_ACTION)) {
812 if (!(current instanceof Node.Root)) {
813 throw new SAXParseException(
814 Localizer.getMessage("jsp.error.nested_jsproot"),
815 locator);
816 }
817 node =
818 new Node.JspRoot(
819 qName,
820 nonTaglibAttrs,
821 nonTaglibXmlnsAttrs,
822 taglibAttrs,
823 start,
824 current);
825 if (isTop) {
826 pageInfo.setHasJspRoot(true);
827 }
828 } else if (localName.equals(PAGE_DIRECTIVE_ACTION)) {
829 if (isTagFile) {
830 throw new SAXParseException(
831 Localizer.getMessage(
832 "jsp.error.action.istagfile",
833 localName),
834 locator);
835 }
836 node =
837 new Node.PageDirective(
838 qName,
839 nonTaglibAttrs,
840 nonTaglibXmlnsAttrs,
841 taglibAttrs,
842 start,
843 current);
844 String imports = nonTaglibAttrs.getValue("import");
845 // There can only be one 'import' attribute per page directive
846 if (imports != null) {
847 ((Node.PageDirective) node).addImport(imports);
848 }
849 } else if (localName.equals(INCLUDE_DIRECTIVE_ACTION)) {
850 node =
851 new Node.IncludeDirective(
852 qName,
853 nonTaglibAttrs,
854 nonTaglibXmlnsAttrs,
855 taglibAttrs,
856 start,
857 current);
858 processIncludeDirective(nonTaglibAttrs.getValue("file"), node);
859 } else if (localName.equals(DECLARATION_ACTION)) {
860 if (scriptlessBodyNode != null) {
861 // We're nested inside a node whose body is
862 // declared to be scriptless
863 throw new SAXParseException(
864 Localizer.getMessage(
865 "jsp.error.no.scriptlets",
866 localName),
867 locator);
868 }
869 node =
870 new Node.Declaration(
871 qName,
872 nonTaglibXmlnsAttrs,
873 taglibAttrs,
874 start,
875 current);
876 } else if (localName.equals(SCRIPTLET_ACTION)) {
877 if (scriptlessBodyNode != null) {
878 // We're nested inside a node whose body is
879 // declared to be scriptless
880 throw new SAXParseException(
881 Localizer.getMessage(
882 "jsp.error.no.scriptlets",
883 localName),
884 locator);
885 }
886 node =
887 new Node.Scriptlet(
888 qName,
889 nonTaglibXmlnsAttrs,
890 taglibAttrs,
891 start,
892 current);
893 } else if (localName.equals(EXPRESSION_ACTION)) {
894 if (scriptlessBodyNode != null) {
895 // We're nested inside a node whose body is
896 // declared to be scriptless
897 throw new SAXParseException(
898 Localizer.getMessage(
899 "jsp.error.no.scriptlets",
900 localName),
901 locator);
902 }
903 node =
904 new Node.Expression(
905 qName,
906 nonTaglibXmlnsAttrs,
907 taglibAttrs,
908 start,
909 current);
910 } else if (localName.equals(USE_BEAN_ACTION)) {
911 node =
912 new Node.UseBean(
913 qName,
914 nonTaglibAttrs,
915 nonTaglibXmlnsAttrs,
916 taglibAttrs,
917 start,
918 current);
919 } else if (localName.equals(SET_PROPERTY_ACTION)) {
920 node =
921 new Node.SetProperty(
922 qName,
923 nonTaglibAttrs,
924 nonTaglibXmlnsAttrs,
925 taglibAttrs,
926 start,
927 current);
928 } else if (localName.equals(GET_PROPERTY_ACTION)) {
929 node =
930 new Node.GetProperty(
931 qName,
932 nonTaglibAttrs,
933 nonTaglibXmlnsAttrs,
934 taglibAttrs,
935 start,
936 current);
937 } else if (localName.equals(INCLUDE_ACTION)) {
938 node =
939 new Node.IncludeAction(
940 qName,
941 nonTaglibAttrs,
942 nonTaglibXmlnsAttrs,
943 taglibAttrs,
944 start,
945 current);
946 } else if (localName.equals(FORWARD_ACTION)) {
947 node =
948 new Node.ForwardAction(
949 qName,
950 nonTaglibAttrs,
951 nonTaglibXmlnsAttrs,
952 taglibAttrs,
953 start,
954 current);
955 } else if (localName.equals(PARAM_ACTION)) {
956 node =
957 new Node.ParamAction(
958 qName,
959 nonTaglibAttrs,
960 nonTaglibXmlnsAttrs,
961 taglibAttrs,
962 start,
963 current);
964 } else if (localName.equals(PARAMS_ACTION)) {
965 node =
966 new Node.ParamsAction(
967 qName,
968 nonTaglibXmlnsAttrs,
969 taglibAttrs,
970 start,
971 current);
972 } else if (localName.equals(PLUGIN_ACTION)) {
973 node =
974 new Node.PlugIn(
975 qName,
976 nonTaglibAttrs,
977 nonTaglibXmlnsAttrs,
978 taglibAttrs,
979 start,
980 current);
981 } else if (localName.equals(TEXT_ACTION)) {
982 node =
983 new Node.JspText(
984 qName,
985 nonTaglibXmlnsAttrs,
986 taglibAttrs,
987 start,
988 current);
989 } else if (localName.equals(BODY_ACTION)) {
990 node =
991 new Node.JspBody(
992 qName,
993 nonTaglibXmlnsAttrs,
994 taglibAttrs,
995 start,
996 current);
997 } else if (localName.equals(ATTRIBUTE_ACTION)) {
998 node =
999 new Node.NamedAttribute(
1000 qName,
1001 nonTaglibAttrs,
1002 nonTaglibXmlnsAttrs,
1003 taglibAttrs,
1004 start,
1005 current);
1006 } else if (localName.equals(OUTPUT_ACTION)) {
1007 node =
1008 new Node.JspOutput(
1009 qName,
1010 nonTaglibAttrs,
1011 nonTaglibXmlnsAttrs,
1012 taglibAttrs,
1013 start,
1014 current);
1015 } else if (localName.equals(TAG_DIRECTIVE_ACTION)) {
1016 if (!isTagFile) {
1017 throw new SAXParseException(
1018 Localizer.getMessage(
1019 "jsp.error.action.isnottagfile",
1020 localName),
1021 locator);
1022 }
1023 node =
1024 new Node.TagDirective(
1025 qName,
1026 nonTaglibAttrs,
1027 nonTaglibXmlnsAttrs,
1028 taglibAttrs,
1029 start,
1030 current);
1031 String imports = nonTaglibAttrs.getValue("import");
1032 // There can only be one 'import' attribute per tag directive
1033 if (imports != null) {
1034 ((Node.TagDirective) node).addImport(imports);
1035 }
1036 } else if (localName.equals(ATTRIBUTE_DIRECTIVE_ACTION)) {
1037 if (!isTagFile) {
1038 throw new SAXParseException(
1039 Localizer.getMessage(
1040 "jsp.error.action.isnottagfile",
1041 localName),
1042 locator);
1043 }
1044 node =
1045 new Node.AttributeDirective(
1046 qName,
1047 nonTaglibAttrs,
1048 nonTaglibXmlnsAttrs,
1049 taglibAttrs,
1050 start,
1051 current);
1052 } else if (localName.equals(VARIABLE_DIRECTIVE_ACTION)) {
1053 if (!isTagFile) {
1054 throw new SAXParseException(
1055 Localizer.getMessage(
1056 "jsp.error.action.isnottagfile",
1057 localName),
1058 locator);
1059 }
1060 node =
1061 new Node.VariableDirective(
1062 qName,
1063 nonTaglibAttrs,
1064 nonTaglibXmlnsAttrs,
1065 taglibAttrs,
1066 start,
1067 current);
1068 } else if (localName.equals(INVOKE_ACTION)) {
1069 if (!isTagFile) {
1070 throw new SAXParseException(
1071 Localizer.getMessage(
1072 "jsp.error.action.isnottagfile",
1073 localName),
1074 locator);
1075 }
1076 node =
1077 new Node.InvokeAction(
1078 qName,
1079 nonTaglibAttrs,
1080 nonTaglibXmlnsAttrs,
1081 taglibAttrs,
1082 start,
1083 current);
1084 } else if (localName.equals(DOBODY_ACTION)) {
1085 if (!isTagFile) {
1086 throw new SAXParseException(
1087 Localizer.getMessage(
1088 "jsp.error.action.isnottagfile",
1089 localName),
1090 locator);
1091 }
1092 node =
1093 new Node.DoBodyAction(
1094 qName,
1095 nonTaglibAttrs,
1096 nonTaglibXmlnsAttrs,
1097 taglibAttrs,
1098 start,
1099 current);
1100 } else if (localName.equals(ELEMENT_ACTION)) {
1101 node =
1102 new Node.JspElement(
1103 qName,
1104 nonTaglibAttrs,
1105 nonTaglibXmlnsAttrs,
1106 taglibAttrs,
1107 start,
1108 current);
1109 } else if (localName.equals(FALLBACK_ACTION)) {
1110 node =
1111 new Node.FallBackAction(
1112 qName,
1113 nonTaglibXmlnsAttrs,
1114 taglibAttrs,
1115 start,
1116 current);
1117 } else {
1118 throw new SAXParseException(
1119 Localizer.getMessage(
1120 "jsp.error.xml.badStandardAction",
1121 localName),
1122 locator);
1123 }
1124
1125 return node;
1126 }
1127
1128 /*
1129 * Checks if the XML element with the given tag name is a custom action,
1130 * and returns the corresponding Node object.
1131 */
1132 private Node parseCustomAction(
1133 String qName,
1134 String localName,
1135 String uri,
1136 Attributes nonTaglibAttrs,
1137 Attributes nonTaglibXmlnsAttrs,
1138 Attributes taglibAttrs,
1139 Mark start,
1140 Node parent)
1141 throws SAXException {
1142
1143
1144 TagLibraryInfo tagLibInfo = pageInfo.getTaglib(uri);
1145 if (tagLibInfo == null) {
1146 return null;
1147 }
1148
1149 TagInfo tagInfo = tagLibInfo.getTag(localName);
1150 TagFileInfo tagFileInfo = tagLibInfo.getTagFile(localName);
1151 if (tagInfo == null && tagFileInfo == null) {
1152 throw new SAXException(
1153 Localizer.getMessage("jsp.error.xml.bad_tag", localName, uri));
1154 }
1155 Class tagHandlerClass = null;
1156 if (tagInfo != null) {
1157 String handlerClassName = tagInfo.getTagClassName();
1158 try {
1159 tagHandlerClass =
1160 ctxt.getClassLoader().loadClass(handlerClassName);
1161 } catch (Exception e) {
1162 throw new SAXException(
1163 Localizer.getMessage("jsp.error.loadclass.taghandler",
1164 handlerClassName,
1165 qName),
1166 e);
1167 }
1168 }
1169
1170 String prefix = getPrefix(qName);
1171
1172 Node.CustomTag ret = null;
1173 if (tagInfo != null) {
1174 ret =
1175 new Node.CustomTag(
1176 qName,
1177 prefix,
1178 localName,
1179 uri,
1180 nonTaglibAttrs,
1181 nonTaglibXmlnsAttrs,
1182 taglibAttrs,
1183 start,
1184 parent,
1185 tagInfo,
1186 tagHandlerClass);
1187 } else {
1188 ret =
1189 new Node.CustomTag(
1190 qName,
1191 prefix,
1192 localName,
1193 uri,
1194 nonTaglibAttrs,
1195 nonTaglibXmlnsAttrs,
1196 taglibAttrs,
1197 start,
1198 parent,
1199 tagFileInfo);
1200 }
1201
1202 return ret;
1203 }
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214 private TagLibraryInfo getTaglibInfo(String prefix, String uri)
1215 throws JasperException {
1216
1217 TagLibraryInfo result = null;
1218
1219 if (uri.startsWith(URN_JSPTAGDIR)) {
1220
1221 String tagdir = uri.substring(URN_JSPTAGDIR.length());
1222 result =
1223 new ImplicitTagLibraryInfo(
1224 ctxt,
1225 parserController,
1226 prefix,
1227 tagdir,
1228 err);
1229 } else {
1230
1231 boolean isPlainUri = false;
1232 if (uri.startsWith(URN_JSPTLD)) {
1233
1234 uri = uri.substring(URN_JSPTLD.length());
1235 } else {
1236 isPlainUri = true;
1237 }
1238
1239 String[] location = ctxt.getTldLocation(uri);
1240 if (location != null || !isPlainUri) {
1241 if (ctxt.getOptions().isCaching()) {
1242 result = (TagLibraryInfoImpl) ctxt.getOptions().getCache().get(uri);
1243 }
1244 if (result == null) {
1245
1246
1247
1248
1249
1250
1251 result =
1252 new TagLibraryInfoImpl(
1253 ctxt,
1254 parserController,
1255 prefix,
1256 uri,
1257 location,
1258 err);
1259 if (ctxt.getOptions().isCaching()) {
1260 ctxt.getOptions().getCache().put(uri, result);
1261 }
1262 }
1263 }
1264 }
1265
1266 return result;
1267 }
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277 private void checkScriptingBody(Node.ScriptingElement scriptingElem)
1278 throws SAXException {
1279 Node.Nodes body = scriptingElem.getBody();
1280 if (body != null) {
1281 int size = body.size();
1282 for (int i = 0; i < size; i++) {
1283 Node n = body.getNode(i);
1284 if (!(n instanceof Node.TemplateText)) {
1285 String elemType = SCRIPTLET_ACTION;
1286 if (scriptingElem instanceof Node.Declaration)
1287 elemType = DECLARATION_ACTION;
1288 if (scriptingElem instanceof Node.Expression)
1289 elemType = EXPRESSION_ACTION;
1290 String msg =
1291 Localizer.getMessage(
1292 "jsp.error.parse.xml.scripting.invalid.body",
1293 elemType);
1294 throw new SAXException(msg);
1295 }
1296 }
1297 }
1298 }
1299
1300
1301
1302
1303
1304
1305
1306
1307 private void processIncludeDirective(String fname, Node parent)
1308 throws SAXException {
1309
1310 if (fname == null) {
1311 return;
1312 }
1313
1314 try {
1315 parserController.parse(fname, parent, null);
1316 } catch (FileNotFoundException fnfe) {
1317 throw new SAXParseException(
1318 Localizer.getMessage("jsp.error.file.not.found", fname),
1319 locator,
1320 fnfe);
1321 } catch (Exception e) {
1322 throw new SAXException(e);
1323 }
1324 }
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335 private void checkPrefixes(String uri, String qName, Attributes attrs) {
1336
1337 checkPrefix(uri, qName);
1338
1339 int len = attrs.getLength();
1340 for (int i = 0; i < len; i++) {
1341 checkPrefix(attrs.getURI(i), attrs.getQName(i));
1342 }
1343 }
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353 private void checkPrefix(String uri, String qName) {
1354
1355 String prefix = getPrefix(qName);
1356 if (prefix.length() > 0) {
1357 pageInfo.addPrefix(prefix);
1358 if ("jsp".equals(prefix) && !JSP_URI.equals(uri)) {
1359 pageInfo.setIsJspPrefixHijacked(true);
1360 }
1361 }
1362 }
1363
1364 private String getPrefix(String qName) {
1365 int index = qName.indexOf(':');
1366 if (index != -1) {
1367 return qName.substring(0, index);
1368 }
1369 return "";
1370 }
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381 private static SAXParser getSAXParser(
1382 boolean validating,
1383 JspDocumentParser jspDocParser)
1384 throws Exception {
1385
1386 SAXParserFactory factory = SAXParserFactory.newInstance();
1387 factory.setNamespaceAware(true);
1388
1389
1390 factory.setFeature(
1391 "http://xml.org/sax/features/namespace-prefixes",
1392 true);
1393 factory.setValidating(validating);
1394
1395
1396
1397
1398
1399 SAXParser saxParser = factory.newSAXParser();
1400 XMLReader xmlReader = saxParser.getXMLReader();
1401 xmlReader.setProperty(LEXICAL_HANDLER_PROPERTY, jspDocParser);
1402 xmlReader.setErrorHandler(jspDocParser);
1403
1404 return saxParser;
1405 }
1406
1407
1408
1409
1410
1411 private static class EnableDTDValidationException
1412 extends SAXParseException {
1413
1414 EnableDTDValidationException(String message, Locator loc) {
1415 super(message, loc);
1416 }
1417 }
1418
1419 private static String getBodyType(Node.CustomTag custom) {
1420
1421 if (custom.getTagInfo() != null) {
1422 return custom.getTagInfo().getBodyContent();
1423 }
1424
1425 return custom.getTagFileInfo().getTagInfo().getBodyContent();
1426 }
1427
1428 private boolean isTagDependent(Node n) {
1429
1430 if (n instanceof Node.CustomTag) {
1431 String bodyType = getBodyType((Node.CustomTag) n);
1432 return
1433 TagInfo.BODY_CONTENT_TAG_DEPENDENT.equalsIgnoreCase(bodyType);
1434 }
1435 return false;
1436 }
1437 }