KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > jasper > compiler > Validator


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 package org.apache.jasper.compiler;
19
20 import java.lang.reflect.Method JavaDoc;
21 import java.util.ArrayList JavaDoc;
22 import java.util.HashMap JavaDoc;
23 import java.util.Hashtable JavaDoc;
24 import java.util.Iterator JavaDoc;
25
26 import javax.el.ELException;
27 import javax.el.ExpressionFactory;
28 import javax.el.FunctionMapper;
29 import javax.servlet.jsp.tagext.FunctionInfo JavaDoc;
30 import javax.servlet.jsp.tagext.JspFragment JavaDoc;
31 import javax.servlet.jsp.tagext.PageData JavaDoc;
32 import javax.servlet.jsp.tagext.TagAttributeInfo JavaDoc;
33 import javax.servlet.jsp.tagext.TagData JavaDoc;
34 import javax.servlet.jsp.tagext.TagExtraInfo JavaDoc;
35 import javax.servlet.jsp.tagext.TagInfo JavaDoc;
36 import javax.servlet.jsp.tagext.TagLibraryInfo JavaDoc;
37 import javax.servlet.jsp.tagext.ValidationMessage JavaDoc;
38
39 import org.apache.el.lang.ELSupport;
40 import org.apache.jasper.Constants;
41 import org.apache.jasper.JasperException;
42 import org.apache.jasper.el.ELContextImpl;
43 import org.xml.sax.Attributes JavaDoc;
44
45 /**
46  * Performs validation on the page elements. Attributes are checked for
47  * mandatory presence, entry value validity, and consistency. As a side effect,
48  * some page global value (such as those from page direcitves) are stored, for
49  * later use.
50  *
51  * @author Kin-man Chung
52  * @author Jan Luehe
53  * @author Shawn Bayern
54  * @author Mark Roth
55  */

