0001: /*
0002: * Copyright 1999,2004 The Apache Software Foundation.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: package org.apache.jasper.compiler;
0018:
0019: import java.lang.reflect.Method;
0020: import java.util.ArrayList;
0021: import java.util.HashMap;
0022: import java.util.Hashtable;
0023: import java.util.Iterator;
0024:
0025: import javax.servlet.jsp.el.FunctionMapper;
0026: import javax.servlet.jsp.tagext.FunctionInfo;
0027: import javax.servlet.jsp.tagext.JspFragment;
0028: import javax.servlet.jsp.tagext.PageData;
0029: import javax.servlet.jsp.tagext.TagAttributeInfo;
0030: import javax.servlet.jsp.tagext.TagData;
0031: import javax.servlet.jsp.tagext.TagExtraInfo;
0032: import javax.servlet.jsp.tagext.TagInfo;
0033: import javax.servlet.jsp.tagext.TagLibraryInfo;
0034: import javax.servlet.jsp.tagext.ValidationMessage;
0035:
0036: import org.apache.jasper.Constants;
0037: import org.apache.jasper.JasperException;
0038: import org.apache.jasper.JspCompilationContext;
0039: import org.xml.sax.Attributes;
0040:
0041: /**
0042: * Performs validation on the page elements. Attributes are checked for
0043: * mandatory presence, entry value validity, and consistency. As a
0044: * side effect, some page global value (such as those from page direcitves)
0045: * are stored, for later use.
0046: *
0047: * @author Kin-man Chung
0048: * @author Jan Luehe
0049: * @author Shawn Bayern
0050: * @author Mark Roth
0051: */
0052: class Validator {
0053:
0054: /**
0055: * A visitor to validate and extract page directive info
0056: */
0057: static class DirectiveVisitor extends Node.Visitor {
0058:
0059: private PageInfo pageInfo;
0060: private ErrorDispatcher err;
0061:
0062: private static final JspUtil.ValidAttribute[] pageDirectiveAttrs = {
0063: new JspUtil.ValidAttribute("language"),
0064: new JspUtil.ValidAttribute("extends"),
0065: new JspUtil.ValidAttribute("import"),
0066: new JspUtil.ValidAttribute("session"),
0067: new JspUtil.ValidAttribute("buffer"),
0068: new JspUtil.ValidAttribute("autoFlush"),
0069: new JspUtil.ValidAttribute("isThreadSafe"),
0070: new JspUtil.ValidAttribute("info"),
0071: new JspUtil.ValidAttribute("errorPage"),
0072: new JspUtil.ValidAttribute("isErrorPage"),
0073: new JspUtil.ValidAttribute("contentType"),
0074: new JspUtil.ValidAttribute("pageEncoding"),
0075: new JspUtil.ValidAttribute("isELIgnored") };
0076:
0077: private boolean pageEncodingSeen = false;
0078:
0079: /*
0080: * Constructor
0081: */
0082: DirectiveVisitor(Compiler compiler) throws JasperException {
0083: this .pageInfo = compiler.getPageInfo();
0084: this .err = compiler.getErrorDispatcher();
0085: JspCompilationContext ctxt = compiler
0086: .getCompilationContext();
0087: }
0088:
0089: public void visit(Node.IncludeDirective n)
0090: throws JasperException {
0091: // Since pageDirectiveSeen flag only applies to the Current page
0092: // save it here and restore it after the file is included.
0093: boolean pageEncodingSeenSave = pageEncodingSeen;
0094: pageEncodingSeen = false;
0095: visitBody(n);
0096: pageEncodingSeen = pageEncodingSeenSave;
0097: }
0098:
0099: public void visit(Node.PageDirective n) throws JasperException {
0100:
0101: JspUtil.checkAttributes("Page directive", n,
0102: pageDirectiveAttrs, err);
0103:
0104: // JSP.2.10.1
0105: Attributes attrs = n.getAttributes();
0106: for (int i = 0; attrs != null && i < attrs.getLength(); i++) {
0107: String attr = attrs.getQName(i);
0108: String value = attrs.getValue(i);
0109:
0110: if ("language".equals(attr)) {
0111: if (pageInfo.getLanguage(false) == null) {
0112: pageInfo.setLanguage(value, n, err, true);
0113: } else if (!pageInfo.getLanguage(false).equals(
0114: value)) {
0115: err.jspError(n,
0116: "jsp.error.page.conflict.language",
0117: pageInfo.getLanguage(false), value);
0118: }
0119: } else if ("extends".equals(attr)) {
0120: if (pageInfo.getExtends(false) == null) {
0121: pageInfo.setExtends(value, n);
0122: } else if (!pageInfo.getExtends(false)
0123: .equals(value)) {
0124: err.jspError(n,
0125: "jsp.error.page.conflict.extends",
0126: pageInfo.getExtends(false), value);
0127: }
0128: } else if ("contentType".equals(attr)) {
0129: if (pageInfo.getContentType() == null) {
0130: pageInfo.setContentType(value);
0131: } else if (!pageInfo.getContentType().equals(value)) {
0132: err.jspError(n,
0133: "jsp.error.page.conflict.contenttype",
0134: pageInfo.getContentType(), value);
0135: }
0136: } else if ("session".equals(attr)) {
0137: if (pageInfo.getSession() == null) {
0138: pageInfo.setSession(value, n, err);
0139: } else if (!pageInfo.getSession().equals(value)) {
0140: err.jspError(n,
0141: "jsp.error.page.conflict.session",
0142: pageInfo.getSession(), value);
0143: }
0144: } else if ("buffer".equals(attr)) {
0145: if (pageInfo.getBufferValue() == null) {
0146: pageInfo.setBufferValue(value, n, err);
0147: } else if (!pageInfo.getBufferValue().equals(value)) {
0148: err.jspError(n,
0149: "jsp.error.page.conflict.buffer",
0150: pageInfo.getBufferValue(), value);
0151: }
0152: } else if ("autoFlush".equals(attr)) {
0153: if (pageInfo.getAutoFlush() == null) {
0154: pageInfo.setAutoFlush(value, n, err);
0155: } else if (!pageInfo.getAutoFlush().equals(value)) {
0156: err.jspError(n,
0157: "jsp.error.page.conflict.autoflush",
0158: pageInfo.getAutoFlush(), value);
0159: }
0160: } else if ("isThreadSafe".equals(attr)) {
0161: if (pageInfo.getIsThreadSafe() == null) {
0162: pageInfo.setIsThreadSafe(value, n, err);
0163: } else if (!pageInfo.getIsThreadSafe()
0164: .equals(value)) {
0165: err.jspError(n,
0166: "jsp.error.page.conflict.isthreadsafe",
0167: pageInfo.getIsThreadSafe(), value);
0168: }
0169: } else if ("isELIgnored".equals(attr)) {
0170: if (pageInfo.getIsELIgnored() == null) {
0171: pageInfo.setIsELIgnored(value, n, err, true);
0172: } else if (!pageInfo.getIsELIgnored().equals(value)) {
0173: err.jspError(n,
0174: "jsp.error.page.conflict.iselignored",
0175: pageInfo.getIsELIgnored(), value);
0176: }
0177: } else if ("isErrorPage".equals(attr)) {
0178: if (pageInfo.getIsErrorPage() == null) {
0179: pageInfo.setIsErrorPage(value, n, err);
0180: } else if (!pageInfo.getIsErrorPage().equals(value)) {
0181: err.jspError(n,
0182: "jsp.error.page.conflict.iserrorpage",
0183: pageInfo.getIsErrorPage(), value);
0184: }
0185: } else if ("errorPage".equals(attr)) {
0186: if (pageInfo.getErrorPage() == null) {
0187: pageInfo.setErrorPage(value);
0188: } else if (!pageInfo.getErrorPage().equals(value)) {
0189: err.jspError(n,
0190: "jsp.error.page.conflict.errorpage",
0191: pageInfo.getErrorPage(), value);
0192: }
0193: } else if ("info".equals(attr)) {
0194: if (pageInfo.getInfo() == null) {
0195: pageInfo.setInfo(value);
0196: } else if (!pageInfo.getInfo().equals(value)) {
0197: err.jspError(n, "jsp.error.page.conflict.info",
0198: pageInfo.getInfo(), value);
0199: }
0200: } else if ("pageEncoding".equals(attr)) {
0201: if (pageEncodingSeen)
0202: err.jspError(n,
0203: "jsp.error.page.multi.pageencoding");
0204: // 'pageEncoding' can occur at most once per file
0205: pageEncodingSeen = true;
0206: /*
0207: * Report any encoding conflict, treating "UTF-16",
0208: * "UTF-16BE", and "UTF-16LE" as identical.
0209: */
0210: comparePageEncodings(value, n);
0211: }
0212: }
0213:
0214: // Check for bad combinations
0215: if (pageInfo.getBuffer() == 0 && !pageInfo.isAutoFlush())
0216: err.jspError(n, "jsp.error.page.badCombo");
0217:
0218: // Attributes for imports for this node have been processed by
0219: // the parsers, just add them to pageInfo.
0220: pageInfo.addImports(n.getImports());
0221: }
0222:
0223: public void visit(Node.TagDirective n) throws JasperException {
0224: // Note: Most of the validation is done in TagFileProcessor
0225: // when it created a TagInfo object from the
0226: // tag file in which the directive appeared.
0227:
0228: // This method does additional processing to collect page info
0229:
0230: Attributes attrs = n.getAttributes();
0231: for (int i = 0; attrs != null && i < attrs.getLength(); i++) {
0232: String attr = attrs.getQName(i);
0233: String value = attrs.getValue(i);
0234:
0235: if ("language".equals(attr)) {
0236: if (pageInfo.getLanguage(false) == null) {
0237: pageInfo.setLanguage(value, n, err, false);
0238: } else if (!pageInfo.getLanguage(false).equals(
0239: value)) {
0240: err.jspError(n,
0241: "jsp.error.tag.conflict.language",
0242: pageInfo.getLanguage(false), value);
0243: }
0244: } else if ("isELIgnored".equals(attr)) {
0245: if (pageInfo.getIsELIgnored() == null) {
0246: pageInfo.setIsELIgnored(value, n, err, false);
0247: } else if (!pageInfo.getIsELIgnored().equals(value)) {
0248: err.jspError(n,
0249: "jsp.error.tag.conflict.iselignored",
0250: pageInfo.getIsELIgnored(), value);
0251: }
0252: } else if ("pageEncoding".equals(attr)) {
0253: if (pageEncodingSeen)
0254: err.jspError(n,
0255: "jsp.error.tag.multi.pageencoding");
0256: pageEncodingSeen = true;
0257: n.getRoot().setPageEncoding(value);
0258: }
0259: }
0260:
0261: // Attributes for imports for this node have been processed by
0262: // the parsers, just add them to pageInfo.
0263: pageInfo.addImports(n.getImports());
0264: }
0265:
0266: public void visit(Node.AttributeDirective n)
0267: throws JasperException {
0268: // Do nothing, since this attribute directive has already been
0269: // validated by TagFileProcessor when it created a TagInfo object
0270: // from the tag file in which the directive appeared
0271: }
0272:
0273: public void visit(Node.VariableDirective n)
0274: throws JasperException {
0275: // Do nothing, since this variable directive has already been
0276: // validated by TagFileProcessor when it created a TagInfo object
0277: // from the tag file in which the directive appeared
0278: }
0279:
0280: /*
0281: * Compares the page encoding specified in the 'pageEncoding'
0282: * attribute of the page directive with the encoding explicitly
0283: * specified in the XML prolog (only for XML syntax) and the encoding
0284: * specified in the JSP config element whose URL pattern matches the
0285: * page, and throws an error in case of a mismatch.
0286: */
0287: private void comparePageEncodings(String pageDirEnc,
0288: Node.PageDirective n) throws JasperException {
0289:
0290: String configEnc = n.getRoot().getJspConfigPageEncoding();
0291:
0292: if (configEnc != null
0293: && !pageDirEnc.equals(configEnc)
0294: && (!pageDirEnc.startsWith("UTF-16") || !configEnc
0295: .startsWith("UTF-16"))) {
0296: err.jspError(n,
0297: "jsp.error.config_pagedir_encoding_mismatch",
0298: configEnc, pageDirEnc);
0299: }
0300:
0301: if (n.getRoot().isXmlSyntax()
0302: && n.getRoot().isEncodingSpecifiedInProlog()) {
0303: String pageEnc = n.getRoot().getPageEncoding();
0304: if (!pageDirEnc.equals(pageEnc)
0305: && (!pageDirEnc.startsWith("UTF-16") || !pageEnc
0306: .startsWith("UTF-16"))) {
0307: err
0308: .jspError(
0309: n,
0310: "jsp.error.prolog_pagedir_encoding_mismatch",
0311: pageEnc, pageDirEnc);
0312: }
0313: }
0314: }
0315: }
0316:
0317: /**
0318: * A visitor for validating nodes other than page directives
0319: */
0320: static class ValidateVisitor extends Node.Visitor {
0321:
0322: private PageInfo pageInfo;
0323: private ErrorDispatcher err;
0324: private TagInfo tagInfo;
0325: private ClassLoader loader;
0326:
0327: private static final JspUtil.ValidAttribute[] jspRootAttrs = {
0328: new JspUtil.ValidAttribute("xsi:schemaLocation"),
0329: new JspUtil.ValidAttribute("version", true) };
0330:
0331: private static final JspUtil.ValidAttribute[] includeDirectiveAttrs = { new JspUtil.ValidAttribute(
0332: "file", true) };
0333:
0334: private static final JspUtil.ValidAttribute[] taglibDirectiveAttrs = {
0335: new JspUtil.ValidAttribute("uri"),
0336: new JspUtil.ValidAttribute("tagdir"),
0337: new JspUtil.ValidAttribute("prefix", true) };
0338:
0339: private static final JspUtil.ValidAttribute[] includeActionAttrs = {
0340: new JspUtil.ValidAttribute("page", true, true),
0341: new JspUtil.ValidAttribute("flush") };
0342:
0343: private static final JspUtil.ValidAttribute[] paramActionAttrs = {
0344: new JspUtil.ValidAttribute("name", true),
0345: new JspUtil.ValidAttribute("value", true, true) };
0346:
0347: private static final JspUtil.ValidAttribute[] forwardActionAttrs = { new JspUtil.ValidAttribute(
0348: "page", true, true) };
0349:
0350: private static final JspUtil.ValidAttribute[] getPropertyAttrs = {
0351: new JspUtil.ValidAttribute("name", true),
0352: new JspUtil.ValidAttribute("property", true) };
0353:
0354: private static final JspUtil.ValidAttribute[] setPropertyAttrs = {
0355: new JspUtil.ValidAttribute("name", true),
0356: new JspUtil.ValidAttribute("property", true),
0357: new JspUtil.ValidAttribute("value", false, true),
0358: new JspUtil.ValidAttribute("param") };
0359:
0360: private static final JspUtil.ValidAttribute[] useBeanAttrs = {
0361: new JspUtil.ValidAttribute("id", true),
0362: new JspUtil.ValidAttribute("scope"),
0363: new JspUtil.ValidAttribute("class"),
0364: new JspUtil.ValidAttribute("type"),
0365: new JspUtil.ValidAttribute("beanName", false, true) };
0366:
0367: private static final JspUtil.ValidAttribute[] plugInAttrs = {
0368: new JspUtil.ValidAttribute("type", true),
0369: new JspUtil.ValidAttribute("code", true),
0370: new JspUtil.ValidAttribute("codebase"),
0371: new JspUtil.ValidAttribute("align"),
0372: new JspUtil.ValidAttribute("archive"),
0373: new JspUtil.ValidAttribute("height", false, true),
0374: new JspUtil.ValidAttribute("hspace"),
0375: new JspUtil.ValidAttribute("jreversion"),
0376: new JspUtil.ValidAttribute("name"),
0377: new JspUtil.ValidAttribute("vspace"),
0378: new JspUtil.ValidAttribute("width", false, true),
0379: new JspUtil.ValidAttribute("nspluginurl"),
0380: new JspUtil.ValidAttribute("iepluginurl") };
0381:
0382: private static final JspUtil.ValidAttribute[] attributeAttrs = {
0383: new JspUtil.ValidAttribute("name", true),
0384: new JspUtil.ValidAttribute("trim") };
0385:
0386: private static final JspUtil.ValidAttribute[] invokeAttrs = {
0387: new JspUtil.ValidAttribute("fragment", true),
0388: new JspUtil.ValidAttribute("var"),
0389: new JspUtil.ValidAttribute("varReader"),
0390: new JspUtil.ValidAttribute("scope") };
0391:
0392: private static final JspUtil.ValidAttribute[] doBodyAttrs = {
0393: new JspUtil.ValidAttribute("var"),
0394: new JspUtil.ValidAttribute("varReader"),
0395: new JspUtil.ValidAttribute("scope") };
0396:
0397: private static final JspUtil.ValidAttribute[] jspOutputAttrs = {
0398: new JspUtil.ValidAttribute("omit-xml-declaration"),
0399: new JspUtil.ValidAttribute("doctype-root-element"),
0400: new JspUtil.ValidAttribute("doctype-public"),
0401: new JspUtil.ValidAttribute("doctype-system") };
0402:
0403: /*
0404: * Constructor
0405: */
0406: ValidateVisitor(Compiler compiler) {
0407: this .pageInfo = compiler.getPageInfo();
0408: this .err = compiler.getErrorDispatcher();
0409: this .tagInfo = compiler.getCompilationContext()
0410: .getTagInfo();
0411: this .loader = compiler.getCompilationContext()
0412: .getClassLoader();
0413: }
0414:
0415: public void visit(Node.JspRoot n) throws JasperException {
0416: JspUtil.checkAttributes("Jsp:root", n, jspRootAttrs, err);
0417: String version = n.getTextAttribute("version");
0418: if (!version.equals("1.2") && !version.equals("2.0")) {
0419: err.jspError(n, "jsp.error.jsproot.version.invalid",
0420: version);
0421: }
0422: visitBody(n);
0423: }
0424:
0425: public void visit(Node.IncludeDirective n)
0426: throws JasperException {
0427: JspUtil.checkAttributes("Include directive", n,
0428: includeDirectiveAttrs, err);
0429: visitBody(n);
0430: }
0431:
0432: public void visit(Node.TaglibDirective n)
0433: throws JasperException {
0434: JspUtil.checkAttributes("Taglib directive", n,
0435: taglibDirectiveAttrs, err);
0436: // Either 'uri' or 'tagdir' attribute must be specified
0437: String uri = n.getAttributeValue("uri");
0438: String tagdir = n.getAttributeValue("tagdir");
0439: if (uri == null && tagdir == null) {
0440: err.jspError(n,
0441: "jsp.error.taglibDirective.missing.location");
0442: }
0443: if (uri != null && tagdir != null) {
0444: err
0445: .jspError(n,
0446: "jsp.error.taglibDirective.both_uri_and_tagdir");
0447: }
0448: }
0449:
0450: public void visit(Node.ParamAction n) throws JasperException {
0451: JspUtil.checkAttributes("Param action", n,
0452: paramActionAttrs, err);
0453: // make sure the value of the 'name' attribute is not a
0454: // request-time expression
0455: throwErrorIfExpression(n, "name", "jsp:param");
0456: n.setValue(getJspAttribute("value", null, null, n
0457: .getAttributeValue("value"),
0458: java.lang.String.class, n, false));
0459: visitBody(n);
0460: }
0461:
0462: public void visit(Node.ParamsAction n) throws JasperException {
0463: // Make sure we've got at least one nested jsp:param
0464: Node.Nodes subElems = n.getBody();
0465: if (subElems == null) {
0466: err.jspError(n, "jsp.error.params.emptyBody");
0467: }
0468: visitBody(n);
0469: }
0470:
0471: public void visit(Node.IncludeAction n) throws JasperException {
0472: JspUtil.checkAttributes("Include action", n,
0473: includeActionAttrs, err);
0474: n.setPage(getJspAttribute("page", null, null, n
0475: .getAttributeValue("page"), java.lang.String.class,
0476: n, false));
0477: visitBody(n);
0478: };
0479:
0480: public void visit(Node.ForwardAction n) throws JasperException {
0481: JspUtil.checkAttributes("Forward", n, forwardActionAttrs,
0482: err);
0483: n.setPage(getJspAttribute("page", null, null, n
0484: .getAttributeValue("page"), java.lang.String.class,
0485: n, false));
0486: visitBody(n);
0487: }
0488:
0489: public void visit(Node.GetProperty n) throws JasperException {
0490: JspUtil.checkAttributes("GetProperty", n, getPropertyAttrs,
0491: err);
0492: }
0493:
0494: public void visit(Node.SetProperty n) throws JasperException {
0495: JspUtil.checkAttributes("SetProperty", n, setPropertyAttrs,
0496: err);
0497: String name = n.getTextAttribute("name");
0498: String property = n.getTextAttribute("property");
0499: String param = n.getTextAttribute("param");
0500: String value = n.getAttributeValue("value");
0501:
0502: n.setValue(getJspAttribute("value", null, null, value,
0503: java.lang.Object.class, n, false));
0504:
0505: boolean valueSpecified = n.getValue() != null;
0506:
0507: if ("*".equals(property)) {
0508: if (param != null || valueSpecified)
0509: err.jspError(n, "jsp.error.setProperty.invalid");
0510:
0511: } else if (param != null && valueSpecified) {
0512: err.jspError(n, "jsp.error.setProperty.invalid");
0513: }
0514:
0515: visitBody(n);
0516: }
0517:
0518: public void visit(Node.UseBean n) throws JasperException {
0519: JspUtil.checkAttributes("UseBean", n, useBeanAttrs, err);
0520:
0521: String name = n.getTextAttribute("id");
0522: String scope = n.getTextAttribute("scope");
0523: JspUtil.checkScope(scope, n, err);
0524: String className = n.getTextAttribute("class");
0525: String type = n.getTextAttribute("type");
0526: BeanRepository beanInfo = pageInfo.getBeanRepository();
0527:
0528: if (className == null && type == null)
0529: err.jspError(n, "jsp.error.useBean.missingType");
0530:
0531: if (beanInfo.checkVariable(name))
0532: err.jspError(n, "jsp.error.useBean.duplicate");
0533:
0534: if ("session".equals(scope) && !pageInfo.isSession())
0535: err.jspError(n, "jsp.error.useBean.noSession");
0536:
0537: Node.JspAttribute jattr = getJspAttribute("beanName", null,
0538: null, n.getAttributeValue("beanName"),
0539: java.lang.String.class, n, false);
0540: n.setBeanName(jattr);
0541: if (className != null && jattr != null)
0542: err.jspError(n, "jsp.error.useBean.notBoth");
0543:
0544: if (className == null)
0545: className = type;
0546:
0547: beanInfo.addBean(n, name, className, scope);
0548:
0549: visitBody(n);
0550: }
0551:
0552: public void visit(Node.PlugIn n) throws JasperException {
0553: JspUtil.checkAttributes("Plugin", n, plugInAttrs, err);
0554:
0555: throwErrorIfExpression(n, "type", "jsp:plugin");
0556: throwErrorIfExpression(n, "code", "jsp:plugin");
0557: throwErrorIfExpression(n, "codebase", "jsp:plugin");
0558: throwErrorIfExpression(n, "align", "jsp:plugin");
0559: throwErrorIfExpression(n, "archive", "jsp:plugin");
0560: throwErrorIfExpression(n, "hspace", "jsp:plugin");
0561: throwErrorIfExpression(n, "jreversion", "jsp:plugin");
0562: throwErrorIfExpression(n, "name", "jsp:plugin");
0563: throwErrorIfExpression(n, "vspace", "jsp:plugin");
0564: throwErrorIfExpression(n, "nspluginurl", "jsp:plugin");
0565: throwErrorIfExpression(n, "iepluginurl", "jsp:plugin");
0566:
0567: String type = n.getTextAttribute("type");
0568: if (type == null)
0569: err.jspError(n, "jsp.error.plugin.notype");
0570: if (!type.equals("bean") && !type.equals("applet"))
0571: err.jspError(n, "jsp.error.plugin.badtype");
0572: if (n.getTextAttribute("code") == null)
0573: err.jspError(n, "jsp.error.plugin.nocode");
0574:
0575: Node.JspAttribute width = getJspAttribute("width", null,
0576: null, n.getAttributeValue("width"),
0577: java.lang.String.class, n, false);
0578: n.setWidth(width);
0579:
0580: Node.JspAttribute height = getJspAttribute("height", null,
0581: null, n.getAttributeValue("height"),
0582: java.lang.String.class, n, false);
0583: n.setHeight(height);
0584:
0585: visitBody(n);
0586: }
0587:
0588: public void visit(Node.NamedAttribute n) throws JasperException {
0589: JspUtil
0590: .checkAttributes("Attribute", n, attributeAttrs,
0591: err);
0592: visitBody(n);
0593: }
0594:
0595: public void visit(Node.JspBody n) throws JasperException {
0596: visitBody(n);
0597: }
0598:
0599: public void visit(Node.Declaration n) throws JasperException {
0600: if (pageInfo.isScriptingInvalid()) {
0601: err.jspError(n.getStart(), "jsp.error.no.scriptlets");
0602: }
0603: }
0604:
0605: public void visit(Node.Expression n) throws JasperException {
0606: if (pageInfo.isScriptingInvalid()) {
0607: err.jspError(n.getStart(), "jsp.error.no.scriptlets");
0608: }
0609: }
0610:
0611: public void visit(Node.Scriptlet n) throws JasperException {
0612: if (pageInfo.isScriptingInvalid()) {
0613: err.jspError(n.getStart(), "jsp.error.no.scriptlets");
0614: }
0615: }
0616:
0617: public void visit(Node.ELExpression n) throws JasperException {
0618: if (!pageInfo.isELIgnored()) {
0619: String expressions = "${" + new String(n.getText())
0620: + "}";
0621: ELNode.Nodes el = ELParser.parse(expressions);
0622: validateFunctions(el, n);
0623: JspUtil.validateExpressions(n.getStart(), expressions,
0624: java.lang.String.class, // XXX - Should template text
0625: // always evaluate to String?
0626: getFunctionMapper(el), err);
0627: n.setEL(el);
0628: }
0629: }
0630:
0631: public void visit(Node.UninterpretedTag n)
0632: throws JasperException {
0633: if (n.getNamedAttributeNodes().size() != 0) {
0634: err.jspError(n, "jsp.error.namedAttribute.invalidUse");
0635: }
0636:
0637: Attributes attrs = n.getAttributes();
0638: if (attrs != null) {
0639: int attrSize = attrs.getLength();
0640: Node.JspAttribute[] jspAttrs = new Node.JspAttribute[attrSize];
0641: for (int i = 0; i < attrSize; i++) {
0642: jspAttrs[i] = getJspAttribute(attrs.getQName(i),
0643: attrs.getURI(i), attrs.getLocalName(i),
0644: attrs.getValue(i), java.lang.Object.class,
0645: n, false);
0646: }
0647: n.setJspAttributes(jspAttrs);
0648: }
0649:
0650: visitBody(n);
0651: }
0652:
0653: public void visit(Node.CustomTag n) throws JasperException {
0654:
0655: TagInfo tagInfo = n.getTagInfo();
0656: if (tagInfo == null) {
0657: err.jspError(n, "jsp.error.missing.tagInfo", n
0658: .getQName());
0659: }
0660:
0661: /*
0662: * The bodyconet of a SimpleTag cannot be JSP.
0663: */
0664: if (n.implements SimpleTag()
0665: && tagInfo.getBodyContent().equalsIgnoreCase(
0666: TagInfo.BODY_CONTENT_JSP)) {
0667: err.jspError(n, "jsp.error.simpletag.badbodycontent",
0668: tagInfo.getTagClassName());
0669: }
0670:
0671: /*
0672: * If the tag handler declares in the TLD that it supports dynamic
0673: * attributes, it also must implement the DynamicAttributes
0674: * interface.
0675: */
0676: if (tagInfo.hasDynamicAttributes()
0677: && !n.implements DynamicAttributes()) {
0678: err.jspError(n,
0679: "jsp.error.dynamic.attributes.not.implemented",
0680: n.getQName());
0681: }
0682:
0683: /*
0684: * Make sure all required attributes are present, either as
0685: * attributes or named attributes (<jsp:attribute>).
0686: * Also make sure that the same attribute is not specified in
0687: * both attributes or named attributes.
0688: */
0689: TagAttributeInfo[] tldAttrs = tagInfo.getAttributes();
0690: String customActionUri = n.getURI();
0691: Attributes attrs = n.getAttributes();
0692: int attrsSize = (attrs == null) ? 0 : attrs.getLength();
0693: for (int i = 0; i < tldAttrs.length; i++) {
0694: String attr = null;
0695: if (attrs != null) {
0696: attr = attrs.getValue(tldAttrs[i].getName());
0697: if (attr == null) {
0698: attr = attrs.getValue(customActionUri,
0699: tldAttrs[i].getName());
0700: }
0701: }
0702: Node.NamedAttribute na = n
0703: .getNamedAttributeNode(tldAttrs[i].getName());
0704:
0705: if (tldAttrs[i].isRequired() && attr == null
0706: && na == null) {
0707: err.jspError(n, "jsp.error.missing_attribute",
0708: tldAttrs[i].getName(), n.getLocalName());
0709: }
0710: if (attr != null && na != null) {
0711: err.jspError(n,
0712: "jsp.error.duplicate.name.jspattribute",
0713: tldAttrs[i].getName());
0714: }
0715: }
0716:
0717: Node.Nodes naNodes = n.getNamedAttributeNodes();
0718: int jspAttrsSize = naNodes.size() + attrsSize;
0719: Node.JspAttribute[] jspAttrs = null;
0720: if (jspAttrsSize > 0) {
0721: jspAttrs = new Node.JspAttribute[jspAttrsSize];
0722: }
0723: Hashtable tagDataAttrs = new Hashtable(attrsSize);
0724:
0725: checkXmlAttributes(n, jspAttrs, tagDataAttrs);
0726: checkNamedAttributes(n, jspAttrs, attrsSize, tagDataAttrs);
0727:
0728: TagData tagData = new TagData(tagDataAttrs);
0729:
0730: // JSP.C1: It is a (translation time) error for an action that
0731: // has one or more variable subelements to have a TagExtraInfo
0732: // class that returns a non-null object.
0733: TagExtraInfo tei = tagInfo.getTagExtraInfo();
0734: if (tei != null && tei.getVariableInfo(tagData) != null
0735: && tei.getVariableInfo(tagData).length > 0
0736: && tagInfo.getTagVariableInfos().length > 0) {
0737: err.jspError("jsp.error.non_null_tei_and_var_subelems",
0738: n.getQName());
0739: }
0740:
0741: n.setTagData(tagData);
0742: n.setJspAttributes(jspAttrs);
0743:
0744: visitBody(n);
0745: }
0746:
0747: public void visit(Node.JspElement n) throws JasperException {
0748:
0749: Attributes attrs = n.getAttributes();
0750: if (attrs == null) {
0751: err.jspError(n, "jsp.error.jspelement.missing.name");
0752: }
0753: int xmlAttrLen = attrs.getLength();
0754:
0755: Node.Nodes namedAttrs = n.getNamedAttributeNodes();
0756:
0757: // XML-style 'name' attribute, which is mandatory, must not be
0758: // included in JspAttribute array
0759: int jspAttrSize = xmlAttrLen - 1 + namedAttrs.size();
0760:
0761: Node.JspAttribute[] jspAttrs = new Node.JspAttribute[jspAttrSize];
0762: int jspAttrIndex = 0;
0763:
0764: // Process XML-style attributes
0765: for (int i = 0; i < xmlAttrLen; i++) {
0766: if ("name".equals(attrs.getLocalName(i))) {
0767: n.setNameAttribute(getJspAttribute(attrs
0768: .getQName(i), attrs.getURI(i), attrs
0769: .getLocalName(i), attrs.getValue(i),
0770: java.lang.String.class, n, false));
0771: } else {
0772: if (jspAttrIndex < jspAttrSize) {
0773: jspAttrs[jspAttrIndex++] = getJspAttribute(
0774: attrs.getQName(i), attrs.getURI(i),
0775: attrs.getLocalName(i), attrs
0776: .getValue(i),
0777: java.lang.Object.class, n, false);
0778: }
0779: }
0780: }
0781: if (n.getNameAttribute() == null) {
0782: err.jspError(n, "jsp.error.jspelement.missing.name");
0783: }
0784:
0785: // Process named attributes
0786: for (int i = 0; i < namedAttrs.size(); i++) {
0787: Node.NamedAttribute na = (Node.NamedAttribute) namedAttrs
0788: .getNode(i);
0789: jspAttrs[jspAttrIndex++] = new Node.JspAttribute(na,
0790: false);
0791: }
0792:
0793: n.setJspAttributes(jspAttrs);
0794:
0795: visitBody(n);
0796: }
0797:
0798: public void visit(Node.JspOutput n) throws JasperException {
0799: JspUtil.checkAttributes("jsp:output", n, jspOutputAttrs,
0800: err);
0801:
0802: if (n.getBody() != null) {
0803: err.jspError(n, "jsp.error.jspoutput.nonemptybody");
0804: }
0805:
0806: String omitXmlDecl = n
0807: .getAttributeValue("omit-xml-declaration");
0808: String doctypeName = n
0809: .getAttributeValue("doctype-root-element");
0810: String doctypePublic = n
0811: .getAttributeValue("doctype-public");
0812: String doctypeSystem = n
0813: .getAttributeValue("doctype-system");
0814:
0815: String omitXmlDeclOld = pageInfo.getOmitXmlDecl();
0816: String doctypeNameOld = pageInfo.getDoctypeName();
0817: String doctypePublicOld = pageInfo.getDoctypePublic();
0818: String doctypeSystemOld = pageInfo.getDoctypeSystem();
0819:
0820: if (omitXmlDecl != null && omitXmlDeclOld != null
0821: && !omitXmlDecl.equals(omitXmlDeclOld)) {
0822: err.jspError(n, "jsp.error.jspoutput.conflict",
0823: "omit-xml-declaration", omitXmlDeclOld,
0824: omitXmlDecl);
0825: }
0826:
0827: if (doctypeName != null && doctypeNameOld != null
0828: && !doctypeName.equals(doctypeNameOld)) {
0829: err.jspError(n, "jsp.error.jspoutput.conflict",
0830: "doctype-root-element", doctypeNameOld,
0831: doctypeName);
0832: }
0833:
0834: if (doctypePublic != null && doctypePublicOld != null
0835: && !doctypePublic.equals(doctypePublicOld)) {
0836: err.jspError(n, "jsp.error.jspoutput.conflict",
0837: "doctype-public", doctypePublicOld,
0838: doctypePublic);
0839: }
0840:
0841: if (doctypeSystem != null && doctypeSystemOld != null
0842: && !doctypeSystem.equals(doctypeSystemOld)) {
0843: err.jspError(n, "jsp.error.jspoutput.conflict",
0844: "doctype-system", doctypeSystemOld,
0845: doctypeSystem);
0846: }
0847:
0848: if (doctypeName == null && doctypeSystem != null
0849: || doctypeName != null && doctypeSystem == null) {
0850: err
0851: .jspError(n,
0852: "jsp.error.jspoutput.doctypenamesystem");
0853: }
0854:
0855: if (doctypePublic != null && doctypeSystem == null) {
0856: err.jspError(n,
0857: "jsp.error.jspoutput.doctypepulicsystem");
0858: }
0859:
0860: if (omitXmlDecl != null) {
0861: pageInfo.setOmitXmlDecl(omitXmlDecl);
0862: }
0863: if (doctypeName != null) {
0864: pageInfo.setDoctypeName(doctypeName);
0865: }
0866: if (doctypeSystem != null) {
0867: pageInfo.setDoctypeSystem(doctypeSystem);
0868: }
0869: if (doctypePublic != null) {
0870: pageInfo.setDoctypePublic(doctypePublic);
0871: }
0872: }
0873:
0874: public void visit(Node.InvokeAction n) throws JasperException {
0875:
0876: JspUtil.checkAttributes("Invoke", n, invokeAttrs, err);
0877:
0878: String scope = n.getTextAttribute("scope");
0879: JspUtil.checkScope(scope, n, err);
0880:
0881: String var = n.getTextAttribute("var");
0882: String varReader = n.getTextAttribute("varReader");
0883: if (scope != null && var == null && varReader == null) {
0884: err.jspError(n, "jsp.error.missing_var_or_varReader");
0885: }
0886: if (var != null && varReader != null) {
0887: err.jspError(n, "jsp.error.var_and_varReader");
0888: }
0889: }
0890:
0891: public void visit(Node.DoBodyAction n) throws JasperException {
0892:
0893: JspUtil.checkAttributes("DoBody", n, doBodyAttrs, err);
0894:
0895: String scope = n.getTextAttribute("scope");
0896: JspUtil.checkScope(scope, n, err);
0897:
0898: String var = n.getTextAttribute("var");
0899: String varReader = n.getTextAttribute("varReader");
0900: if (scope != null && var == null && varReader == null) {
0901: err.jspError(n, "jsp.error.missing_var_or_varReader");
0902: }
0903: if (var != null && varReader != null) {
0904: err.jspError(n, "jsp.error.var_and_varReader");
0905: }
0906: }
0907:
0908: /*
0909: * Make sure the given custom action does not have any invalid
0910: * attributes.
0911: *
0912: * A custom action and its declared attributes always belong to the
0913: * same namespace, which is identified by the prefix name of the
0914: * custom tag invocation. For example, in this invocation:
0915: *
0916: * <my:test a="1" b="2" c="3"/>, the action
0917: *
0918: * "test" and its attributes "a", "b", and "c" all belong to the
0919: * namespace identified by the prefix "my". The above invocation would
0920: * be equivalent to:
0921: *
0922: * <my:test my:a="1" my:b="2" my:c="3"/>
0923: *
0924: * An action attribute may have a prefix different from that of the
0925: * action invocation only if the underlying tag handler supports
0926: * dynamic attributes, in which case the attribute with the different
0927: * prefix is considered a dynamic attribute.
0928: */
0929: private void checkXmlAttributes(Node.CustomTag n,
0930: Node.JspAttribute[] jspAttrs, Hashtable tagDataAttrs)
0931: throws JasperException {
0932:
0933: TagInfo tagInfo = n.getTagInfo();
0934: if (tagInfo == null) {
0935: err.jspError(n, "jsp.error.missing.tagInfo", n
0936: .getQName());
0937: }
0938: TagAttributeInfo[] tldAttrs = tagInfo.getAttributes();
0939: Attributes attrs = n.getAttributes();
0940:
0941: for (int i = 0; attrs != null && i < attrs.getLength(); i++) {
0942: boolean found = false;
0943: for (int j = 0; tldAttrs != null && j < tldAttrs.length; j++) {
0944: if (attrs.getLocalName(i).equals(
0945: tldAttrs[j].getName())
0946: && (attrs.getURI(i) == null
0947: || attrs.getURI(i).length() == 0 || attrs
0948: .getURI(i).equals(n.getURI()))) {
0949: if (tldAttrs[j].canBeRequestTime()) {
0950: Class expectedType = String.class;
0951: try {
0952: String typeStr = tldAttrs[j]
0953: .getTypeName();
0954: if (tldAttrs[j].isFragment()) {
0955: expectedType = JspFragment.class;
0956: } else if (typeStr != null) {
0957: expectedType = JspUtil.toClass(
0958: typeStr, loader);
0959: }
0960: jspAttrs[i] = getJspAttribute(attrs
0961: .getQName(i), attrs.getURI(i),
0962: attrs.getLocalName(i), attrs
0963: .getValue(i),
0964: expectedType, n, false);
0965: } catch (ClassNotFoundException e) {
0966: err
0967: .jspError(
0968: n,
0969: "jsp.error.unknown_attribute_type",
0970: tldAttrs[j].getName(),
0971: tldAttrs[j]
0972: .getTypeName());
0973: }
0974: } else {
0975: // Attribute does not accept any expressions.
0976: // Make sure its value does not contain any.
0977: if (isExpression(n, attrs.getValue(i))) {
0978: err
0979: .jspError(
0980: n,
0981: "jsp.error.attribute.custom.non_rt_with_expr",
0982: tldAttrs[j].getName());
0983: }
0984: jspAttrs[i] = new Node.JspAttribute(attrs
0985: .getQName(i), attrs.getURI(i),
0986: attrs.getLocalName(i), attrs
0987: .getValue(i), false, null,
0988: false);
0989: }
0990: if (jspAttrs[i].isExpression()) {
0991: tagDataAttrs.put(attrs.getQName(i),
0992: TagData.REQUEST_TIME_VALUE);
0993: } else {
0994: tagDataAttrs.put(attrs.getQName(i), attrs
0995: .getValue(i));
0996: }
0997: found = true;
0998: break;
0999: }
1000: }
1001: if (!found) {
1002: if (tagInfo.hasDynamicAttributes()) {
1003: jspAttrs[i] = getJspAttribute(
1004: attrs.getQName(i), attrs.getURI(i),
1005: attrs.getLocalName(i), attrs
1006: .getValue(i),
1007: java.lang.Object.class, n, true);
1008: } else {
1009: err.jspError(n, "jsp.error.bad_attribute",
1010: attrs.getQName(i), n.getLocalName());
1011: }
1012: }
1013: }
1014: }
1015:
1016: /*
1017: * Make sure the given custom action does not have any invalid named
1018: * attributes
1019: */
1020: private void checkNamedAttributes(Node.CustomTag n,
1021: Node.JspAttribute[] jspAttrs, int start,
1022: Hashtable tagDataAttrs) throws JasperException {
1023:
1024: TagInfo tagInfo = n.getTagInfo();
1025: if (tagInfo == null) {
1026: err.jspError(n, "jsp.error.missing.tagInfo", n
1027: .getQName());
1028: }
1029: TagAttributeInfo[] tldAttrs = tagInfo.getAttributes();
1030: Node.Nodes naNodes = n.getNamedAttributeNodes();
1031:
1032: for (int i = 0; i < naNodes.size(); i++) {
1033: Node.NamedAttribute na = (Node.NamedAttribute) naNodes
1034: .getNode(i);
1035: boolean found = false;
1036: for (int j = 0; j < tldAttrs.length; j++) {
1037: /*
1038: * See above comment about namespace matches. For named
1039: * attributes, we use the prefix instead of URI as the
1040: * match criterion, because in the case of a JSP document,
1041: * we'd have to keep track of which namespaces are in scope
1042: * when parsing a named attribute, in order to determine
1043: * the URI that the prefix of the named attribute's name
1044: * matches to.
1045: */
1046: String attrPrefix = na.getPrefix();
1047: if (na.getLocalName().equals(tldAttrs[j].getName())
1048: && (attrPrefix == null
1049: || attrPrefix.length() == 0 || attrPrefix
1050: .equals(n.getPrefix()))) {
1051: jspAttrs[start + i] = new Node.JspAttribute(na,
1052: false);
1053: NamedAttributeVisitor nav = null;
1054: if (na.getBody() != null) {
1055: nav = new NamedAttributeVisitor();
1056: na.getBody().visit(nav);
1057: }
1058: if (nav != null && nav.hasDynamicContent()) {
1059: tagDataAttrs.put(na.getName(),
1060: TagData.REQUEST_TIME_VALUE);
1061: } else {
1062: tagDataAttrs
1063: .put(na.getName(), na.getText());
1064: }
1065: found = true;
1066: break;
1067: }
1068: }
1069: if (!found) {
1070: if (tagInfo.hasDynamicAttributes()) {
1071: jspAttrs[start + i] = new Node.JspAttribute(na,
1072: true);
1073: } else {
1074: err.jspError(n, "jsp.error.bad_attribute", na
1075: .getName(), n.getLocalName());
1076: }
1077: }
1078: }
1079: }
1080:
1081: /**
1082: * Preprocess attributes that can be expressions. Expression
1083: * delimiters are stripped.
1084: * <p>
1085: * If value is null, checks if there are any
1086: * NamedAttribute subelements in the tree node, and if so,
1087: * constructs a JspAttribute out of a child NamedAttribute node.
1088: */
1089: private Node.JspAttribute getJspAttribute(String qName,
1090: String uri, String localName, String value,
1091: Class expectedType, Node n, boolean dynamic)
1092: throws JasperException {
1093:
1094: Node.JspAttribute result = null;
1095:
1096: // XXX Is it an error to see "%=foo%" in non-Xml page?
1097: // (We won't see "<%=foo%> in xml page because '<' is not a
1098: // valid attribute value in xml).
1099:
1100: if (value != null) {
1101: if (n.getRoot().isXmlSyntax() && value.startsWith("%=")) {
1102: result = new Node.JspAttribute(qName, uri,
1103: localName, value.substring(2, value
1104: .length() - 1), true, null, dynamic);
1105: } else if (!n.getRoot().isXmlSyntax()
1106: && value.startsWith("<%=")) {
1107: result = new Node.JspAttribute(qName, uri,
1108: localName, value.substring(3, value
1109: .length() - 2), true, null, dynamic);
1110: } else {
1111: // The attribute can contain expressions but is not a
1112: // scriptlet expression; thus, we want to run it through
1113: // the expression interpreter
1114:
1115: // validate expression syntax if string contains
1116: // expression(s)
1117: ELNode.Nodes el = ELParser.parse(value);
1118: if (el.containsEL() && !pageInfo.isELIgnored()) {
1119: validateFunctions(el, n);
1120: JspUtil.validateExpressions(n.getStart(),
1121: value, expectedType,
1122: getFunctionMapper(el), this .err);
1123:
1124: result = new Node.JspAttribute(qName, uri,
1125: localName, value, false, el, dynamic);
1126: } else {
1127: value = value.replace(Constants.ESC, '$');
1128: result = new Node.JspAttribute(qName, uri,
1129: localName, value, false, null, dynamic);
1130: }
1131: }
1132: } else {
1133: // Value is null. Check for any NamedAttribute subnodes
1134: // that might contain the value for this attribute.
1135: // Otherwise, the attribute wasn't found so we return null.
1136:
1137: Node.NamedAttribute namedAttributeNode = n
1138: .getNamedAttributeNode(qName);
1139: if (namedAttributeNode != null) {
1140: result = new Node.JspAttribute(namedAttributeNode,
1141: dynamic);
1142: }
1143: }
1144:
1145: return result;
1146: }
1147:
1148: /*
1149: * Checks to see if the given attribute value represents a runtime or
1150: * EL expression.
1151: */
1152: private boolean isExpression(Node n, String value) {
1153: if ((n.getRoot().isXmlSyntax() && value.startsWith("%="))
1154: || (!n.getRoot().isXmlSyntax() && value
1155: .startsWith("<%="))
1156: || (value.indexOf("${") != -1 && !pageInfo
1157: .isELIgnored()))
1158: return true;
1159: else
1160: return false;
1161: }
1162:
1163: /*
1164: * Throws exception if the value of the attribute with the given
1165: * name in the given node is given as an RT or EL expression, but the
1166: * spec requires a static value.
1167: */
1168: private void throwErrorIfExpression(Node n, String attrName,
1169: String actionName) throws JasperException {
1170: if (n.getAttributes() != null
1171: && n.getAttributes().getValue(attrName) != null
1172: && isExpression(n, n.getAttributes().getValue(
1173: attrName))) {
1174: err
1175: .jspError(
1176: n,
1177: "jsp.error.attribute.standard.non_rt_with_expr",
1178: attrName, actionName);
1179: }
1180: }
1181:
1182: private static class NamedAttributeVisitor extends Node.Visitor {
1183: private boolean hasDynamicContent;
1184:
1185: public void doVisit(Node n) throws JasperException {
1186: if (!(n instanceof Node.JspText)
1187: && !(n instanceof Node.TemplateText)) {
1188: hasDynamicContent = true;
1189: }
1190: visitBody(n);
1191: }
1192:
1193: public boolean hasDynamicContent() {
1194: return hasDynamicContent;
1195: }
1196: }
1197:
1198: private String findUri(String prefix, Node n) {
1199:
1200: for (Node p = n; p != null; p = p.getParent()) {
1201: Attributes attrs = p.getTaglibAttributes();
1202: if (attrs == null) {
1203: continue;
1204: }
1205: for (int i = 0; i < attrs.getLength(); i++) {
1206: String name = attrs.getQName(i);
1207: int k = name.indexOf(':');
1208: if (prefix == null && k < 0) {
1209: // prefix not specified and a default ns found
1210: return attrs.getValue(i);
1211: }
1212: if (prefix != null && k >= 0
1213: && prefix.equals(name.substring(k + 1))) {
1214: return attrs.getValue(i);
1215: }
1216: }
1217: }
1218: return null;
1219: }
1220:
1221: /**
1222: * Validate functions in EL expressions
1223: */
1224: private void validateFunctions(ELNode.Nodes el, Node n)
1225: throws JasperException {
1226:
1227: class FVVisitor extends ELNode.Visitor {
1228:
1229: Node n;
1230:
1231: FVVisitor(Node n) {
1232: this .n = n;
1233: }
1234:
1235: public void visit(ELNode.Function func)
1236: throws JasperException {
1237: String prefix = func.getPrefix();
1238: String function = func.getName();
1239: String uri = null;
1240:
1241: if (n.getRoot().isXmlSyntax()) {
1242: uri = findUri(prefix, n);
1243: } else if (prefix != null) {
1244: uri = pageInfo.getURI(prefix);
1245: }
1246:
1247: if (uri == null) {
1248: if (prefix == null) {
1249: err.jspError(n,
1250: "jsp.error.noFunctionPrefix",
1251: function);
1252: } else {
1253: err
1254: .jspError(
1255: n,
1256: "jsp.error.attribute.invalidPrefix",
1257: prefix);
1258: }
1259: }
1260: TagLibraryInfo taglib = pageInfo.getTaglib(uri);
1261: FunctionInfo funcInfo = null;
1262: if (taglib != null) {
1263: funcInfo = taglib.getFunction(function);
1264: }
1265: if (funcInfo == null) {
1266: err.jspError(n, "jsp.error.noFunction",
1267: function);
1268: }
1269: // Skip TLD function uniqueness check. Done by Schema ?
1270: func.setUri(uri);
1271: func.setFunctionInfo(funcInfo);
1272: processSignature(func);
1273: }
1274: }
1275:
1276: el.visit(new FVVisitor(n));
1277: }
1278:
1279: private void processSignature(ELNode.Function func)
1280: throws JasperException {
1281: func.setMethodName(getMethod(func));
1282: func.setParameters(getParameters(func));
1283: }
1284:
1285: /**
1286: * Get the method name from the signature.
1287: */
1288: private String getMethod(ELNode.Function func)
1289: throws JasperException {
1290: FunctionInfo funcInfo = func.getFunctionInfo();
1291: String signature = funcInfo.getFunctionSignature();
1292:
1293: int start = signature.indexOf(' ');
1294: if (start < 0) {
1295: err.jspError("jsp.error.tld.fn.invalid.signature", func
1296: .getPrefix(), func.getName());
1297: }
1298: int end = signature.indexOf('(');
1299: if (end < 0) {
1300: err
1301: .jspError(
1302: "jsp.error.tld.fn.invalid.signature.parenexpected",
1303: func.getPrefix(), func.getName());
1304: }
1305: return signature.substring(start + 1, end).trim();
1306: }
1307:
1308: /**
1309: * Get the parameters types from the function signature.
1310: * @return An array of parameter class names
1311: */
1312: private String[] getParameters(ELNode.Function func)
1313: throws JasperException {
1314: FunctionInfo funcInfo = func.getFunctionInfo();
1315: String signature = funcInfo.getFunctionSignature();
1316: ArrayList params = new ArrayList();
1317: // Signature is of the form
1318: // <return-type> S <method-name S? '('
1319: // < <arg-type> ( ',' <arg-type> )* )? ')'
1320: int start = signature.indexOf('(') + 1;
1321: boolean lastArg = false;
1322: while (true) {
1323: int p = signature.indexOf(',', start);
1324: if (p < 0) {
1325: p = signature.indexOf(')', start);
1326: if (p < 0) {
1327: err.jspError(
1328: "jsp.error.tld.fn.invalid.signature",
1329: func.getPrefix(), func.getName());
1330: }
1331: lastArg = true;
1332: }
1333: String arg = signature.substring(start, p).trim();
1334: if (!"".equals(arg)) {
1335: params.add(arg);
1336: }
1337: if (lastArg) {
1338: break;
1339: }
1340: start = p + 1;
1341: }
1342: return (String[]) params.toArray(new String[params.size()]);
1343: }
1344:
1345: private FunctionMapper getFunctionMapper(ELNode.Nodes el)
1346: throws JasperException {
1347:
1348: class ValidateFunctionMapper implements FunctionMapper {
1349:
1350: private HashMap fnmap = new java.util.HashMap();
1351:
1352: public void mapFunction(String fnQName, Method method) {
1353: fnmap.put(fnQName, method);
1354: }
1355:
1356: public Method resolveFunction(String prefix,
1357: String localName) {
1358: return (Method) this .fnmap.get(prefix + ":"
1359: + localName);
1360: }
1361: }
1362:
1363: class MapperELVisitor extends ELNode.Visitor {
1364: ValidateFunctionMapper fmapper;
1365:
1366: MapperELVisitor(ValidateFunctionMapper fmapper) {
1367: this .fmapper = fmapper;
1368: }
1369:
1370: public void visit(ELNode.Function n)
1371: throws JasperException {
1372:
1373: Class c = null;
1374: Method method = null;
1375: try {
1376: c = loader.loadClass(n.getFunctionInfo()
1377: .getFunctionClass());
1378: } catch (ClassNotFoundException e) {
1379: err.jspError(
1380: "jsp.error.function.classnotfound", n
1381: .getFunctionInfo()
1382: .getFunctionClass(), n
1383: .getPrefix()
1384: + ':' + n.getName(), e
1385: .getMessage());
1386: }
1387: String paramTypes[] = n.getParameters();
1388: int size = paramTypes.length;
1389: Class params[] = new Class[size];
1390: int i = 0;
1391: try {
1392: for (i = 0; i < size; i++) {
1393: params[i] = JspUtil.toClass(paramTypes[i],
1394: loader);
1395: }
1396: method = c.getDeclaredMethod(n.getMethodName(),
1397: params);
1398: } catch (ClassNotFoundException e) {
1399: err.jspError(
1400: "jsp.error.signature.classnotfound",
1401: paramTypes[i], n.getPrefix() + ':'
1402: + n.getName(), e.getMessage());
1403: } catch (NoSuchMethodException e) {
1404: err.jspError("jsp.error.noFunctionMethod", n
1405: .getMethodName(), n.getName(), c
1406: .getName());
1407: }
1408: fmapper.mapFunction(n.getPrefix() + ':'
1409: + n.getName(), method);
1410: }
1411: }
1412:
1413: ValidateFunctionMapper fmapper = new ValidateFunctionMapper();
1414: el.visit(new MapperELVisitor(fmapper));
1415: return fmapper;
1416: }
1417: } // End of ValidateVisitor
1418:
1419: /**
1420: * A visitor for validating TagExtraInfo classes of all tags
1421: */
1422: static class TagExtraInfoVisitor extends Node.Visitor {
1423:
1424: private PageInfo pageInfo;
1425: private ErrorDispatcher err;
1426:
1427: /*
1428: * Constructor
1429: */
1430: TagExtraInfoVisitor(Compiler compiler) {
1431: this .pageInfo = compiler.getPageInfo();
1432: this .err = compiler.getErrorDispatcher();
1433: }
1434:
1435: public void visit(Node.CustomTag n) throws JasperException {
1436: TagInfo tagInfo = n.getTagInfo();
1437: if (tagInfo == null) {
1438: err.jspError(n, "jsp.error.missing.tagInfo", n
1439: .getQName());
1440: }
1441:
1442: ValidationMessage[] errors = tagInfo.validate(n
1443: .getTagData());
1444: if (errors != null && errors.length != 0) {
1445: StringBuffer errMsg = new StringBuffer();
1446: errMsg.append("<h3>");
1447: errMsg.append(Localizer.getMessage(
1448: "jsp.error.tei.invalid.attributes", n
1449: .getQName()));
1450: errMsg.append("</h3>");
1451: for (int i = 0; i < errors.length; i++) {
1452: errMsg.append("<p>");
1453: if (errors[i].getId() != null) {
1454: errMsg.append(errors[i].getId());
1455: errMsg.append(": ");
1456: }
1457: errMsg.append(errors[i].getMessage());
1458: errMsg.append("</p>");
1459: }
1460:
1461: err.jspError(n, errMsg.toString());
1462: }
1463:
1464: visitBody(n);
1465: }
1466: }
1467:
1468: public static void validate(Compiler compiler, Node.Nodes page)
1469: throws JasperException {
1470:
1471: /*
1472: * Visit the page/tag directives first, as they are global to the page
1473: * and are position independent.
1474: */
1475: page.visit(new DirectiveVisitor(compiler));
1476:
1477: // Determine the default output content type
1478: PageInfo pageInfo = compiler.getPageInfo();
1479: String contentType = pageInfo.getContentType();
1480:
1481: if (contentType == null || contentType.indexOf("charset=") < 0) {
1482: boolean isXml = page.getRoot().isXmlSyntax();
1483: String defaultType;
1484: if (contentType == null) {
1485: defaultType = isXml ? "text/xml" : "text/html";
1486: } else {
1487: defaultType = contentType;
1488: }
1489:
1490: String charset = null;
1491: if (isXml) {
1492: charset = "UTF-8";
1493: } else {
1494: if (!page.getRoot().isDefaultPageEncoding()) {
1495: charset = page.getRoot().getPageEncoding();
1496: }
1497: }
1498:
1499: if (charset != null) {
1500: pageInfo.setContentType(defaultType + ";charset="
1501: + charset);
1502: } else {
1503: pageInfo.setContentType(defaultType);
1504: }
1505: }
1506:
1507: /*
1508: * Validate all other nodes.
1509: * This validation step includes checking a custom tag's mandatory and
1510: * optional attributes against information in the TLD (first validation
1511: * step for custom tags according to JSP.10.5).
1512: */
1513: page.visit(new ValidateVisitor(compiler));
1514:
1515: /*
1516: * Invoke TagLibraryValidator classes of all imported tags
1517: * (second validation step for custom tags according to JSP.10.5).
1518: */
1519: validateXmlView(new PageDataImpl(page, compiler), compiler);
1520:
1521: /*
1522: * Invoke TagExtraInfo method isValid() for all imported tags
1523: * (third validation step for custom tags according to JSP.10.5).
1524: */
1525: page.visit(new TagExtraInfoVisitor(compiler));
1526:
1527: }
1528:
1529: //*********************************************************************
1530: // Private (utility) methods
1531:
1532: /**
1533: * Validate XML view against the TagLibraryValidator classes of all
1534: * imported tag libraries.
1535: */
1536: private static void validateXmlView(PageData xmlView,
1537: Compiler compiler) throws JasperException {
1538:
1539: StringBuffer errMsg = null;
1540: ErrorDispatcher errDisp = compiler.getErrorDispatcher();
1541:
1542: for (Iterator iter = compiler.getPageInfo().getTaglibs()
1543: .iterator(); iter.hasNext();) {
1544:
1545: Object o = iter.next();
1546: if (!(o instanceof TagLibraryInfoImpl))
1547: continue;
1548: TagLibraryInfoImpl tli = (TagLibraryInfoImpl) o;
1549:
1550: ValidationMessage[] errors = tli.validate(xmlView);
1551: if ((errors != null) && (errors.length != 0)) {
1552: if (errMsg == null) {
1553: errMsg = new StringBuffer();
1554: }
1555: errMsg.append("<h3>");
1556: errMsg.append(Localizer.getMessage(
1557: "jsp.error.tlv.invalid.page", tli
1558: .getShortName()));
1559: errMsg.append("</h3>");
1560: for (int i = 0; i < errors.length; i++) {
1561: if (errors[i] != null) {
1562: errMsg.append("<p>");
1563: errMsg.append(errors[i].getId());
1564: errMsg.append(": ");
1565: errMsg.append(errors[i].getMessage());
1566: errMsg.append("</p>");
1567: }
1568: }
1569: }
1570: }
1571:
1572: if (errMsg != null) {
1573: errDisp.jspError(errMsg.toString());
1574: }
1575: }
1576: }
|