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