56 class Validator {
57
58     /**
59      * A visitor to validate and extract page directive info
60      */

61     static class DirectiveVisitor extends Node.Visitor {
62
63         private PageInfo pageInfo;
64
65         private ErrorDispatcher err;
66
67         private static final JspUtil.ValidAttribute[] pageDirectiveAttrs = {
68             new JspUtil.ValidAttribute("language"),
69             new JspUtil.ValidAttribute("extends"),
70             new JspUtil.ValidAttribute("import"),
71             new JspUtil.ValidAttribute("session"),
72             new JspUtil.ValidAttribute("buffer"),
73             new JspUtil.ValidAttribute("autoFlush"),
74             new JspUtil.ValidAttribute("isThreadSafe"),
75             new JspUtil.ValidAttribute("info"),
76             new JspUtil.ValidAttribute("errorPage"),
77             new JspUtil.ValidAttribute("isErrorPage"),
78             new JspUtil.ValidAttribute("contentType"),
79             new JspUtil.ValidAttribute("pageEncoding"),
80             new JspUtil.ValidAttribute("isELIgnored"),
81             new JspUtil.ValidAttribute("deferredSyntaxAllowedAsLiteral"),
82             new JspUtil.ValidAttribute("trimDirectiveWhitespaces")
83         };
84
85         private boolean pageEncodingSeen = false;
86
87         /*
88          * Constructor
89          */

90         DirectiveVisitor(Compiler JavaDoc compiler) throws JasperException {
91             this.pageInfo = compiler.getPageInfo();
92             this.err = compiler.getErrorDispatcher();
93         }
94
95         public void visit(Node.IncludeDirective n) throws JasperException {
96             // Since pageDirectiveSeen flag only applies to the Current page
97
// save it here and restore it after the file is included.
98
boolean pageEncodingSeenSave = pageEncodingSeen;
99             pageEncodingSeen = false;
100             visitBody(n);
101             pageEncodingSeen = pageEncodingSeenSave;
102         }
103
104         public void visit(Node.PageDirective n) throws JasperException {
105
106             JspUtil.checkAttributes("Page directive", n, pageDirectiveAttrs,
107                     err);
108
109             // JSP.2.10.1
110
Attributes JavaDoc attrs = n.getAttributes();
111             for (int i = 0; attrs != null && i < attrs.getLength(); i++) {
112                 String JavaDoc attr = attrs.getQName(i);
113                 String JavaDoc value = attrs.getValue(i);
114
115                 if ("language".equals(attr)) {
116                     if (pageInfo.getLanguage(false) == null) {
117                         pageInfo.setLanguage(value, n, err, true);
118                     } else if (!pageInfo.getLanguage(false).equals(value)) {
119                         err.jspError(n, "jsp.error.page.conflict.language",
120                                 pageInfo.getLanguage(false), value);
121                     }
122                 } else if ("extends".equals(attr)) {
123                     if (pageInfo.getExtends(false) == null) {
124                         pageInfo.setExtends(value, n);
125                     } else if (!pageInfo.getExtends(false).equals(value)) {
126                         err.jspError(n, "jsp.error.page.conflict.extends",
127                                 pageInfo.getExtends(false), value);
128                     }
129                 } else if ("contentType".equals(attr)) {
130                     if (pageInfo.getContentType() == null) {
131                         pageInfo.setContentType(value);
132                     } else if (!pageInfo.getContentType().equals(value)) {
133                         err.jspError(n, "jsp.error.page.conflict.contenttype",
134                                 pageInfo.getContentType(), value);
135                     }
136                 } else if ("session".equals(attr)) {
137                     if (pageInfo.getSession() == null) {
138                         pageInfo.setSession(value, n, err);
139                     } else if (!pageInfo.getSession().equals(value)) {
140                         err.jspError(n, "jsp.error.page.conflict.session",
141                                 pageInfo.getSession(), value);
142                     }
143                 } else if ("buffer".equals(attr)) {
144                     if (pageInfo.getBufferValue() == null) {
145                         pageInfo.setBufferValue(value, n, err);
146                     } else if (!pageInfo.getBufferValue().equals(value)) {
147                         err.jspError(n, "jsp.error.page.conflict.buffer",
148                                 pageInfo.getBufferValue(), value);
149                     }
150                 } else if ("autoFlush".equals(attr)) {
151                     if (pageInfo.getAutoFlush() == null) {
152                         pageInfo.setAutoFlush(value, n, err);
153                     } else if (!pageInfo.getAutoFlush().equals(value)) {
154                         err.jspError(n, "jsp.error.page.conflict.autoflush",
155                                 pageInfo.getAutoFlush(), value);
156                     }
157                 } else if ("isThreadSafe".equals(attr)) {
158                     if (pageInfo.getIsThreadSafe() == null) {
159                         pageInfo.setIsThreadSafe(value, n, err);
160                     } else if (!pageInfo.getIsThreadSafe().equals(value)) {
161                         err.jspError(n, "jsp.error.page.conflict.isthreadsafe",
162                                 pageInfo.getIsThreadSafe(), value);
163                     }
164                 } else if ("isELIgnored".equals(attr)) {
165                     if (pageInfo.getIsELIgnored() == null) {
166                         pageInfo.setIsELIgnored(value, n, err, true);
167                     } else if (!pageInfo.getIsELIgnored().equals(value)) {
168                         err.jspError(n, "jsp.error.page.conflict.iselignored",
169                                 pageInfo.getIsELIgnored(), value);
170                     }
171                 } else if ("isErrorPage".equals(attr)) {
172                     if (pageInfo.getIsErrorPage() == null) {
173                         pageInfo.setIsErrorPage(value, n, err);
174                     } else if (!pageInfo.getIsErrorPage().equals(value)) {
175                         err.jspError(n, "jsp.error.page.conflict.iserrorpage",
176                                 pageInfo.getIsErrorPage(), value);
177                     }
178                 } else if ("errorPage".equals(attr)) {
179                     if (pageInfo.getErrorPage() == null) {
180                         pageInfo.setErrorPage(value);
181                     } else if (!pageInfo.getErrorPage().equals(value)) {
182                         err.jspError(n, "jsp.error.page.conflict.errorpage",
183                                 pageInfo.getErrorPage(), value);
184                     }
185                 } else if ("info".equals(attr)) {
186                     if (pageInfo.getInfo() == null) {
187                         pageInfo.setInfo(value);
188                     } else if (!pageInfo.getInfo().equals(value)) {
189                         err.jspError(n, "jsp.error.page.conflict.info",
190                                 pageInfo.getInfo(), value);
191                     }
192                 } else if ("pageEncoding".equals(attr)) {
193                     if (pageEncodingSeen)
194                         err.jspError(n, "jsp.error.page.multi.pageencoding");
195                     // 'pageEncoding' can occur at most once per file
196
pageEncodingSeen = true;
197                     String JavaDoc actual = comparePageEncodings(value, n);
198                     n.getRoot().setPageEncoding(actual);
199                 } else if ("deferredSyntaxAllowedAsLiteral".equals(attr)) {
200                     if (pageInfo.getDeferredSyntaxAllowedAsLiteral() == null) {
201                         pageInfo.setDeferredSyntaxAllowedAsLiteral(value, n,
202                                 err, true);
203                     } else if (!pageInfo.getDeferredSyntaxAllowedAsLiteral()
204                             .equals(value)) {
205                         err
206                                 .jspError(
207                                         n,
208                                         "jsp.error.page.conflict.deferredsyntaxallowedasliteral",
209                                         pageInfo
210                                                 .getDeferredSyntaxAllowedAsLiteral(),
211                                         value);
212                     }
213                 } else if ("trimDirectiveWhitespaces".equals(attr)) {
214                     if (pageInfo.getTrimDirectiveWhitespaces() == null) {
215                         pageInfo.setTrimDirectiveWhitespaces(value, n, err,
216                                 true);
217                     } else if (!pageInfo.getTrimDirectiveWhitespaces().equals(
218                             value)) {
219                         err
220                                 .jspError(
221                                         n,
222                                         "jsp.error.page.conflict.trimdirectivewhitespaces",
223                                         pageInfo.getTrimDirectiveWhitespaces(),
224                                         value);
225                     }
226                 }
227             }
228
229             // Check for bad combinations
230
if (pageInfo.getBuffer() == 0 && !pageInfo.isAutoFlush())
231                 err.jspError(n, "jsp.error.page.badCombo");
232
233             // Attributes for imports for this node have been processed by
234
// the parsers, just add them to pageInfo.
235
pageInfo.addImports(n.getImports());
236         }
237
238         public void visit(Node.TagDirective n) throws JasperException {
239             // Note: Most of the validation is done in TagFileProcessor
240
// when it created a TagInfo object from the
241
// tag file in which the directive appeared.
242

243             // This method does additional processing to collect page info
244

245             Attributes JavaDoc attrs = n.getAttributes();
246             for (int i = 0; attrs != null && i < attrs.getLength(); i++) {
247                 String JavaDoc attr = attrs.getQName(i);
248                 String JavaDoc value = attrs.getValue(i);
249
250                 if ("language".equals(attr)) {
251                     if (pageInfo.getLanguage(false) == null) {
252                         pageInfo.setLanguage(value, n, err, false);
253                     } else if (!pageInfo.getLanguage(false).equals(value)) {
254                         err.jspError(n, "jsp.error.tag.conflict.language",
255                                 pageInfo.getLanguage(false), value);
256                     }
257                 } else if ("isELIgnored".equals(attr)) {
258                     if (pageInfo.getIsELIgnored() == null) {
259                         pageInfo.setIsELIgnored(value, n, err, false);
260                     } else if (!pageInfo.getIsELIgnored().equals(value)) {
261                         err.jspError(n, "jsp.error.tag.conflict.iselignored",
262                                 pageInfo.getIsELIgnored(), value);
263                     }
264                 } else if ("pageEncoding".equals(attr)) {
265                     if (pageEncodingSeen)
266                         err.jspError(n, "jsp.error.tag.multi.pageencoding");
267                     pageEncodingSeen = true;
268                     compareTagEncodings(value, n);
269                     n.getRoot().setPageEncoding(value);
270                 } else if ("deferredSyntaxAllowedAsLiteral".equals(attr)) {
271                     if (pageInfo.getDeferredSyntaxAllowedAsLiteral() == null) {
272                         pageInfo.setDeferredSyntaxAllowedAsLiteral(value, n,
273                                 err, false);
274                     } else if (!pageInfo.getDeferredSyntaxAllowedAsLiteral()
275                             .equals(value)) {
276                         err
277                                 .jspError(
278                                         n,
279                                         "jsp.error.tag.conflict.deferredsyntaxallowedasliteral",
280                                         pageInfo
281                                                 .getDeferredSyntaxAllowedAsLiteral(),
282                                         value);
283                     }
284                 } else if ("trimDirectiveWhitespaces".equals(attr)) {
285                     if (pageInfo.getTrimDirectiveWhitespaces() == null) {
286                         pageInfo.setTrimDirectiveWhitespaces(value, n, err,
287                                 false);
288                     } else if (!pageInfo.getTrimDirectiveWhitespaces().equals(
289                             value)) {
290                         err
291                                 .jspError(
292                                         n,
293                                         "jsp.error.tag.conflict.trimdirectivewhitespaces",
294                                         pageInfo.getTrimDirectiveWhitespaces(),
295                                         value);
296                     }
297                 }
298             }
299
300             // Attributes for imports for this node have been processed by
301
// the parsers, just add them to pageInfo.
302
pageInfo.addImports(n.getImports());
303         }
304
305         public void visit(Node.AttributeDirective n) throws JasperException {
306             // Do nothing, since this attribute directive has already been
307
// validated by TagFileProcessor when it created a TagInfo object
308
// from the tag file in which the directive appeared
309
}
310
311         public void visit(Node.VariableDirective n) throws JasperException {
312             // Do nothing, since this variable directive has already been
313
// validated by TagFileProcessor when it created a TagInfo object
314
// from the tag file in which the directive appeared
315
}
316
317         /*
318          * Compares page encodings specified in various places, and throws
319          * exception in case of page encoding mismatch.
320          *
321          * @param pageDirEnc The value of the pageEncoding attribute of the page
322          * directive @param pageDir The page directive node
323          *
324          * @throws JasperException in case of page encoding mismatch
325          */

326         private String JavaDoc comparePageEncodings(String JavaDoc pageDirEnc,
327                 Node.PageDirective pageDir) throws JasperException {
328
329             Node.Root root = pageDir.getRoot();
330             String JavaDoc configEnc = root.getJspConfigPageEncoding();
331
332             /*
333              * Compare the 'pageEncoding' attribute of the page directive with
334              * the encoding specified in the JSP config element whose URL
335              * pattern matches this page. Treat "UTF-16", "UTF-16BE", and
336              * "UTF-16LE" as identical.
337              */

338             if (configEnc != null) {
339                 if (!pageDirEnc.equals(configEnc)
340                         && (!pageDirEnc.startsWith("UTF-16") || !configEnc
341                                 .startsWith("UTF-16"))) {
342                     err.jspError(pageDir,
343                             "jsp.error.config_pagedir_encoding_mismatch",
344                             configEnc, pageDirEnc);
345                 } else {
346                     return configEnc;
347                 }
348             }
349
350             /*
351              * Compare the 'pageEncoding' attribute of the page directive with
352              * the encoding specified in the XML prolog (only for XML syntax,
353              * and only if JSP document contains XML prolog with encoding
354              * declaration). Treat "UTF-16", "UTF-16BE", and "UTF-16LE" as
355              * identical.
356              */

357             if ((root.isXmlSyntax() && root.isEncodingSpecifiedInProlog()) || root.isBomPresent()) {
358                 String JavaDoc pageEnc = root.getPageEncoding();
359                 if (!pageDirEnc.equals(pageEnc)
360                         && (!pageDirEnc.startsWith("UTF-16") || !pageEnc
361                                 .startsWith("UTF-16"))) {
362                     err.jspError(pageDir,
363                             "jsp.error.prolog_pagedir_encoding_mismatch",
364                             pageEnc, pageDirEnc);
365                 } else {
366                     return pageEnc;
367                 }
368             }
369             
370             return pageDirEnc;
371         }
372         
373         /*
374          * Compares page encodings specified in various places, and throws
375          * exception in case of page encoding mismatch.
376          *
377          * @param pageDirEnc The value of the pageEncoding attribute of the page
378          * directive @param pageDir The page directive node
379          *
380          * @throws JasperException in case of page encoding mismatch
381          */

382         private void compareTagEncodings(String JavaDoc pageDirEnc,
383                 Node.TagDirective pageDir) throws JasperException {
384
385             Node.Root root = pageDir.getRoot();
386
387             /*
388              * Compare the 'pageEncoding' attribute of the page directive with
389              * the encoding specified in the XML prolog (only for XML syntax,
390              * and only if JSP document contains XML prolog with encoding
391              * declaration). Treat "UTF-16", "UTF-16BE", and "UTF-16LE" as
392              * identical.
393              */

394             if ((root.isXmlSyntax() && root.isEncodingSpecifiedInProlog()) || root.isBomPresent()) {
395                 String JavaDoc pageEnc = root.getPageEncoding();
396                 if (!pageDirEnc.equals(pageEnc)
397                         && (!pageDirEnc.startsWith("UTF-16") || !pageEnc
398                                 .startsWith("UTF-16"))) {
399                     err.jspError(pageDir,
400                             "jsp.error.prolog_pagedir_encoding_mismatch",
401                             pageEnc, pageDirEnc);
402                 }
403             }
404         }
405
406     }
407
408     /**
409      * A visitor for validating nodes other than page directives
410      */

411     static class ValidateVisitor extends Node.Visitor {
412
413         private PageInfo pageInfo;
414
415         private ErrorDispatcher err;
416
417         private TagInfo JavaDoc tagInfo;
418
419         private ClassLoader JavaDoc loader;
420
421         private final StringBuffer JavaDoc buf = new StringBuffer JavaDoc(32);
422
423         private static final JspUtil.ValidAttribute[] jspRootAttrs = {
424                 new JspUtil.ValidAttribute("xsi:schemaLocation"),
425                 new JspUtil.ValidAttribute("version", true) };
426
427         private static final JspUtil.ValidAttribute[] includeDirectiveAttrs = { new JspUtil.ValidAttribute(
428                 "file", true) };
429
430         private static final JspUtil.ValidAttribute[] taglibDirectiveAttrs = {
431                 new JspUtil.ValidAttribute("uri"),
432                 new JspUtil.ValidAttribute("tagdir"),
433                 new JspUtil.ValidAttribute("prefix", true) };
434
435         private static final JspUtil.ValidAttribute[] includeActionAttrs = {
436                 new JspUtil.ValidAttribute("page", true, true),
437                 new JspUtil.ValidAttribute("flush") };
438
439         private static final JspUtil.ValidAttribute[] paramActionAttrs = {
440                 new JspUtil.ValidAttribute("name", true),
441                 new JspUtil.ValidAttribute("value", true, true) };
442
443         private static final JspUtil.ValidAttribute[] forwardActionAttrs = { new JspUtil.ValidAttribute(
444                 "page", true, true) };
445
446         private static final JspUtil.ValidAttribute[] getPropertyAttrs = {
447                 new JspUtil.ValidAttribute("name", true),
448                 new JspUtil.ValidAttribute("property", true) };
449
450         private static final JspUtil.ValidAttribute[] setPropertyAttrs = {
451                 new JspUtil.ValidAttribute("name", true),
452                 new JspUtil.ValidAttribute("property", true),
453                 new JspUtil.ValidAttribute("value", false, true),
454                 new JspUtil.ValidAttribute("param") };
455
456         private static final JspUtil.ValidAttribute[] useBeanAttrs = {
457                 new JspUtil.ValidAttribute("id", true),
458                 new JspUtil.ValidAttribute("scope"),
459                 new JspUtil.ValidAttribute("class"),
460                 new JspUtil.ValidAttribute("type"),
461                 new JspUtil.ValidAttribute("beanName", false, true) };
462
463         private static final JspUtil.ValidAttribute[] plugInAttrs = {
464                 new JspUtil.ValidAttribute("type", true),
465                 new JspUtil.ValidAttribute("code", true),
466                 new JspUtil.ValidAttribute("codebase"),
467                 new JspUtil.ValidAttribute("align"),
468                 new JspUtil.ValidAttribute("archive"),
469                 new JspUtil.ValidAttribute("height", false, true),
470                 new JspUtil.ValidAttribute("hspace"),
471                 new JspUtil.ValidAttribute("jreversion"),
472                 new JspUtil.ValidAttribute("name"),
473                 new JspUtil.ValidAttribute("vspace"),
474                 new JspUtil.ValidAttribute("width", false, true),
475                 new JspUtil.ValidAttribute("nspluginurl"),
476                 new JspUtil.ValidAttribute("iepluginurl") };
477
478         private static final JspUtil.ValidAttribute[] attributeAttrs = {
479                 new JspUtil.ValidAttribute("name", true),
480                 new JspUtil.ValidAttribute("trim") };
481
482         private static final JspUtil.ValidAttribute[] invokeAttrs = {
483                 new JspUtil.ValidAttribute("fragment", true),
484                 new JspUtil.ValidAttribute("var"),
485                 new JspUtil.ValidAttribute("varReader"),
486                 new JspUtil.ValidAttribute("scope") };
487
488         private static final JspUtil.ValidAttribute[] doBodyAttrs = {
489                 new JspUtil.ValidAttribute("var"),
490                 new JspUtil.ValidAttribute("varReader"),
491                 new JspUtil.ValidAttribute("scope") };
492
493         private static final JspUtil.ValidAttribute[] jspOutputAttrs = {
494                 new JspUtil.ValidAttribute("omit-xml-declaration"),
495                 new JspUtil.ValidAttribute("doctype-root-element"),
496                 new JspUtil.ValidAttribute("doctype-public"),
497                 new JspUtil.ValidAttribute("doctype-system") };
498
499         /*
500          * Constructor
501          */

502         ValidateVisitor(Compiler JavaDoc compiler) {
503             this.pageInfo = compiler.getPageInfo();
504             this.err = compiler.getErrorDispatcher();
505             this.tagInfo = compiler.getCompilationContext().getTagInfo();
506             this.loader = compiler.getCompilationContext().getClassLoader();
507         }
508
509         public void visit(Node.JspRoot n) throws JasperException {
510             JspUtil.checkAttributes("Jsp:root", n, jspRootAttrs, err);
511             String JavaDoc version = n.getTextAttribute("version");
512             if (!version.equals("1.2") && !version.equals("2.0")) {
513                 err.jspError(n, "jsp.error.jsproot.version.invalid", version);
514             }
515             visitBody(n);
516         }
517
518         public void visit(Node.IncludeDirective n) throws JasperException {
519             JspUtil.checkAttributes("Include directive", n,
520                     includeDirectiveAttrs, err);
521             visitBody(n);
522         }
523
524         public void visit(Node.TaglibDirective n) throws JasperException {
525             JspUtil.checkAttributes("Taglib directive", n,
526                     taglibDirectiveAttrs, err);
527             // Either 'uri' or 'tagdir' attribute must be specified
528
String JavaDoc uri = n.getAttributeValue("uri");
529             String JavaDoc tagdir = n.getAttributeValue("tagdir");
530             if (uri == null && tagdir == null) {
531                 err.jspError(n, "jsp.error.taglibDirective.missing.location");
532             }
533             if (uri != null && tagdir != null) {
534                 err
535                         .jspError(n,
536                                 "jsp.error.taglibDirective.both_uri_and_tagdir");
537             }
538         }
539
540         public void visit(Node.ParamAction n) throws JasperException {
541             JspUtil.checkAttributes("Param action", n, paramActionAttrs, err);
542             // make sure the value of the 'name' attribute is not a
543
// request-time expression
544
throwErrorIfExpression(n, "name", "jsp:param");
545             n.setValue(getJspAttribute(null, "value", null, null, n
546                     .getAttributeValue("value"), java.lang.String JavaDoc.class, n,
547                     false));
548             visitBody(n);
549         }
550
551         public void visit(Node.ParamsAction n) throws JasperException {
552             // Make sure we've got at least one nested jsp:param
553
Node.Nodes subElems = n.getBody();
554             if (subElems == null) {
555                 err.jspError(n, "jsp.error.params.emptyBody");
556             }
557             visitBody(n);
558         }
559
560         public void visit(Node.IncludeAction n) throws JasperException {
561             JspUtil.checkAttributes("Include action", n, includeActionAttrs,
562                     err);
563             n.setPage(getJspAttribute(null, "page", null, null, n
564                     .getAttributeValue("page"), java.lang.String JavaDoc.class, n,
565                     false));
566             visitBody(n);
567         };
568
569         public void visit(Node.ForwardAction n) throws JasperException {
570             JspUtil.checkAttributes("Forward", n, forwardActionAttrs, err);
571             n.setPage(getJspAttribute(null, "page", null, null, n
572                     .getAttributeValue("page"), java.lang.String JavaDoc.class, n,
573                     false));
574             visitBody(n);
575         }
576
577         public void visit(Node.GetProperty n) throws JasperException {
578             JspUtil.checkAttributes("GetProperty", n, getPropertyAttrs, err);
579         }
580
581         public void visit(Node.SetProperty n) throws JasperException {
582             JspUtil.checkAttributes("SetProperty", n, setPropertyAttrs, err);
583             String JavaDoc property = n.getTextAttribute("property");
584             String JavaDoc param = n.getTextAttribute("param");
585             String JavaDoc value = n.getAttributeValue("value");
586
587             n.setValue(getJspAttribute(null, "value", null, null, value,
588                     java.lang.Object JavaDoc.class, n, false));
589
590             boolean valueSpecified = n.getValue() != null;
591
592             if ("*".equals(property)) {
593                 if (param != null || valueSpecified)
594                     err.jspError(n, "jsp.error.setProperty.invalid");
595
596             } else if (param != null && valueSpecified) {
597                 err.jspError(n, "jsp.error.setProperty.invalid");
598             }
599
600             visitBody(n);
601         }
602
603         public void visit(Node.UseBean n) throws JasperException {
604             JspUtil.checkAttributes("UseBean", n, useBeanAttrs, err);
605
606             String JavaDoc name = n.getTextAttribute("id");
607             String JavaDoc scope = n.getTextAttribute("scope");
608             JspUtil.checkScope(scope, n, err);
609             String JavaDoc className = n.getTextAttribute("class");
610             String JavaDoc type = n.getTextAttribute("type");
611             BeanRepository beanInfo = pageInfo.getBeanRepository();
612
613             if (className == null && type == null)
614                 err.jspError(n, "jsp.error.usebean.missingType");
615
616             if (beanInfo.checkVariable(name))
617                 err.jspError(n, "jsp.error.usebean.duplicate");
618
619             if ("session".equals(scope) && !pageInfo.isSession())
620                 err.jspError(n, "jsp.error.usebean.noSession");
621
622             Node.JspAttribute jattr = getJspAttribute(null, "beanName", null,
623                     null, n.getAttributeValue("beanName"),
624                     java.lang.String JavaDoc.class, n, false);
625             n.setBeanName(jattr);
626             if (className != null && jattr != null)
627                 err.jspError(n, "jsp.error.usebean.notBoth");
628
629             if (className == null)
630                 className = type;
631
632             beanInfo.addBean(n, name, className, scope);
633
634             visitBody(n);
635         }
636
637         public void visit(Node.PlugIn n) throws JasperException {
638             JspUtil.checkAttributes("Plugin", n, plugInAttrs, err);
639
640             throwErrorIfExpression(n, "type", "jsp:plugin");
641             throwErrorIfExpression(n, "code", "jsp:plugin");
642             throwErrorIfExpression(n, "codebase", "jsp:plugin");
643             throwErrorIfExpression(n, "align", "jsp:plugin");
644             throwErrorIfExpression(n, "archive", "jsp:plugin");
645             throwErrorIfExpression(n, "hspace", "jsp:plugin");
646             throwErrorIfExpression(n, "jreversion", "jsp:plugin");
647             throwErrorIfExpression(n, "name", "jsp:plugin");
648             throwErrorIfExpression(n, "vspace", "jsp:plugin");
649             throwErrorIfExpression(n, "nspluginurl", "jsp:plugin");
650             throwErrorIfExpression(n, "iepluginurl", "jsp:plugin");
651
652             String JavaDoc type = n.getTextAttribute("type");
653             if (type == null)
654                 err.jspError(n, "jsp.error.plugin.notype");
655             if (!type.equals("bean") && !type.equals("applet"))
656                 err.jspError(n, "jsp.error.plugin.badtype");
657             if (n.getTextAttribute("code") == null)
658                 err.jspError(n, "jsp.error.plugin.nocode");
659
660             Node.JspAttribute width = getJspAttribute(null, "width", null,
661                     null, n.getAttributeValue("width"), java.lang.String JavaDoc.class,
662                     n, false);
663             n.setWidth(width);
664
665             Node.JspAttribute height = getJspAttribute(null, "height", null,
666                     null, n.getAttributeValue("height"),
667                     java.lang.String JavaDoc.class, n, false);
668             n.setHeight(height);
669
670             visitBody(n);
671         }
672
673         public void visit(Node.NamedAttribute n) throws JasperException {
674             JspUtil.checkAttributes("Attribute", n, attributeAttrs, err);
675             visitBody(n);
676         }
677
678         public void visit(Node.JspBody n) throws JasperException {
679             visitBody(n);
680         }
681
682         public void visit(Node.Declaration n) throws JasperException {
683             if (pageInfo.isScriptingInvalid()) {
684                 err.jspError(n.getStart(), "jsp.error.no.scriptlets");
685             }
686         }
687
688         public void visit(Node.Expression n) throws JasperException {
689             if (pageInfo.isScriptingInvalid()) {
690                 err.jspError(n.getStart(), "jsp.error.no.scriptlets");
691             }
692         }
693
694         public void visit(Node.Scriptlet n) throws JasperException {
695             if (pageInfo.isScriptingInvalid()) {
696                 err.jspError(n.getStart(), "jsp.error.no.scriptlets");
697             }
698         }
699
700         public void visit(Node.ELExpression n) throws JasperException {
701             // exit if we are ignoring EL all together
702
if (pageInfo.isELIgnored())
703                 return;
704
705             // JSP.2.2 - '#{' not allowed in template text
706
if (n.getType() == '#') {
707                 if (!pageInfo.isDeferredSyntaxAllowedAsLiteral()
708                         && (tagInfo == null
709                                 || ((tagInfo != null) && !tagInfo.getTagLibrary().getRequiredVersion().equals("2.0")))) {
710                     err.jspError(n, "jsp.error.el.template.deferred");
711                 } else {
712                     return;
713                 }
714             }
715
716             // build expression
717
StringBuffer JavaDoc expr = this.getBuffer();
718             expr.append(n.getType()).append('{').append(n.getText())
719                     .append('}');
720             ELNode.Nodes el = ELParser.parse(expr.toString());
721
722             // validate/prepare expression
723
prepareExpression(el, n, expr.toString());
724
725             // store it
726
n.setEL(el);
727         }
728
729         public void visit(Node.UninterpretedTag n) throws JasperException {
730             if (n.getNamedAttributeNodes().size() != 0) {
731                 err.jspError(n, "jsp.error.namedAttribute.invalidUse");
732             }
733
734             Attributes JavaDoc attrs = n.getAttributes();
735             if (attrs != null) {
736                 int attrSize = attrs.getLength();
737                 Node.JspAttribute[] jspAttrs = new Node.JspAttribute[attrSize];
738                 for (int i = 0; i < attrSize; i++) {
739                     jspAttrs[i] = getJspAttribute(null, attrs.getQName(i),
740                             attrs.getURI(i), attrs.getLocalName(i), attrs
741                                     .getValue(i), java.lang.Object JavaDoc.class, n,
742                             false);
743                 }
744                 n.setJspAttributes(jspAttrs);
745             }
746
747             visitBody(n);
748         }
749
750         public void visit(Node.CustomTag n) throws JasperException {
751
752             TagInfo JavaDoc tagInfo = n.getTagInfo();
753             if (tagInfo == null) {
754                 err.jspError(n, "jsp.error.missing.tagInfo", n.getQName());
755             }
756
757             /*
758              * The bodyconet of a SimpleTag cannot be JSP.
759              */

760             if (n.implementsSimpleTag()
761                     && tagInfo.getBodyContent().equalsIgnoreCase(
762                             TagInfo.BODY_CONTENT_JSP)) {
763                 err.jspError(n, "jsp.error.simpletag.badbodycontent", tagInfo
764                         .getTagClassName());
765             }
766
767             /*
768              * If the tag handler declares in the TLD that it supports dynamic
769              * attributes, it also must implement the DynamicAttributes
770              * interface.
771              */

772             if (tagInfo.hasDynamicAttributes()
773                     && !n.implementsDynamicAttributes()) {
774                 err.jspError(n, "jsp.error.dynamic.attributes.not.implemented",
775                         n.getQName());
776             }
777
778             /*
779              * Make sure all required attributes are present, either as
780              * attributes or named attributes (<jsp:attribute>). Also make sure
781              * that the same attribute is not specified in both attributes or
782              * named attributes.
783              */

784             TagAttributeInfo JavaDoc[] tldAttrs = tagInfo.getAttributes();
785             String JavaDoc customActionUri = n.getURI();
786             Attributes JavaDoc attrs = n.getAttributes();
787             int attrsSize = (attrs == null) ? 0 : attrs.getLength();
788             for (int i = 0; i < tldAttrs.length; i++) {
789                 String JavaDoc attr = null;
790                 if (attrs != null) {
791                     attr = attrs.getValue(tldAttrs[i].getName());
792                     if (attr == null) {
793                         attr = attrs.getValue(customActionUri, tldAttrs[i]
794                                 .getName());
795                     }
796                 }
797                 Node.NamedAttribute na = n.getNamedAttributeNode(tldAttrs[i]
798                         .getName());
799
800                 if (tldAttrs[i].isRequired() && attr == null && na == null) {
801                     err.jspError(n, "jsp.error.missing_attribute", tldAttrs[i]
802                             .getName(), n.getLocalName());
803                 }
804                 if (attr != null && na != null) {
805                     err.jspError(n, "jsp.error.duplicate.name.jspattribute",
806                             tldAttrs[i].getName());
807                 }
808             }
809
810             Node.Nodes naNodes = n.getNamedAttributeNodes();
811             int jspAttrsSize = naNodes.size() + attrsSize;
812             Node.JspAttribute[] jspAttrs = null;
813             if (jspAttrsSize > 0) {
814                 jspAttrs = new Node.JspAttribute[jspAttrsSize];
815             }
816             Hashtable JavaDoc<String JavaDoc, Object JavaDoc> tagDataAttrs = new Hashtable JavaDoc<String JavaDoc, Object JavaDoc>(attrsSize);
817
818             checkXmlAttributes(n, jspAttrs, tagDataAttrs);
819             checkNamedAttributes(n, jspAttrs, attrsSize, tagDataAttrs);
820
821             TagData JavaDoc tagData = new TagData JavaDoc(tagDataAttrs);
822
823             // JSP.C1: It is a (translation time) error for an action that
824
// has one or more variable subelements to have a TagExtraInfo
825
// class that returns a non-null object.
826
TagExtraInfo JavaDoc tei = tagInfo.getTagExtraInfo();
827             if (tei != null && tei.getVariableInfo(tagData) != null
828                     && tei.getVariableInfo(tagData).length > 0
829                     && tagInfo.getTagVariableInfos().length > 0) {
830                 err.jspError("jsp.error.non_null_tei_and_var_subelems", n
831                         .getQName());
832             }
833
834             n.setTagData(tagData);
835             n.setJspAttributes(jspAttrs);
836
837             visitBody(n);
838         }
839
840         public void visit(Node.JspElement n) throws JasperException {
841
842             Attributes JavaDoc attrs = n.getAttributes();
843             if (attrs == null) {
844                 err.jspError(n, "jsp.error.jspelement.missing.name");
845             }
846             int xmlAttrLen = attrs.getLength();
847
848             Node.Nodes namedAttrs = n.getNamedAttributeNodes();
849
850             // XML-style 'name' attribute, which is mandatory, must not be
851
// included in JspAttribute array
852
int jspAttrSize = xmlAttrLen - 1 + namedAttrs.size();
853
854             Node.JspAttribute[] jspAttrs = new Node.JspAttribute[jspAttrSize];
855             int jspAttrIndex = 0;
856
857             // Process XML-style attributes
858
for (int i = 0; i < xmlAttrLen; i++) {
859                 if ("name".equals(attrs.getLocalName(i))) {
860                     n.setNameAttribute(getJspAttribute(null, attrs.getQName(i),
861                             attrs.getURI(i), attrs.getLocalName(i), attrs
862                                     .getValue(i), java.lang.String JavaDoc.class, n,
863                             false));
864                 } else {
865                     if (jspAttrIndex < jspAttrSize) {
866                         jspAttrs[jspAttrIndex++] = getJspAttribute(null, attrs
867                                 .getQName(i), attrs.getURI(i), attrs
868                                 .getLocalName(i), attrs.getValue(i),
869                                 java.lang.Object JavaDoc.class, n, false);
870                     }
871                 }
872             }
873             if (n.getNameAttribute() == null) {
874                 err.jspError(n, "jsp.error.jspelement.missing.name");
875             }
876
877             // Process named attributes
878
for (int i = 0; i < namedAttrs.size(); i++) {
879                 Node.NamedAttribute na = (Node.NamedAttribute) namedAttrs
880                         .getNode(i);
881                 jspAttrs[jspAttrIndex++] = new Node.JspAttribute(na, null,
882                         false);
883             }
884
885             n.setJspAttributes(jspAttrs);
886
887             visitBody(n);
888         }
889
890         public void visit(Node.JspOutput n) throws JasperException {
891             JspUtil.checkAttributes("jsp:output", n, jspOutputAttrs, err);
892
893             if (n.getBody() != null) {
894                 err.jspError(n, "jsp.error.jspoutput.nonemptybody");
895             }
896
897             String JavaDoc omitXmlDecl = n.getAttributeValue("omit-xml-declaration");
898             String JavaDoc doctypeName = n.getAttributeValue("doctype-root-element");
899             String JavaDoc doctypePublic = n.getAttributeValue("doctype-public");
900             String JavaDoc doctypeSystem = n.getAttributeValue("doctype-system");
901
902             String JavaDoc omitXmlDeclOld = pageInfo.getOmitXmlDecl();
903             String JavaDoc doctypeNameOld = pageInfo.getDoctypeName();
904             String JavaDoc doctypePublicOld = pageInfo.getDoctypePublic();
905             String JavaDoc doctypeSystemOld = pageInfo.getDoctypeSystem();
906
907             if (omitXmlDecl != null && omitXmlDeclOld != null
908                     && !omitXmlDecl.equals(omitXmlDeclOld)) {
909                 err.jspError(n, "jsp.error.jspoutput.conflict",
910                         "omit-xml-declaration", omitXmlDeclOld, omitXmlDecl);
911             }
912
913             if (doctypeName != null && doctypeNameOld != null
914                     && !doctypeName.equals(doctypeNameOld)) {
915                 err.jspError(n, "jsp.error.jspoutput.conflict",
916                         "doctype-root-element", doctypeNameOld, doctypeName);
917             }
918
919             if (doctypePublic != null && doctypePublicOld != null
920                     && !doctypePublic.equals(doctypePublicOld)) {
921                 err.jspError(n, "jsp.error.jspoutput.conflict",
922                         "doctype-public", doctypePublicOld, doctypePublic);
923             }
924
925             if (doctypeSystem != null && doctypeSystemOld != null
926                     && !doctypeSystem.equals(doctypeSystemOld)) {
927                 err.jspError(n, "jsp.error.jspoutput.conflict",
928                         "doctype-system", doctypeSystemOld, doctypeSystem);
929             }
930
931             if (doctypeName == null && doctypeSystem != null
932                     || doctypeName != null && doctypeSystem == null) {
933                 err.jspError(n, "jsp.error.jspoutput.doctypenamesystem");
934             }
935
936             if (doctypePublic != null && doctypeSystem == null) {
937                 err.jspError(n, "jsp.error.jspoutput.doctypepulicsystem");
938             }
939
940             if (omitXmlDecl != null) {
941                 pageInfo.setOmitXmlDecl(omitXmlDecl);
942             }
943             if (doctypeName != null) {
944                 pageInfo.setDoctypeName(doctypeName);
945             }
946             if (doctypeSystem != null) {
947                 pageInfo.setDoctypeSystem(doctypeSystem);
948             }
949             if (doctypePublic != null) {
950                 pageInfo.setDoctypePublic(doctypePublic);
951             }
952         }
953
954         public void visit(Node.InvokeAction n) throws JasperException {
955
956             JspUtil.checkAttributes("Invoke", n, invokeAttrs, err);
957
958             String JavaDoc scope = n.getTextAttribute("scope");
959             JspUtil.checkScope(scope, n, err);
960
961             String JavaDoc var = n.getTextAttribute("var");
962             String JavaDoc varReader = n.getTextAttribute("varReader");
963             if (scope != null && var == null && varReader == null) {
964                 err.jspError(n, "jsp.error.missing_var_or_varReader");
965             }
966             if (var != null && varReader != null) {
967                 err.jspError(n, "jsp.error.var_and_varReader");
968             }
969         }
970
971         public void visit(Node.DoBodyAction n) throws JasperException {
972
973             JspUtil.checkAttributes("DoBody", n, doBodyAttrs, err);
974
975             String JavaDoc scope = n.getTextAttribute("scope");
976             JspUtil.checkScope(scope, n, err);
977
978             String JavaDoc var = n.getTextAttribute("var");
979             String JavaDoc varReader = n.getTextAttribute("varReader");
980             if (scope != null && var == null && varReader == null) {
981                 err.jspError(n, "jsp.error.missing_var_or_varReader");
982             }
983             if (var != null && varReader != null) {
984                 err.jspError(n, "jsp.error.var_and_varReader");
985             }
986         }
987
988         /*
989          * Make sure the given custom action does not have any invalid
990          * attributes.
991          *
992          * A custom action and its declared attributes always belong to the same
993          * namespace, which is identified by the prefix name of the custom tag
994          * invocation. For example, in this invocation:
995          *
996          * <my:test a="1" b="2" c="3"/>, the action
997          *
998          * "test" and its attributes "a", "b", and "c" all belong to the
999          * namespace identified by the prefix "my". The above invocation would
1000         * be equivalent to:
1001         *
1002         * <my:test my:a="1" my:b="2" my:c="3"/>
1003         *
1004         * An action attribute may have a prefix different from that of the
1005         * action invocation only if the underlying tag handler supports dynamic
1006         * attributes, in which case the attribute with the different prefix is
1007         * considered a dynamic attribute.
1008         */

1009        private void checkXmlAttributes(Node.CustomTag n,
1010                Node.JspAttribute[] jspAttrs, Hashtable JavaDoc<String JavaDoc, Object JavaDoc> tagDataAttrs)
1011                throws JasperException {
1012
1013            TagInfo JavaDoc tagInfo = n.getTagInfo();
1014            if (tagInfo == null) {
1015                err.jspError(n, "jsp.error.missing.tagInfo", n.getQName());
1016            }
1017            TagAttributeInfo JavaDoc[] tldAttrs = tagInfo.getAttributes();
1018            Attributes JavaDoc attrs = n.getAttributes();
1019
1020            for (int i = 0; attrs != null && i < attrs.getLength(); i++) {
1021                boolean found = false;
1022                for (int j = 0; tldAttrs != null && j < tldAttrs.length; j++) {
1023                    if (attrs.getLocalName(i).equals(tldAttrs[j].getName())
1024                            && (attrs.getURI(i) == null
1025                                    || attrs.getURI(i).length() == 0 || attrs
1026                                    .getURI(i).equals(n.getURI()))) {
1027                        boolean checkDeferred = !tagInfo.getTagLibrary().getRequiredVersion().equals("2.0");
1028                        boolean deferred = false;
1029                        boolean deferredValueIsLiteral = false;
1030                        boolean expression = isExpression(n, attrs.getValue(i), checkDeferred);
1031                        if (checkDeferred && attrs.getValue(i).indexOf("#{") != -1) {
1032                            deferred = true;
1033                            if (pageInfo.isELIgnored()) {
1034                                deferredValueIsLiteral = true;
1035                            }
1036                        }
1037                        
1038                        if (tldAttrs[j].canBeRequestTime()
1039                                || tldAttrs[j].isDeferredMethod() || tldAttrs[j].isDeferredValue()) { // JSP 2.1
1040

1041                            if (!expression) {
1042                                
1043                                if (deferredValueIsLiteral && !pageInfo.isDeferredSyntaxAllowedAsLiteral()) {
1044                                    err.jspError(n, "jsp.error.attribute.custom.non_rt_with_expr",
1045                                            tldAttrs[j].getName());
1046                                }
1047                                
1048                                String JavaDoc expectedType = null;
1049                                if (tldAttrs[j].isDeferredMethod()) {
1050                                    // The String litteral must be castable to what is declared as type
1051
// for the attribute
1052
String JavaDoc m = tldAttrs[j].getMethodSignature();
1053                                    if (m != null) {
1054                                        int rti = m.trim().indexOf(' ');
1055                                        if (rti > 0) {
1056                                            expectedType = m.substring(0, rti).trim();
1057                                        }
1058                                    } else {
1059                                        expectedType = "java.lang.Object";
1060                                    }
1061                                }
1062                                if (tldAttrs[j].isDeferredValue()) {
1063                                    // The String litteral must be castable to what is declared as type
1064
// for the attribute
1065
expectedType = tldAttrs[j].getExpectedTypeName();
1066                                }
1067                                if (expectedType != null) {
1068                                    Class JavaDoc expectedClass = String JavaDoc.class;
1069                                    try {
1070                                        expectedClass = JspUtil.toClass(expectedType, loader);
1071                                    } catch (ClassNotFoundException JavaDoc e) {
1072                                        err.jspError
1073                                            (n, "jsp.error.unknown_attribute_type",
1074                                             tldAttrs[j].getName(), expectedType);
1075                                    }
1076                                    // Check casting
1077
try {
1078                                        ELSupport.coerceToType(attrs.getValue(i), expectedClass);
1079                                    } catch (Exception JavaDoc e) {
1080                                        err.jspError
1081                                            (n, "jsp.error.coerce_to_type",
1082                                             tldAttrs[j].getName(), expectedType, attrs.getValue(i));
1083                                    }
1084                                }
1085
1086                                jspAttrs[i] = new Node.JspAttribute(tldAttrs[j],
1087                                        attrs.getQName(i), attrs.getURI(i), attrs
1088                                                .getLocalName(i),
1089                                        attrs.getValue(i), false, null, false);
1090                            } else {
1091                                
1092                                if (deferred && !tldAttrs[j].isDeferredMethod() && !tldAttrs[j].isDeferredValue()) {
1093                                    // No deferred expressions allowed for this attribute
1094
err.jspError(n, "jsp.error.attribute.custom.non_rt_with_expr",
1095                                            tldAttrs[j].getName());
1096                                }
1097                                if (!deferred && !tldAttrs[j].canBeRequestTime()) {
1098                                    // Only deferred expressions are allowed for this attribute
1099
err.jspError(n, "jsp.error.attribute.custom.non_rt_with_expr",
1100                                            tldAttrs[j].getName());
1101                                }
1102                                
1103                                Class JavaDoc expectedType = String JavaDoc.class;
1104                                try {
1105                                    String JavaDoc typeStr = tldAttrs[j].getTypeName();
1106                                    if (tldAttrs[j].isFragment()) {
1107                                        expectedType = JspFragment JavaDoc.class;
1108                                    } else if (typeStr != null) {
1109                                        expectedType = JspUtil.toClass(typeStr,
1110                                                loader);
1111                                    }
1112                                    jspAttrs[i] = getJspAttribute(tldAttrs[j],
1113                                            attrs.getQName(i), attrs.getURI(i),
1114                                            attrs.getLocalName(i), attrs
1115                                            .getValue(i), expectedType, n,
1116                                            false);
1117                                } catch (ClassNotFoundException JavaDoc e) {
1118                                    err.jspError
1119                                        (n, "jsp.error.unknown_attribute_type",
1120                                         tldAttrs[j].getName(), tldAttrs[j].getTypeName());
1121                                }
1122                            }
1123                            
1124                        } else {
1125                            // Attribute does not accept any expressions.
1126
// Make sure its value does not contain any.
1127
if (expression) {
1128                                err.jspError(n, "jsp.error.attribute.custom.non_rt_with_expr",
1129                                                tldAttrs[j].getName());
1130                            }
1131                            jspAttrs[i] = new Node.JspAttribute(tldAttrs[j],
1132                                    attrs.getQName(i), attrs.getURI(i), attrs
1133                                            .getLocalName(i),
1134                                    attrs.getValue(i), false, null, false);
1135                        }
1136                        if (jspAttrs[i].isExpression()) {
1137                            tagDataAttrs.put(attrs.getQName(i),
1138                                    TagData.REQUEST_TIME_VALUE);
1139                        } else {
1140                            tagDataAttrs.put(attrs.getQName(i), attrs
1141                                    .getValue(i));
1142                        }
1143                        found = true;
1144                        break;
1145                    }
1146                }
1147                if (!found) {
1148                    if (tagInfo.hasDynamicAttributes()) {
1149                        jspAttrs[i] = getJspAttribute(null, attrs.getQName(i),
1150                                attrs.getURI(i), attrs.getLocalName(i), attrs
1151                                        .getValue(i), java.lang.Object JavaDoc.class,
1152                                n, true);
1153                    } else {
1154                        err.jspError(n, "jsp.error.bad_attribute", attrs
1155                                .getQName(i), n.getLocalName());
1156                    }
1157                }
1158            }
1159        }
1160
1161        /*
1162         * Make sure the given custom action does not have any invalid named
1163         * attributes
1164         */

1165        private void checkNamedAttributes(Node.CustomTag n,
1166                Node.JspAttribute[] jspAttrs, int start,
1167                Hashtable JavaDoc<String JavaDoc, Object JavaDoc> tagDataAttrs)
1168                throws JasperException {
1169
1170            TagInfo JavaDoc tagInfo = n.getTagInfo();
1171            if (tagInfo == null) {
1172                err.jspError(n, "jsp.error.missing.tagInfo", n.getQName());
1173            }
1174            TagAttributeInfo JavaDoc[] tldAttrs = tagInfo.getAttributes();
1175            Node.Nodes naNodes = n.getNamedAttributeNodes();
1176
1177            for (int i = 0; i < naNodes.size(); i++) {
1178                Node.NamedAttribute na = (Node.NamedAttribute) naNodes
1179                        .getNode(i);
1180                boolean found = false;
1181                for (int j = 0; j < tldAttrs.length; j++) {
1182                    /*
1183                     * See above comment about namespace matches. For named
1184                     * attributes, we use the prefix instead of URI as the match
1185                     * criterion, because in the case of a JSP document, we'd
1186                     * have to keep track of which namespaces are in scope when
1187                     * parsing a named attribute, in order to determine the URI
1188                     * that the prefix of the named attribute's name matches to.
1189                     */

1190                    String JavaDoc attrPrefix = na.getPrefix();
1191                    if (na.getLocalName().equals(tldAttrs[j].getName())
1192                            && (attrPrefix == null || attrPrefix.length() == 0 || attrPrefix
1193                                    .equals(n.getPrefix()))) {
1194                        jspAttrs[start + i] = new Node.JspAttribute(na,
1195                                tldAttrs[j], false);
1196                        NamedAttributeVisitor nav = null;
1197                        if (na.getBody() != null) {
1198                            nav = new NamedAttributeVisitor();
1199                            na.getBody().visit(nav);
1200                        }
1201                        if (nav != null && nav.hasDynamicContent()) {
1202                            tagDataAttrs.put(na.getName(),
1203                                    TagData.REQUEST_TIME_VALUE);
1204                        } else {
1205                            tagDataAttrs.put(na.getName(), na.getText());
1206                        }
1207                        found = true;
1208                        break;
1209                    }
1210                }
1211                if (!found) {
1212                    if (tagInfo.hasDynamicAttributes()) {
1213                        jspAttrs[start + i] = new Node.JspAttribute(na, null,
1214                                true);
1215                    } else {
1216                        err.jspError(n, "jsp.error.bad_attribute",
1217                                na.getName(), n.getLocalName());
1218                    }
1219                }
1220            }
1221        }
1222
1223        /**
1224         * Preprocess attributes that can be expressions. Expression delimiters
1225         * are stripped.
1226         * <p>
1227         * If value is null, checks if there are any NamedAttribute subelements
1228         * in the tree node, and if so, constructs a JspAttribute out of a child
1229         * NamedAttribute node.
1230         */

1231        private Node.JspAttribute getJspAttribute(TagAttributeInfo JavaDoc tai,
1232                String JavaDoc qName, String JavaDoc uri, String JavaDoc localName, String JavaDoc value,
1233                Class JavaDoc expectedType, Node n, boolean dynamic)
1234                throws JasperException {
1235
1236            Node.JspAttribute result = null;
1237
1238            // XXX Is it an error to see "%=foo%" in non-Xml page?
1239
// (We won't see "<%=foo%> in xml page because '<' is not a
1240
// valid attribute value in xml).
1241

1242            if (value != null) {
1243                if (n.getRoot().isXmlSyntax() && value.startsWith("%=")) {
1244                    result = new Node.JspAttribute(tai, qName, uri, localName,
1245                            value.substring(2, value.length() - 1), true, null,
1246                            dynamic);
1247                } else if (!n.getRoot().isXmlSyntax()
1248                        && value.startsWith("<%=")) {
1249                    result = new Node.JspAttribute(tai, qName, uri, localName,
1250                            value.substring(3, value.length() - 2), true, null,
1251                            dynamic);
1252                } else {
1253                    // The attribute can contain expressions but is not a
1254
// scriptlet expression; thus, we want to run it through
1255
// the expression interpreter
1256

1257                    // validate expression syntax if string contains
1258
// expression(s)
1259
ELNode.Nodes el = ELParser.parse(value);
1260                    
1261                    boolean deferred = false;
1262                    Iterator JavaDoc<ELNode> nodes = el.iterator();
1263                    while (nodes.hasNext()) {
1264                        ELNode node = nodes.next();
1265                        if (node instanceof ELNode.Root) {
1266                            if (((ELNode.Root) node).getType() == '#') {
1267                                deferred = true;
1268                            }
1269                        }
1270                    }
1271
1272                    if (el.containsEL() && !pageInfo.isELIgnored()
1273                            && ((!pageInfo.isDeferredSyntaxAllowedAsLiteral() && deferred)
1274                                    || !deferred)) {
1275
1276                        validateFunctions(el, n);
1277
1278                        result = new Node.JspAttribute(tai, qName, uri,
1279                                localName, value, false, el, dynamic);
1280
1281                        ELContextImpl ctx = new ELContextImpl();
1282                        ctx.setFunctionMapper(getFunctionMapper(el));
1283
1284                        try {
1285                            result.validateEL(this.pageInfo
1286                                    .getExpressionFactory(), ctx);
1287                        } catch (ELException e) {
1288                            this.err.jspError(n.getStart(),
1289                                    "jsp.error.invalid.expression", value, e
1290                                            .toString());
1291                        }
1292
1293                    } else {
1294                        value = value.replace(Constants.ESC, '$');
1295                        result = new Node.JspAttribute(tai, qName, uri,
1296                                localName, value, false, null, dynamic);
1297                    }
1298                }
1299            } else {
1300                // Value is null. Check for any NamedAttribute subnodes
1301
// that might contain the value for this attribute.
1302
// Otherwise, the attribute wasn't found so we return null.
1303

1304                Node.NamedAttribute namedAttributeNode = n
1305                        .getNamedAttributeNode(qName);
1306                if (namedAttributeNode != null) {
1307                    result = new Node.JspAttribute(namedAttributeNode, tai,
1308                            dynamic);
1309                }
1310            }
1311
1312            return result;
1313        }
1314
1315        /*
1316         * Return an empty StringBuffer [not thread-safe]
1317         */

1318        private StringBuffer JavaDoc getBuffer() {
1319            this.buf.setLength(0);
1320            return this.buf;
1321        }
1322
1323        /*
1324         * Checks to see if the given attribute value represents a runtime or EL
1325         * expression.
1326         */

1327        private boolean isExpression(Node n, String JavaDoc value, boolean checkDeferred) {
1328            if ((n.getRoot().isXmlSyntax() && value.startsWith("%="))
1329                    || (!n.getRoot().isXmlSyntax() && value.startsWith("<%="))
1330                    || (value.indexOf("${") != -1 && !pageInfo.isELIgnored())
1331                    || (checkDeferred && value.indexOf("#{") != -1 && !pageInfo.isELIgnored()
1332                            && !pageInfo.isDeferredSyntaxAllowedAsLiteral()))
1333                return true;
1334            else
1335                return false;
1336        }
1337
1338        /*
1339         * Throws exception if the value of the attribute with the given name in
1340         * the given node is given as an RT or EL expression, but the spec
1341         * requires a static value.
1342         */

1343        private void throwErrorIfExpression(Node n, String JavaDoc attrName,
1344                String JavaDoc actionName) throws JasperException {
1345            if (n.getAttributes() != null
1346                    && n.getAttributes().getValue(attrName) != null
1347                    && isExpression(n, n.getAttributes().getValue(attrName), true)) {
1348                err.jspError(n,
1349                        "jsp.error.attribute.standard.non_rt_with_expr",
1350                        attrName, actionName);
1351            }
1352        }
1353
1354        private static class NamedAttributeVisitor extends Node.Visitor {
1355            private boolean hasDynamicContent;
1356
1357            public void doVisit(Node n) throws JasperException {
1358                if (!(n instanceof Node.JspText)
1359                        && !(n instanceof Node.TemplateText)) {
1360                    hasDynamicContent = true;
1361                }
1362                visitBody(n);
1363            }
1364
1365            public boolean hasDynamicContent() {
1366                return hasDynamicContent;
1367            }
1368        }
1369
1370        private String JavaDoc findUri(String JavaDoc prefix, Node n) {
1371
1372            for (Node p = n; p != null; p = p.getParent()) {
1373                Attributes JavaDoc attrs = p.getTaglibAttributes();
1374                if (attrs == null) {
1375                    continue;
1376                }
1377                for (int i = 0; i < attrs.getLength(); i++) {
1378                    String JavaDoc name = attrs.getQName(i);
1379                    int k = name.indexOf(':');
1380                    if (prefix == null && k < 0) {
1381                        // prefix not specified and a default ns found
1382
return attrs.getValue(i);
1383                    }
1384                    if (prefix != null && k >= 0
1385                            && prefix.equals(name.substring(k + 1))) {
1386                        return attrs.getValue(i);
1387                    }
1388                }
1389            }
1390            return null;
1391        }
1392
1393        /**
1394         * Validate functions in EL expressions
1395         */

1396        private void validateFunctions(ELNode.Nodes el, Node n)
1397                throws JasperException {
1398
1399            class FVVisitor extends ELNode.Visitor {
1400
1401                Node n;
1402
1403                FVVisitor(Node n) {
1404                    this.n = n;
1405                }
1406
1407                public void visit(ELNode.Function func) throws JasperException {
1408                    String JavaDoc prefix = func.getPrefix();
1409                    String JavaDoc function = func.getName();
1410                    String JavaDoc uri = null;
1411
1412                    if (n.getRoot().isXmlSyntax()) {
1413                        uri = findUri(prefix, n);
1414                    } else if (prefix != null) {
1415                        uri = pageInfo.getURI(prefix);
1416                    }
1417
1418                    if (uri == null) {
1419                        if (prefix == null) {
1420                            err.jspError(n, "jsp.error.noFunctionPrefix",
1421                                    function);
1422                        } else {
1423                            err
1424                                    .jspError(
1425                                            n,
1426                                            "jsp.error.attribute.invalidPrefix",
1427                                            prefix);
1428                        }
1429                    }
1430                    TagLibraryInfo JavaDoc taglib = pageInfo.getTaglib(uri);
1431                    FunctionInfo JavaDoc funcInfo = null;
1432                    if (taglib != null) {
1433                        funcInfo = taglib.getFunction(function);
1434                    }
1435                    if (funcInfo == null) {
1436                        err.jspError(n, "jsp.error.noFunction", function);
1437                    }
1438                    // Skip TLD function uniqueness check. Done by Schema ?
1439
func.setUri(uri);
1440                    func.setFunctionInfo(funcInfo);
1441                    processSignature(func);
1442                }
1443            }
1444
1445            el.visit(new FVVisitor(n));
1446        }
1447
1448        private void prepareExpression(ELNode.Nodes el, Node n, String JavaDoc expr)
1449                throws JasperException {
1450            validateFunctions(el, n);
1451
1452            // test it out
1453
ELContextImpl ctx = new ELContextImpl();
1454            ctx.setFunctionMapper(this.getFunctionMapper(el));
1455            ExpressionFactory ef = this.pageInfo.getExpressionFactory();
1456            try {
1457                ef.createValueExpression(ctx, expr, Object JavaDoc.class);
1458            } catch (ELException e) {
1459
1460            }
1461        }
1462
1463        private void processSignature(ELNode.Function func)
1464                throws JasperException {
1465            func.setMethodName(getMethod(func));
1466            func.setParameters(getParameters(func));
1467        }
1468
1469        /**
1470         * Get the method name from the signature.
1471         */

1472        private String JavaDoc getMethod(ELNode.Function func) throws JasperException {
1473            FunctionInfo JavaDoc funcInfo = func.getFunctionInfo();
1474            String JavaDoc signature = funcInfo.getFunctionSignature();
1475
1476            int start = signature.indexOf(' ');
1477            if (start < 0) {
1478                err.jspError("jsp.error.tld.fn.invalid.signature", func
1479                        .getPrefix(), func.getName());
1480            }
1481            int end = signature.indexOf('(');
1482            if (end < 0) {
1483                err.jspError(
1484                        "jsp.error.tld.fn.invalid.signature.parenexpected",
1485                        func.getPrefix(), func.getName());
1486            }
1487            return signature.substring(start + 1, end).trim();
1488        }
1489
1490        /**
1491         * Get the parameters types from the function signature.
1492         *
1493         * @return An array of parameter class names
1494         */

1495        private String JavaDoc[] getParameters(ELNode.Function func)
1496                throws JasperException {
1497            FunctionInfo JavaDoc funcInfo = func.getFunctionInfo();
1498            String JavaDoc signature = funcInfo.getFunctionSignature();
1499            ArrayList JavaDoc<String JavaDoc> params = new ArrayList JavaDoc<String JavaDoc>();
1500            // Signature is of the form
1501
// <return-type> S <method-name S? '('
1502
// < <arg-type> ( ',' <arg-type> )* )? ')'
1503
int start = signature.indexOf('(') + 1;
1504            boolean lastArg = false;
1505            while (true) {
1506                int p = signature.indexOf(',', start);
1507                if (p < 0) {
1508                    p = signature.indexOf(')', start);
1509                    if (p < 0) {
1510                        err.jspError("jsp.error.tld.fn.invalid.signature", func
1511                                .getPrefix(), func.getName());
1512                    }
1513                    lastArg = true;
1514                }
1515                String JavaDoc arg = signature.substring(start, p).trim();
1516                if (!"".equals(arg)) {
1517                    params.add(arg);
1518                }
1519                if (lastArg) {
1520                    break;
1521                }
1522                start = p + 1;
1523            }
1524            return (String JavaDoc[]) params.toArray(new String JavaDoc[params.size()]);
1525        }
1526
1527        private FunctionMapper getFunctionMapper(ELNode.Nodes el)
1528                throws JasperException {
1529
1530            class ValidateFunctionMapper extends FunctionMapper {
1531
1532                private HashMap JavaDoc<String JavaDoc, Method JavaDoc> fnmap = new HashMap JavaDoc<String JavaDoc, Method JavaDoc>();
1533
1534                public void mapFunction(String JavaDoc fnQName, Method JavaDoc method) {
1535                    fnmap.put(fnQName, method);
1536                }
1537
1538                public Method JavaDoc resolveFunction(String JavaDoc prefix, String JavaDoc localName) {
1539                    return this.fnmap.get(prefix + ":" + localName);
1540                }
1541            }
1542
1543            class MapperELVisitor extends ELNode.Visitor {
1544                ValidateFunctionMapper fmapper;
1545
1546                MapperELVisitor(ValidateFunctionMapper fmapper) {
1547                    this.fmapper = fmapper;
1548                }
1549
1550                public void visit(ELNode.Function n) throws JasperException {
1551
1552                    Class JavaDoc c = null;
1553                    Method JavaDoc method = null;
1554                    try {
1555                        c = loader.loadClass(n.getFunctionInfo()
1556                                .getFunctionClass());
1557                    } catch (ClassNotFoundException JavaDoc e) {
1558                        err.jspError("jsp.error.function.classnotfound", n
1559                                .getFunctionInfo().getFunctionClass(), n
1560                                .getPrefix()
1561                                + ':' + n.getName(), e.getMessage());
1562                    }
1563                    String JavaDoc paramTypes[] = n.getParameters();
1564                    int size = paramTypes.length;
1565                    Class JavaDoc params[] = new Class JavaDoc[size];
1566                    int i = 0;
1567                    try {
1568                        for (i = 0; i < size; i++) {
1569                            params[i] = JspUtil.toClass(paramTypes[i], loader);
1570                        }
1571                        method = c.getDeclaredMethod(n.getMethodName(), params);
1572                    } catch (ClassNotFoundException JavaDoc e) {
1573                        err.jspError("jsp.error.signature.classnotfound",
1574                                paramTypes[i], n.getPrefix() + ':'
1575                                        + n.getName(), e.getMessage());
1576                    } catch (NoSuchMethodException JavaDoc e) {
1577                        err.jspError("jsp.error.noFunctionMethod", n
1578                                .getMethodName(), n.getName(), c.getName());
1579                    }
1580                    fmapper.mapFunction(n.getPrefix() + ':' + n.getName(),
1581                            method);
1582                }
1583            }
1584
1585            ValidateFunctionMapper fmapper = new ValidateFunctionMapper();
1586            el.visit(new MapperELVisitor(fmapper));
1587            return fmapper;
1588        }
1589    } // End of ValidateVisitor
1590

1591    /**
1592     * A visitor for validating TagExtraInfo classes of all tags
1593     */

1594    static class TagExtraInfoVisitor extends Node.Visitor {
1595
1596        private ErrorDispatcher err;
1597
1598        /*
1599         * Constructor
1600         */

1601        TagExtraInfoVisitor(Compiler JavaDoc compiler) {
1602            this.err = compiler.getErrorDispatcher();
1603        }
1604
1605        public void visit(Node.CustomTag n) throws JasperException {
1606            TagInfo JavaDoc tagInfo = n.getTagInfo();
1607            if (tagInfo == null) {
1608                err.jspError(n, "jsp.error.missing.tagInfo", n.getQName());
1609            }
1610
1611            ValidationMessage JavaDoc[] errors = tagInfo.validate(n.getTagData());
1612            if (errors != null && errors.length != 0) {
1613                StringBuffer JavaDoc errMsg = new StringBuffer JavaDoc();
1614                errMsg.append("<h3>");
1615                errMsg.append(Localizer.getMessage(
1616                        "jsp.error.tei.invalid.attributes", n.getQName()));
1617                errMsg.append("</h3>");
1618                for (int i = 0; i < errors.length; i++) {
1619                    errMsg.append("<p>");
1620                    if (errors[i].getId() != null) {
1621                        errMsg.append(errors[i].getId());
1622                        errMsg.append(": ");
1623                    }
1624                    errMsg.append(errors[i].getMessage());
1625                    errMsg.append("</p>");
1626                }
1627
1628                err.jspError(n, errMsg.toString());
1629            }
1630
1631            visitBody(n);
1632        }
1633    }
1634
1635    public static void validate(Compiler JavaDoc compiler, Node.Nodes page)
1636            throws JasperException {
1637
1638        /*
1639         * Visit the page/tag directives first, as they are global to the page
1640         * and are position independent.
1641         */

1642        page.visit(new DirectiveVisitor(compiler));
1643
1644        // Determine the default output content type
1645
PageInfo pageInfo = compiler.getPageInfo();
1646        String JavaDoc contentType = pageInfo.getContentType();
1647
1648        if (contentType == null || contentType.indexOf("charset=") < 0) {
1649            boolean isXml = page.getRoot().isXmlSyntax();
1650            String JavaDoc defaultType;
1651            if (contentType == null) {
1652                defaultType = isXml ? "text/xml" : "text/html";
1653            } else {
1654                defaultType = contentType;
1655            }
1656
1657            String JavaDoc charset = null;
1658            if (isXml) {
1659                charset = "UTF-8";
1660            } else {
1661                if (!page.getRoot().isDefaultPageEncoding()) {
1662                    charset = page.getRoot().getPageEncoding();
1663                }
1664            }
1665
1666            if (charset != null) {
1667                pageInfo.setContentType(defaultType + ";charset=" + charset);
1668            } else {
1669                pageInfo.setContentType(defaultType);
1670            }
1671        }
1672
1673        /*
1674         * Validate all other nodes. This validation step includes checking a
1675         * custom tag's mandatory and optional attributes against information in
1676         * the TLD (first validation step for custom tags according to
1677         * JSP.10.5).
1678         */

1679        page.visit(new ValidateVisitor(compiler));
1680
1681        /*
1682         * Invoke TagLibraryValidator classes of all imported tags (second
1683         * validation step for custom tags according to JSP.10.5).
1684         */

1685        validateXmlView(new PageDataImpl(page, compiler), compiler);
1686
1687        /*
1688         * Invoke TagExtraInfo method isValid() for all imported tags (third
1689         * validation step for custom tags according to JSP.10.5).
1690         */

1691        page.visit(new TagExtraInfoVisitor(compiler));
1692
1693    }
1694
1695    // *********************************************************************
1696
// Private (utility) methods
1697

1698    /**
1699     * Validate XML view against the TagLibraryValidator classes of all imported
1700     * tag libraries.
1701     */

1702    private static void validateXmlView(PageData JavaDoc xmlView, Compiler JavaDoc compiler)
1703            throws JasperException {
1704
1705        StringBuffer JavaDoc errMsg = null;
1706        ErrorDispatcher errDisp = compiler.getErrorDispatcher();
1707
1708        for (Iterator JavaDoc iter = compiler.getPageInfo().getTaglibs().iterator(); iter
1709                .hasNext();) {
1710
1711            Object JavaDoc o = iter.next();
1712            if (!(o instanceof TagLibraryInfoImpl))
1713                continue;
1714            TagLibraryInfoImpl tli = (TagLibraryInfoImpl) o;
1715
1716            ValidationMessage JavaDoc[] errors = tli.validate(xmlView);
1717            if ((errors != null) && (errors.length != 0)) {
1718                if (errMsg == null) {
1719                    errMsg = new StringBuffer JavaDoc();
1720                }
1721                errMsg.append("<h3>");
1722                errMsg.append(Localizer.getMessage(
1723                        "jsp.error.tlv.invalid.page", tli.getShortName(),
1724                        compiler.getPageInfo().getJspFile()));
1725                errMsg.append("</h3>");
1726                for (int i = 0; i < errors.length; i++) {
1727                    if (errors[i] != null) {
1728                        errMsg.append("<p>");
1729                        errMsg.append(errors[i].getId());
1730                        errMsg.append(": ");
1731                        errMsg.append(errors[i].getMessage());
1732                        errMsg.append("</p>");
1733                    }
1734                }
1735            }
1736        }
1737
1738        if (errMsg != null) {
1739            errDisp.jspError(errMsg.toString());
1740        }
1741    }
1742}
1743
Popular Tags