/* | |
* Licensed to the Apache Software Foundation (ASF) under one or more | |
* contributor license agreements. See the NOTICE file distributed with | |
* this work for additional information regarding copyright ownership. | |
* The ASF licenses this file to You under the Apache License, Version 2.0 | |
* (the "License"); you may not use this file except in compliance with | |
* the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package org.apache.struts2.jasper.compiler; | |
import org.apache.struts2.jasper.Constants; | |
import org.apache.struts2.jasper.JasperException; | |
import org.xml.sax.Attributes; | |
import javax.servlet.jsp.el.FunctionMapper; | |
import javax.servlet.jsp.tagext.*; | |
import java.lang.reflect.Method; | |
import java.util.ArrayList; | |
import java.util.HashMap; | |
import java.util.Hashtable; | |
import java.util.Iterator; | |
/** | |
* Performs validation on the page elements. Attributes are checked for | |
* mandatory presence, entry value validity, and consistency. As a | |
* side effect, some page global value (such as those from page direcitves) | |
* are stored, for later use. | |
* | |
* @author Kin-man Chung | |
* @author Jan Luehe | |
* @author Shawn Bayern | |
* @author Mark Roth | |
*/ | |
class Validator { | |
/** | |
* A visitor to validate and extract page directive info | |
*/ | |
static class DirectiveVisitor extends Node.Visitor { | |
private PageInfo pageInfo; | |
private ErrorDispatcher err; | |
private static final JspUtil.ValidAttribute[] pageDirectiveAttrs = { | |
new JspUtil.ValidAttribute("language"), | |
new JspUtil.ValidAttribute("extends"), | |
new JspUtil.ValidAttribute("import"), | |
new JspUtil.ValidAttribute("session"), | |
new JspUtil.ValidAttribute("buffer"), | |
new JspUtil.ValidAttribute("autoFlush"), | |
new JspUtil.ValidAttribute("isThreadSafe"), | |
new JspUtil.ValidAttribute("info"), | |
new JspUtil.ValidAttribute("errorPage"), | |
new JspUtil.ValidAttribute("isErrorPage"), | |
new JspUtil.ValidAttribute("contentType"), | |
new JspUtil.ValidAttribute("pageEncoding"), | |
new JspUtil.ValidAttribute("isELIgnored") | |
}; | |
private boolean pageEncodingSeen = false; | |
/* | |
* Constructor | |
*/ | |
DirectiveVisitor(Compiler compiler) throws JasperException { | |
this.pageInfo = compiler.getPageInfo(); | |
this.err = compiler.getErrorDispatcher(); | |
} | |
public void visit(Node.IncludeDirective n) throws JasperException { | |
// Since pageDirectiveSeen flag only applies to the Current page | |
// save it here and restore it after the file is included. | |
boolean pageEncodingSeenSave = pageEncodingSeen; | |
pageEncodingSeen = false; | |
visitBody(n); | |
pageEncodingSeen = pageEncodingSeenSave; | |
} | |
public void visit(Node.PageDirective n) throws JasperException { | |
JspUtil.checkAttributes("Page directive", n, | |
pageDirectiveAttrs, err); | |
// JSP.2.10.1 | |
Attributes attrs = n.getAttributes(); | |
for (int i = 0; attrs != null && i < attrs.getLength(); i++) { | |
String attr = attrs.getQName(i); | |
String value = attrs.getValue(i); | |
if ("language".equals(attr)) { | |
if (pageInfo.getLanguage(false) == null) { | |
pageInfo.setLanguage(value, n, err, true); | |
} else if (!pageInfo.getLanguage(false).equals(value)) { | |
err.jspError(n, "jsp.error.page.conflict.language", | |
pageInfo.getLanguage(false), value); | |
} | |
} else if ("extends".equals(attr)) { | |
if (pageInfo.getExtends(false) == null) { | |
pageInfo.setExtends(value, n); | |
} else if (!pageInfo.getExtends(false).equals(value)) { | |
err.jspError(n, "jsp.error.page.conflict.extends", | |
pageInfo.getExtends(false), value); | |
} | |
} else if ("contentType".equals(attr)) { | |
if (pageInfo.getContentType() == null) { | |
pageInfo.setContentType(value); | |
} else if (!pageInfo.getContentType().equals(value)) { | |
err.jspError(n, "jsp.error.page.conflict.contenttype", | |
pageInfo.getContentType(), value); | |
} | |
} else if ("session".equals(attr)) { | |
if (pageInfo.getSession() == null) { | |
pageInfo.setSession(value, n, err); | |
} else if (!pageInfo.getSession().equals(value)) { | |
err.jspError(n, "jsp.error.page.conflict.session", | |
pageInfo.getSession(), value); | |
} | |
} else if ("buffer".equals(attr)) { | |
if (pageInfo.getBufferValue() == null) { | |
pageInfo.setBufferValue(value, n, err); | |
} else if (!pageInfo.getBufferValue().equals(value)) { | |
err.jspError(n, "jsp.error.page.conflict.buffer", | |
pageInfo.getBufferValue(), value); | |
} | |
} else if ("autoFlush".equals(attr)) { | |
if (pageInfo.getAutoFlush() == null) { | |
pageInfo.setAutoFlush(value, n, err); | |
} else if (!pageInfo.getAutoFlush().equals(value)) { | |
err.jspError(n, "jsp.error.page.conflict.autoflush", | |
pageInfo.getAutoFlush(), value); | |
} | |
} else if ("isThreadSafe".equals(attr)) { | |
if (pageInfo.getIsThreadSafe() == null) { | |
pageInfo.setIsThreadSafe(value, n, err); | |
} else if (!pageInfo.getIsThreadSafe().equals(value)) { | |
err.jspError(n, "jsp.error.page.conflict.isthreadsafe", | |
pageInfo.getIsThreadSafe(), value); | |
} | |
} else if ("isELIgnored".equals(attr)) { | |
if (pageInfo.getIsELIgnored() == null) { | |
pageInfo.setIsELIgnored(value, n, err, true); | |
} else if (!pageInfo.getIsELIgnored().equals(value)) { | |
err.jspError(n, "jsp.error.page.conflict.iselignored", | |
pageInfo.getIsELIgnored(), value); | |
} | |
} else if ("isErrorPage".equals(attr)) { | |
if (pageInfo.getIsErrorPage() == null) { | |
pageInfo.setIsErrorPage(value, n, err); | |
} else if (!pageInfo.getIsErrorPage().equals(value)) { | |
err.jspError(n, "jsp.error.page.conflict.iserrorpage", | |
pageInfo.getIsErrorPage(), value); | |
} | |
} else if ("errorPage".equals(attr)) { | |
if (pageInfo.getErrorPage() == null) { | |
pageInfo.setErrorPage(value); | |
} else if (!pageInfo.getErrorPage().equals(value)) { | |
err.jspError(n, "jsp.error.page.conflict.errorpage", | |
pageInfo.getErrorPage(), value); | |
} | |
} else if ("info".equals(attr)) { | |
if (pageInfo.getInfo() == null) { | |
pageInfo.setInfo(value); | |
} else if (!pageInfo.getInfo().equals(value)) { | |
err.jspError(n, "jsp.error.page.conflict.info", | |
pageInfo.getInfo(), value); | |
} | |
} else if ("pageEncoding".equals(attr)) { | |
if (pageEncodingSeen) | |
err.jspError(n, "jsp.error.page.multi.pageencoding"); | |
// 'pageEncoding' can occur at most once per file | |
pageEncodingSeen = true; | |
comparePageEncodings(value, n); | |
} | |
} | |
// Check for bad combinations | |
if (pageInfo.getBuffer() == 0 && !pageInfo.isAutoFlush()) | |
err.jspError(n, "jsp.error.page.badCombo"); | |
// Attributes for imports for this node have been processed by | |
// the parsers, just add them to pageInfo. | |
pageInfo.addImports(n.getImports()); | |
} | |
public void visit(Node.TagDirective n) throws JasperException { | |
// Note: Most of the validation is done in TagFileProcessor | |
// when it created a TagInfo object from the | |
// tag file in which the directive appeared. | |
// This method does additional processing to collect page info | |
Attributes attrs = n.getAttributes(); | |
for (int i = 0; attrs != null && i < attrs.getLength(); i++) { | |
String attr = attrs.getQName(i); | |
String value = attrs.getValue(i); | |
if ("language".equals(attr)) { | |
if (pageInfo.getLanguage(false) == null) { | |
pageInfo.setLanguage(value, n, err, false); | |
} else if (!pageInfo.getLanguage(false).equals(value)) { | |
err.jspError(n, "jsp.error.tag.conflict.language", | |
pageInfo.getLanguage(false), value); | |
} | |
} else if ("isELIgnored".equals(attr)) { | |
if (pageInfo.getIsELIgnored() == null) { | |
pageInfo.setIsELIgnored(value, n, err, false); | |
} else if (!pageInfo.getIsELIgnored().equals(value)) { | |
err.jspError(n, "jsp.error.tag.conflict.iselignored", | |
pageInfo.getIsELIgnored(), value); | |
} | |
} else if ("pageEncoding".equals(attr)) { | |
if (pageEncodingSeen) | |
err.jspError(n, "jsp.error.tag.multi.pageencoding"); | |
pageEncodingSeen = true; | |
n.getRoot().setPageEncoding(value); | |
} | |
} | |
// Attributes for imports for this node have been processed by | |
// the parsers, just add them to pageInfo. | |
pageInfo.addImports(n.getImports()); | |
} | |
public void visit(Node.AttributeDirective n) throws JasperException { | |
// Do nothing, since this attribute directive has already been | |
// validated by TagFileProcessor when it created a TagInfo object | |
// from the tag file in which the directive appeared | |
} | |
public void visit(Node.VariableDirective n) throws JasperException { | |
// Do nothing, since this variable directive has already been | |
// validated by TagFileProcessor when it created a TagInfo object | |
// from the tag file in which the directive appeared | |
} | |
/* | |
* Compares page encodings specified in various places, and throws | |
* exception in case of page encoding mismatch. | |
* | |
* @param pageDirEnc The value of the pageEncoding attribute of the | |
* page directive | |
* @param pageDir The page directive node | |
* | |
* @throws JasperException in case of page encoding mismatch | |
*/ | |
private void comparePageEncodings(String pageDirEnc, | |
Node.PageDirective pageDir) | |
throws JasperException { | |
Node.Root root = pageDir.getRoot(); | |
String configEnc = root.getJspConfigPageEncoding(); | |
/* | |
* Compare the 'pageEncoding' attribute of the page directive with | |
* the encoding specified in the JSP config element whose URL | |
* pattern matches this page. | |
* Treat "UTF-16", "UTF-16BE", and "UTF-16LE" as identical. | |
*/ | |
if (configEnc != null && !pageDirEnc.equals(configEnc) | |
&& (!pageDirEnc.startsWith("UTF-16") | |
|| !configEnc.startsWith("UTF-16"))) { | |
err.jspError(pageDir, | |
"jsp.error.config_pagedir_encoding_mismatch", | |
configEnc, pageDirEnc); | |
} | |
/* | |
* Compare the 'pageEncoding' attribute of the page directive with | |
* the encoding specified in the XML prolog (only for XML syntax, | |
* and only if JSP document contains XML prolog with encoding | |
* declaration). | |
* Treat "UTF-16", "UTF-16BE", and "UTF-16LE" as identical. | |
*/ | |
if (root.isXmlSyntax() && root.isEncodingSpecifiedInProlog()) { | |
String pageEnc = root.getPageEncoding(); | |
if (!pageDirEnc.equals(pageEnc) | |
&& (!pageDirEnc.startsWith("UTF-16") | |
|| !pageEnc.startsWith("UTF-16"))) { | |
err.jspError(pageDir, | |
"jsp.error.prolog_pagedir_encoding_mismatch", | |
pageEnc, pageDirEnc); | |
} | |
} | |
} | |
} | |
/** | |
* A visitor for validating nodes other than page directives | |
*/ | |
static class ValidateVisitor extends Node.Visitor { | |
private PageInfo pageInfo; | |
private ErrorDispatcher err; | |
private ClassLoader loader; | |
private static final JspUtil.ValidAttribute[] jspRootAttrs = { | |
new JspUtil.ValidAttribute("xsi:schemaLocation"), | |
new JspUtil.ValidAttribute("version", true) }; | |
private static final JspUtil.ValidAttribute[] includeDirectiveAttrs = { | |
new JspUtil.ValidAttribute("file", true) }; | |
private static final JspUtil.ValidAttribute[] taglibDirectiveAttrs = { | |
new JspUtil.ValidAttribute("uri"), | |
new JspUtil.ValidAttribute("tagdir"), | |
new JspUtil.ValidAttribute("prefix", true) }; | |
private static final JspUtil.ValidAttribute[] includeActionAttrs = { | |
new JspUtil.ValidAttribute("page", true, true), | |
new JspUtil.ValidAttribute("flush") }; | |
private static final JspUtil.ValidAttribute[] paramActionAttrs = { | |
new JspUtil.ValidAttribute("name", true), | |
new JspUtil.ValidAttribute("value", true, true) }; | |
private static final JspUtil.ValidAttribute[] forwardActionAttrs = { | |
new JspUtil.ValidAttribute("page", true, true) }; | |
private static final JspUtil.ValidAttribute[] getPropertyAttrs = { | |
new JspUtil.ValidAttribute("name", true), | |
new JspUtil.ValidAttribute("property", true) }; | |
private static final JspUtil.ValidAttribute[] setPropertyAttrs = { | |
new JspUtil.ValidAttribute("name", true), | |
new JspUtil.ValidAttribute("property", true), | |
new JspUtil.ValidAttribute("value", false, true), | |
new JspUtil.ValidAttribute("param") }; | |
private static final JspUtil.ValidAttribute[] useBeanAttrs = { | |
new JspUtil.ValidAttribute("id", true), | |
new JspUtil.ValidAttribute("scope"), | |
new JspUtil.ValidAttribute("class"), | |
new JspUtil.ValidAttribute("type"), | |
new JspUtil.ValidAttribute("beanName", false, true) }; | |
private static final JspUtil.ValidAttribute[] plugInAttrs = { | |
new JspUtil.ValidAttribute("type",true), | |
new JspUtil.ValidAttribute("code", true), | |
new JspUtil.ValidAttribute("codebase"), | |
new JspUtil.ValidAttribute("align"), | |
new JspUtil.ValidAttribute("archive"), | |
new JspUtil.ValidAttribute("height", false, true), | |
new JspUtil.ValidAttribute("hspace"), | |
new JspUtil.ValidAttribute("jreversion"), | |
new JspUtil.ValidAttribute("name"), | |
new JspUtil.ValidAttribute("vspace"), | |
new JspUtil.ValidAttribute("width", false, true), | |
new JspUtil.ValidAttribute("nspluginurl"), | |
new JspUtil.ValidAttribute("iepluginurl") }; | |
private static final JspUtil.ValidAttribute[] attributeAttrs = { | |
new JspUtil.ValidAttribute("name", true), | |
new JspUtil.ValidAttribute("trim") }; | |
private static final JspUtil.ValidAttribute[] invokeAttrs = { | |
new JspUtil.ValidAttribute("fragment", true), | |
new JspUtil.ValidAttribute("var"), | |
new JspUtil.ValidAttribute("varReader"), | |
new JspUtil.ValidAttribute("scope") }; | |
private static final JspUtil.ValidAttribute[] doBodyAttrs = { | |
new JspUtil.ValidAttribute("var"), | |
new JspUtil.ValidAttribute("varReader"), | |
new JspUtil.ValidAttribute("scope") }; | |
private static final JspUtil.ValidAttribute[] jspOutputAttrs = { | |
new JspUtil.ValidAttribute("omit-xml-declaration"), | |
new JspUtil.ValidAttribute("doctype-root-element"), | |
new JspUtil.ValidAttribute("doctype-public"), | |
new JspUtil.ValidAttribute("doctype-system") }; | |
/* | |
* Constructor | |
*/ | |
ValidateVisitor(Compiler compiler) { | |
this.pageInfo = compiler.getPageInfo(); | |
this.err = compiler.getErrorDispatcher(); | |
this.loader = compiler.getCompilationContext().getClassLoader(); | |
} | |
public void visit(Node.JspRoot n) throws JasperException { | |
JspUtil.checkAttributes("Jsp:root", n, | |
jspRootAttrs, err); | |
String version = n.getTextAttribute("version"); | |
if (!version.equals("1.2") && !version.equals("2.0")) { | |
err.jspError(n, "jsp.error.jsproot.version.invalid", version); | |
} | |
visitBody(n); | |
} | |
public void visit(Node.IncludeDirective n) throws JasperException { | |
JspUtil.checkAttributes("Include directive", n, | |
includeDirectiveAttrs, err); | |
visitBody(n); | |
} | |
public void visit(Node.TaglibDirective n) throws JasperException { | |
JspUtil.checkAttributes("Taglib directive", n, | |
taglibDirectiveAttrs, err); | |
// Either 'uri' or 'tagdir' attribute must be specified | |
String uri = n.getAttributeValue("uri"); | |
String tagdir = n.getAttributeValue("tagdir"); | |
if (uri == null && tagdir == null) { | |
err.jspError(n, "jsp.error.taglibDirective.missing.location"); | |
} | |
if (uri != null && tagdir != null) { | |
err.jspError(n, "jsp.error.taglibDirective.both_uri_and_tagdir"); | |
} | |
} | |
public void visit(Node.ParamAction n) throws JasperException { | |
JspUtil.checkAttributes("Param action", n, | |
paramActionAttrs, err); | |
// make sure the value of the 'name' attribute is not a | |
// request-time expression | |
throwErrorIfExpression(n, "name", "jsp:param"); | |
n.setValue(getJspAttribute("value", null, null, | |
n.getAttributeValue("value"), | |
java.lang.String.class, | |
n, false)); | |
visitBody(n); | |
} | |
public void visit(Node.ParamsAction n) throws JasperException { | |
// Make sure we've got at least one nested jsp:param | |
Node.Nodes subElems = n.getBody(); | |
if (subElems == null) { | |
err.jspError(n, "jsp.error.params.emptyBody"); | |
} | |
visitBody(n); | |
} | |
public void visit(Node.IncludeAction n) throws JasperException { | |
JspUtil.checkAttributes("Include action", n, | |
includeActionAttrs, err); | |
n.setPage(getJspAttribute("page", null, null, | |
n.getAttributeValue("page"), | |
java.lang.String.class, n, false)); | |
visitBody(n); | |
}; | |
public void visit(Node.ForwardAction n) throws JasperException { | |
JspUtil.checkAttributes("Forward", n, | |
forwardActionAttrs, err); | |
n.setPage(getJspAttribute("page", null, null, | |
n.getAttributeValue("page"), | |
java.lang.String.class, n, false)); | |
visitBody(n); | |
} | |
public void visit(Node.GetProperty n) throws JasperException { | |
JspUtil.checkAttributes("GetProperty", n, | |
getPropertyAttrs, err); | |
} | |
public void visit(Node.SetProperty n) throws JasperException { | |
JspUtil.checkAttributes("SetProperty", n, | |
setPropertyAttrs, err); | |
String property = n.getTextAttribute("property"); | |
String param = n.getTextAttribute("param"); | |
String value = n.getAttributeValue("value"); | |
n.setValue(getJspAttribute("value", null, null, value, | |
java.lang.Object.class, n, false)); | |
boolean valueSpecified = n.getValue() != null; | |
if ("*".equals(property)) { | |
if (param != null || valueSpecified) | |
err.jspError(n, "jsp.error.setProperty.invalid"); | |
} else if (param != null && valueSpecified) { | |
err.jspError(n, "jsp.error.setProperty.invalid"); | |
} | |
visitBody(n); | |
} | |
public void visit(Node.UseBean n) throws JasperException { | |
JspUtil.checkAttributes("UseBean", n, | |
useBeanAttrs, err); | |
String name = n.getTextAttribute ("id"); | |
String scope = n.getTextAttribute ("scope"); | |
JspUtil.checkScope(scope, n, err); | |
String className = n.getTextAttribute ("class"); | |
String type = n.getTextAttribute ("type"); | |
BeanRepository beanInfo = pageInfo.getBeanRepository(); | |
if (className == null && type == null) | |
err.jspError(n, "jsp.error.usebean.missingType"); | |
if (beanInfo.checkVariable(name)) | |
err.jspError(n, "jsp.error.usebean.duplicate"); | |
if ("session".equals(scope) && !pageInfo.isSession()) | |
err.jspError(n, "jsp.error.usebean.noSession"); | |
Node.JspAttribute jattr | |
= getJspAttribute("beanName", null, null, | |
n.getAttributeValue("beanName"), | |
java.lang.String.class, n, false); | |
n.setBeanName(jattr); | |
if (className != null && jattr != null) | |
err.jspError(n, "jsp.error.usebean.notBoth"); | |
if (className == null) | |
className = type; | |
beanInfo.addBean(n, name, className, scope); | |
visitBody(n); | |
} | |
public void visit(Node.PlugIn n) throws JasperException { | |
JspUtil.checkAttributes("Plugin", n, plugInAttrs, err); | |
throwErrorIfExpression(n, "type", "jsp:plugin"); | |
throwErrorIfExpression(n, "code", "jsp:plugin"); | |
throwErrorIfExpression(n, "codebase", "jsp:plugin"); | |
throwErrorIfExpression(n, "align", "jsp:plugin"); | |
throwErrorIfExpression(n, "archive", "jsp:plugin"); | |
throwErrorIfExpression(n, "hspace", "jsp:plugin"); | |
throwErrorIfExpression(n, "jreversion", "jsp:plugin"); | |
throwErrorIfExpression(n, "name", "jsp:plugin"); | |
throwErrorIfExpression(n, "vspace", "jsp:plugin"); | |
throwErrorIfExpression(n, "nspluginurl", "jsp:plugin"); | |
throwErrorIfExpression(n, "iepluginurl", "jsp:plugin"); | |
String type = n.getTextAttribute("type"); | |
if (type == null) | |
err.jspError(n, "jsp.error.plugin.notype"); | |
if (!type.equals("bean") && !type.equals("applet")) | |
err.jspError(n, "jsp.error.plugin.badtype"); | |
if (n.getTextAttribute("code") == null) | |
err.jspError(n, "jsp.error.plugin.nocode"); | |
Node.JspAttribute width | |
= getJspAttribute("width", null, null, | |
n.getAttributeValue("width"), | |
java.lang.String.class, n, false); | |
n.setWidth( width ); | |
Node.JspAttribute height | |
= getJspAttribute("height", null, null, | |
n.getAttributeValue("height"), | |
java.lang.String.class, n, false); | |
n.setHeight( height ); | |
visitBody(n); | |
} | |
public void visit(Node.NamedAttribute n) throws JasperException { | |
JspUtil.checkAttributes("Attribute", n, | |
attributeAttrs, err); | |
visitBody(n); | |
} | |
public void visit(Node.JspBody n) throws JasperException { | |
visitBody(n); | |
} | |
public void visit(Node.Declaration n) throws JasperException { | |
if (pageInfo.isScriptingInvalid()) { | |
err.jspError(n.getStart(), "jsp.error.no.scriptlets"); | |
} | |
} | |
public void visit(Node.Expression n) throws JasperException { | |
if (pageInfo.isScriptingInvalid()) { | |
err.jspError(n.getStart(), "jsp.error.no.scriptlets"); | |
} | |
} | |
public void visit(Node.Scriptlet n) throws JasperException { | |
if (pageInfo.isScriptingInvalid()) { | |
err.jspError(n.getStart(), "jsp.error.no.scriptlets"); | |
} | |
} | |
public void visit(Node.ELExpression n) throws JasperException { | |
if ( !pageInfo.isELIgnored() ) { | |
String expressions = "${" + new String(n.getText()) + "}"; | |
ELNode.Nodes el = ELParser.parse(expressions); | |
validateFunctions(el, n); | |
JspUtil.validateExpressions( | |
n.getStart(), | |
expressions, | |
java.lang.String.class, // XXX - Should template text | |
// always evaluate to String? | |
getFunctionMapper(el), | |
err); | |
n.setEL(el); | |
} | |
} | |
public void visit(Node.UninterpretedTag n) throws JasperException { | |
if (n.getNamedAttributeNodes().size() != 0) { | |
err.jspError(n, "jsp.error.namedAttribute.invalidUse"); | |
} | |
Attributes attrs = n.getAttributes(); | |
if (attrs != null) { | |
int attrSize = attrs.getLength(); | |
Node.JspAttribute[] jspAttrs = new Node.JspAttribute[attrSize]; | |
for (int i=0; i < attrSize; i++) { | |
jspAttrs[i] = getJspAttribute(attrs.getQName(i), | |
attrs.getURI(i), | |
attrs.getLocalName(i), | |
attrs.getValue(i), | |
java.lang.Object.class, | |
n, | |
false); | |
} | |
n.setJspAttributes(jspAttrs); | |
} | |
visitBody(n); | |
} | |
public void visit(Node.CustomTag n) throws JasperException { | |
TagInfo tagInfo = n.getTagInfo(); | |
if (tagInfo == null) { | |
err.jspError(n, "jsp.error.missing.tagInfo", n.getQName()); | |
} | |
/* | |
* The bodyconet of a SimpleTag cannot be JSP. | |
*/ | |
if (n.implementsSimpleTag() && | |
tagInfo.getBodyContent().equalsIgnoreCase(TagInfo.BODY_CONTENT_JSP)) { | |
err.jspError(n, "jsp.error.simpletag.badbodycontent", | |
tagInfo.getTagClassName()); | |
} | |
/* | |
* If the tag handler declares in the TLD that it supports dynamic | |
* attributes, it also must implement the DynamicAttributes | |
* interface. | |
*/ | |
if (tagInfo.hasDynamicAttributes() | |
&& !n.implementsDynamicAttributes()) { | |
err.jspError(n, "jsp.error.dynamic.attributes.not.implemented", | |
n.getQName()); | |
} | |
/* | |
* Make sure all required attributes are present, either as | |
* attributes or named attributes (<jsp:attribute>). | |
* Also make sure that the same attribute is not specified in | |
* both attributes or named attributes. | |
*/ | |
TagAttributeInfo[] tldAttrs = tagInfo.getAttributes(); | |
String customActionUri = n.getURI(); | |
Attributes attrs = n.getAttributes(); | |
int attrsSize = (attrs == null) ? 0 : attrs.getLength(); | |
for (int i=0; i<tldAttrs.length; i++) { | |
String attr = null; | |
if (attrs != null) { | |
attr = attrs.getValue(tldAttrs[i].getName()); | |
if (attr == null) { | |
attr = attrs.getValue(customActionUri, | |
tldAttrs[i].getName()); | |
} | |
} | |
Node.NamedAttribute na = | |
n.getNamedAttributeNode(tldAttrs[i].getName()); | |
if (tldAttrs[i].isRequired() && attr == null && na == null) { | |
err.jspError(n, "jsp.error.missing_attribute", | |
tldAttrs[i].getName(), n.getLocalName()); | |
} | |
if (attr != null && na != null) { | |
err.jspError(n, "jsp.error.duplicate.name.jspattribute", | |
tldAttrs[i].getName()); | |
} | |
} | |
Node.Nodes naNodes = n.getNamedAttributeNodes(); | |
int jspAttrsSize = naNodes.size() + attrsSize; | |
Node.JspAttribute[] jspAttrs = null; | |
if (jspAttrsSize > 0) { | |
jspAttrs = new Node.JspAttribute[jspAttrsSize]; | |
} | |
Hashtable tagDataAttrs = new Hashtable(attrsSize); | |
checkXmlAttributes(n, jspAttrs, tagDataAttrs); | |
checkNamedAttributes(n, jspAttrs, attrsSize, tagDataAttrs); | |
TagData tagData = new TagData(tagDataAttrs); | |
// JSP.C1: It is a (translation time) error for an action that | |
// has one or more variable subelements to have a TagExtraInfo | |
// class that returns a non-null object. | |
TagExtraInfo tei = tagInfo.getTagExtraInfo(); | |
if (tei != null | |
&& tei.getVariableInfo(tagData) != null | |
&& tei.getVariableInfo(tagData).length > 0 | |
&& tagInfo.getTagVariableInfos().length > 0) { | |
err.jspError("jsp.error.non_null_tei_and_var_subelems", | |
n.getQName()); | |
} | |
n.setTagData(tagData); | |
n.setJspAttributes(jspAttrs); | |
visitBody(n); | |
} | |
public void visit(Node.JspElement n) throws JasperException { | |
Attributes attrs = n.getAttributes(); | |
if (attrs == null) { | |
err.jspError(n, "jsp.error.jspelement.missing.name"); | |
} | |
int xmlAttrLen = attrs.getLength(); | |
Node.Nodes namedAttrs = n.getNamedAttributeNodes(); | |
// XML-style 'name' attribute, which is mandatory, must not be | |
// included in JspAttribute array | |
int jspAttrSize = xmlAttrLen-1 + namedAttrs.size(); | |
Node.JspAttribute[] jspAttrs = new Node.JspAttribute[jspAttrSize]; | |
int jspAttrIndex = 0; | |
// Process XML-style attributes | |
for (int i=0; i<xmlAttrLen; i++) { | |
if ("name".equals(attrs.getLocalName(i))) { | |
n.setNameAttribute(getJspAttribute(attrs.getQName(i), | |
attrs.getURI(i), | |
attrs.getLocalName(i), | |
attrs.getValue(i), | |
java.lang.String.class, | |
n, | |
false)); | |
} else { | |
if (jspAttrIndex<jspAttrSize) { | |
jspAttrs[jspAttrIndex++] | |
= getJspAttribute(attrs.getQName(i), | |
attrs.getURI(i), | |
attrs.getLocalName(i), | |
attrs.getValue(i), | |
java.lang.Object.class, | |
n, | |
false); | |
} | |
} | |
} | |
if (n.getNameAttribute() == null) { | |
err.jspError(n, "jsp.error.jspelement.missing.name"); | |
} | |
// Process named attributes | |
for (int i=0; i<namedAttrs.size(); i++) { | |
Node.NamedAttribute na = | |
(Node.NamedAttribute) namedAttrs.getNode(i); | |
jspAttrs[jspAttrIndex++] = new Node.JspAttribute(na, false); | |
} | |
n.setJspAttributes(jspAttrs); | |
visitBody(n); | |
} | |
public void visit(Node.JspOutput n) throws JasperException { | |
JspUtil.checkAttributes("jsp:output", n, jspOutputAttrs, err); | |
if (n.getBody() != null) { | |
err.jspError(n, "jsp.error.jspoutput.nonemptybody"); | |
} | |
String omitXmlDecl = n.getAttributeValue("omit-xml-declaration"); | |
String doctypeName = n.getAttributeValue("doctype-root-element"); | |
String doctypePublic = n.getAttributeValue("doctype-public"); | |
String doctypeSystem = n.getAttributeValue("doctype-system"); | |
String omitXmlDeclOld = pageInfo.getOmitXmlDecl(); | |
String doctypeNameOld = pageInfo.getDoctypeName(); | |
String doctypePublicOld = pageInfo.getDoctypePublic(); | |
String doctypeSystemOld = pageInfo.getDoctypeSystem(); | |
if (omitXmlDecl != null && omitXmlDeclOld != null && | |
!omitXmlDecl.equals(omitXmlDeclOld) ) { | |
err.jspError(n, "jsp.error.jspoutput.conflict", | |
"omit-xml-declaration", omitXmlDeclOld, omitXmlDecl); | |
} | |
if (doctypeName != null && doctypeNameOld != null && | |
!doctypeName.equals(doctypeNameOld) ) { | |
err.jspError(n, "jsp.error.jspoutput.conflict", | |
"doctype-root-element", doctypeNameOld, doctypeName); | |
} | |
if (doctypePublic != null && doctypePublicOld != null && | |
!doctypePublic.equals(doctypePublicOld) ) { | |
err.jspError(n, "jsp.error.jspoutput.conflict", | |
"doctype-public", doctypePublicOld, doctypePublic); | |
} | |
if (doctypeSystem != null && doctypeSystemOld != null && | |
!doctypeSystem.equals(doctypeSystemOld) ) { | |
err.jspError(n, "jsp.error.jspoutput.conflict", | |
"doctype-system", doctypeSystemOld, doctypeSystem); | |
} | |
if (doctypeName == null && doctypeSystem != null || | |
doctypeName != null && doctypeSystem == null) { | |
err.jspError(n, "jsp.error.jspoutput.doctypenamesystem"); | |
} | |
if (doctypePublic != null && doctypeSystem == null) { | |
err.jspError(n, "jsp.error.jspoutput.doctypepulicsystem"); | |
} | |
if (omitXmlDecl != null) { | |
pageInfo.setOmitXmlDecl(omitXmlDecl); | |
} | |
if (doctypeName != null) { | |
pageInfo.setDoctypeName(doctypeName); | |
} | |
if (doctypeSystem != null) { | |
pageInfo.setDoctypeSystem(doctypeSystem); | |
} | |
if (doctypePublic != null) { | |
pageInfo.setDoctypePublic(doctypePublic); | |
} | |
} | |
public void visit(Node.InvokeAction n) throws JasperException { | |
JspUtil.checkAttributes("Invoke", n, invokeAttrs, err); | |
String scope = n.getTextAttribute ("scope"); | |
JspUtil.checkScope(scope, n, err); | |
String var = n.getTextAttribute("var"); | |
String varReader = n.getTextAttribute("varReader"); | |
if (scope != null && var == null && varReader == null) { | |
err.jspError(n, "jsp.error.missing_var_or_varReader"); | |
} | |
if (var != null && varReader != null) { | |
err.jspError(n, "jsp.error.var_and_varReader"); | |
} | |
} | |
public void visit(Node.DoBodyAction n) throws JasperException { | |
JspUtil.checkAttributes("DoBody", n, doBodyAttrs, err); | |
String scope = n.getTextAttribute ("scope"); | |
JspUtil.checkScope(scope, n, err); | |
String var = n.getTextAttribute("var"); | |
String varReader = n.getTextAttribute("varReader"); | |
if (scope != null && var == null && varReader == null) { | |
err.jspError(n, "jsp.error.missing_var_or_varReader"); | |
} | |
if (var != null && varReader != null) { | |
err.jspError(n, "jsp.error.var_and_varReader"); | |
} | |
} | |
/* | |
* Make sure the given custom action does not have any invalid | |
* attributes. | |
* | |
* A custom action and its declared attributes always belong to the | |
* same namespace, which is identified by the prefix name of the | |
* custom tag invocation. For example, in this invocation: | |
* | |
* <my:test a="1" b="2" c="3"/>, the action | |
* | |
* "test" and its attributes "a", "b", and "c" all belong to the | |
* namespace identified by the prefix "my". The above invocation would | |
* be equivalent to: | |
* | |
* <my:test my:a="1" my:b="2" my:c="3"/> | |
* | |
* An action attribute may have a prefix different from that of the | |
* action invocation only if the underlying tag handler supports | |
* dynamic attributes, in which case the attribute with the different | |
* prefix is considered a dynamic attribute. | |
*/ | |
private void checkXmlAttributes(Node.CustomTag n, | |
Node.JspAttribute[] jspAttrs, | |
Hashtable tagDataAttrs) | |
throws JasperException { | |
TagInfo tagInfo = n.getTagInfo(); | |
if (tagInfo == null) { | |
err.jspError(n, "jsp.error.missing.tagInfo", n.getQName()); | |
} | |
TagAttributeInfo[] tldAttrs = tagInfo.getAttributes(); | |
Attributes attrs = n.getAttributes(); | |
for (int i=0; attrs != null && i<attrs.getLength(); i++) { | |
boolean found = false; | |
for (int j=0; tldAttrs != null && j<tldAttrs.length; j++) { | |
if (attrs.getLocalName(i).equals(tldAttrs[j].getName()) | |
&& (attrs.getURI(i) == null | |
|| attrs.getURI(i).length() == 0 | |
|| attrs.getURI(i).equals(n.getURI()))) { | |
if (tldAttrs[j].canBeRequestTime()) { | |
Class expectedType = String.class; | |
try { | |
String typeStr = tldAttrs[j].getTypeName(); | |
if( tldAttrs[j].isFragment() ) { | |
expectedType = JspFragment.class; | |
} | |
else if( typeStr != null ) { | |
expectedType = JspUtil.toClass(typeStr, | |
loader); | |
} | |
jspAttrs[i] | |
= getJspAttribute(attrs.getQName(i), | |
attrs.getURI(i), | |
attrs.getLocalName(i), | |
attrs.getValue(i), | |
expectedType, | |
n, | |
false); | |
} catch (ClassNotFoundException e) { | |
err.jspError(n, | |
"jsp.error.unknown_attribute_type", | |
tldAttrs[j].getName(), | |
tldAttrs[j].getTypeName() ); | |
} | |
} else { | |
// Attribute does not accept any expressions. | |
// Make sure its value does not contain any. | |
if (isExpression(n, attrs.getValue(i))) { | |
err.jspError(n, | |
"jsp.error.attribute.custom.non_rt_with_expr", | |
tldAttrs[j].getName()); | |
} | |
jspAttrs[i] | |
= new Node.JspAttribute(attrs.getQName(i), | |
attrs.getURI(i), | |
attrs.getLocalName(i), | |
attrs.getValue(i), | |
false, | |
null, | |
false); | |
} | |
if (isExpression(n, attrs.getValue(i))) { | |
tagDataAttrs.put(attrs.getQName(i), | |
TagData.REQUEST_TIME_VALUE); | |
} else { | |
tagDataAttrs.put(attrs.getQName(i), | |
attrs.getValue(i)); | |
} | |
found = true; | |
break; | |
} | |
} | |
if (!found) { | |
if (tagInfo.hasDynamicAttributes()) { | |
jspAttrs[i] = getJspAttribute(attrs.getQName(i), | |
attrs.getURI(i), | |
attrs.getLocalName(i), | |
attrs.getValue(i), | |
java.lang.Object.class, | |
n, | |
true); | |
} else { | |
err.jspError(n, "jsp.error.bad_attribute", | |
attrs.getQName(i), n.getLocalName()); | |
} | |
} | |
} | |
} | |
/* | |
* Make sure the given custom action does not have any invalid named | |
* attributes | |
*/ | |
private void checkNamedAttributes(Node.CustomTag n, | |
Node.JspAttribute[] jspAttrs, | |
int start, | |
Hashtable tagDataAttrs) | |
throws JasperException { | |
TagInfo tagInfo = n.getTagInfo(); | |
if (tagInfo == null) { | |
err.jspError(n, "jsp.error.missing.tagInfo", n.getQName()); | |
} | |
TagAttributeInfo[] tldAttrs = tagInfo.getAttributes(); | |
Node.Nodes naNodes = n.getNamedAttributeNodes(); | |
for (int i=0; i<naNodes.size(); i++) { | |
Node.NamedAttribute na = (Node.NamedAttribute) | |
naNodes.getNode(i); | |
boolean found = false; | |
for (int j=0; j<tldAttrs.length; j++) { | |
/* | |
* See above comment about namespace matches. For named | |
* attributes, we use the prefix instead of URI as the | |
* match criterion, because in the case of a JSP document, | |
* we'd have to keep track of which namespaces are in scope | |
* when parsing a named attribute, in order to determine | |
* the URI that the prefix of the named attribute's name | |
* matches to. | |
*/ | |
String attrPrefix = na.getPrefix(); | |
if (na.getLocalName().equals(tldAttrs[j].getName()) | |
&& (attrPrefix == null || attrPrefix.length() == 0 | |
|| attrPrefix.equals(n.getPrefix()))) { | |
jspAttrs[start + i] = new Node.JspAttribute(na, false); | |
NamedAttributeVisitor nav = null; | |
if (na.getBody() != null) { | |
nav = new NamedAttributeVisitor(); | |
na.getBody().visit(nav); | |
} | |
if (nav != null && nav.hasDynamicContent()) { | |
tagDataAttrs.put(na.getName(), | |
TagData.REQUEST_TIME_VALUE); | |
} else { | |
tagDataAttrs.put(na.getName(), na.getText()); | |
} | |
found = true; | |
break; | |
} | |
} | |
if (!found) { | |
if (tagInfo.hasDynamicAttributes()) { | |
jspAttrs[start + i] = new Node.JspAttribute(na, true); | |
} else { | |
err.jspError(n, "jsp.error.bad_attribute", | |
na.getName(), n.getLocalName()); | |
} | |
} | |
} | |
} | |
/** | |
* Preprocess attributes that can be expressions. Expression | |
* delimiters are stripped. | |
* <p> | |
* If value is null, checks if there are any | |
* NamedAttribute subelements in the tree node, and if so, | |
* constructs a JspAttribute out of a child NamedAttribute node. | |
*/ | |
private Node.JspAttribute getJspAttribute(String qName, | |
String uri, | |
String localName, | |
String value, | |
Class expectedType, | |
Node n, | |
boolean dynamic) | |
throws JasperException { | |
Node.JspAttribute result = null; | |
// XXX Is it an error to see "%=foo%" in non-Xml page? | |
// (We won't see "<%=foo%> in xml page because '<' is not a | |
// valid attribute value in xml). | |
if (value != null) { | |
if (n.getRoot().isXmlSyntax() && value.startsWith("%=")) { | |
result = new Node.JspAttribute( | |
qName, | |
uri, | |
localName, | |
value.substring(2, value.length()-1), | |
true, | |
null, | |
dynamic); | |
} | |
else if(!n.getRoot().isXmlSyntax() && value.startsWith("<%=")) { | |
result = new Node.JspAttribute( | |
qName, | |
uri, | |
localName, | |
value.substring(3, value.length()-2), | |
true, | |
null, | |
dynamic); | |
} | |
else { | |
// The attribute can contain expressions but is not a | |
// scriptlet expression; thus, we want to run it through | |
// the expression interpreter | |
// validate expression syntax if string contains | |
// expression(s) | |
ELNode.Nodes el = ELParser.parse(value); | |
if (el.containsEL() && !pageInfo.isELIgnored()) { | |
validateFunctions(el, n); | |
JspUtil.validateExpressions( | |
n.getStart(), | |
value, | |
expectedType, | |
getFunctionMapper(el), | |
this.err); | |
result = new Node.JspAttribute(qName, uri, localName, | |
value, false, el, | |
dynamic); | |
} else { | |
value = value.replace(Constants.HACK_CHAR, '$'); | |
result = new Node.JspAttribute(qName, uri, localName, | |
value, false, null, | |
dynamic); | |
} | |
} | |
} | |
else { | |
// Value is null. Check for any NamedAttribute subnodes | |
// that might contain the value for this attribute. | |
// Otherwise, the attribute wasn't found so we return null. | |
Node.NamedAttribute namedAttributeNode = | |
n.getNamedAttributeNode( qName ); | |
if( namedAttributeNode != null ) { | |
result = new Node.JspAttribute(namedAttributeNode, | |
dynamic); | |
} | |
} | |
return result; | |
} | |
/* | |
* Checks to see if the given attribute value represents a runtime or | |
* EL expression. | |
*/ | |
private boolean isExpression(Node n, String value) { | |
if ((n.getRoot().isXmlSyntax() && value.startsWith("%=")) | |
|| (!n.getRoot().isXmlSyntax() && value.startsWith("<%=")) | |
|| (value.indexOf("${") != -1 && !pageInfo.isELIgnored())) | |
return true; | |
else | |
return false; | |
} | |
/* | |
* Throws exception if the value of the attribute with the given | |
* name in the given node is given as an RT or EL expression, but the | |
* spec requires a static value. | |
*/ | |
private void throwErrorIfExpression(Node n, String attrName, | |
String actionName) | |
throws JasperException { | |
if (n.getAttributes() != null | |
&& n.getAttributes().getValue(attrName) != null | |
&& isExpression(n, n.getAttributes().getValue(attrName))) { | |
err.jspError(n, | |
"jsp.error.attribute.standard.non_rt_with_expr", | |
attrName, actionName); | |
} | |
} | |
private static class NamedAttributeVisitor extends Node.Visitor { | |
private boolean hasDynamicContent; | |
public void doVisit(Node n) throws JasperException { | |
if (!(n instanceof Node.JspText) | |
&& !(n instanceof Node.TemplateText)) { | |
hasDynamicContent = true; | |
} | |
visitBody(n); | |
} | |
public boolean hasDynamicContent() { | |
return hasDynamicContent; | |
} | |
} | |
private String findUri(String prefix, Node n) { | |
for (Node p = n; p != null; p = p.getParent()) { | |
Attributes attrs = p.getTaglibAttributes(); | |
if (attrs == null) { | |
continue; | |
} | |
for (int i = 0; i < attrs.getLength(); i++) { | |
String name = attrs.getQName(i); | |
int k = name.indexOf(':'); | |
if (prefix == null && k < 0) { | |
// prefix not specified and a default ns found | |
return attrs.getValue(i); | |
} | |
if (prefix != null && k >= 0 && | |
prefix.equals(name.substring(k+1))) { | |
return attrs.getValue(i); | |
} | |
} | |
} | |
return null; | |
} | |
/** | |
* Validate functions in EL expressions | |
*/ | |
private void validateFunctions(ELNode.Nodes el, Node n) | |
throws JasperException { | |
class FVVisitor extends ELNode.Visitor { | |
Node n; | |
FVVisitor(Node n) { | |
this.n = n; | |
} | |
public void visit(ELNode.Function func) throws JasperException { | |
String prefix = func.getPrefix(); | |
String function = func.getName(); | |
String uri = null; | |
if (n.getRoot().isXmlSyntax()) { | |
uri = findUri(prefix, n); | |
} else if (prefix != null) { | |
uri = pageInfo.getURI(prefix); | |
} | |
if (uri == null) { | |
if (prefix == null) { | |
err.jspError(n, "jsp.error.noFunctionPrefix", | |
function); | |
} | |
else { | |
err.jspError(n, | |
"jsp.error.attribute.invalidPrefix", prefix); | |
} | |
} | |
TagLibraryInfo taglib = pageInfo.getTaglib(uri); | |
FunctionInfo funcInfo = null; | |
if (taglib != null) { | |
funcInfo = taglib.getFunction(function); | |
} | |
if (funcInfo == null) { | |
err.jspError(n, "jsp.error.noFunction", function); | |
} | |
// Skip TLD function uniqueness check. Done by Schema ? | |
func.setUri(uri); | |
func.setFunctionInfo(funcInfo); | |
processSignature(func); | |
} | |
} | |
el.visit(new FVVisitor(n)); | |
} | |
private void processSignature(ELNode.Function func) | |
throws JasperException { | |
func.setMethodName(getMethod(func)); | |
func.setParameters(getParameters(func)); | |
} | |
/** | |
* Get the method name from the signature. | |
*/ | |
private String getMethod(ELNode.Function func) | |
throws JasperException { | |
FunctionInfo funcInfo = func.getFunctionInfo(); | |
String signature = funcInfo.getFunctionSignature(); | |
int start = signature.indexOf(' '); | |
if (start < 0) { | |
err.jspError("jsp.error.tld.fn.invalid.signature", | |
func.getPrefix(), func.getName()); | |
} | |
int end = signature.indexOf('('); | |
if (end < 0) { | |
err.jspError("jsp.error.tld.fn.invalid.signature.parenexpected", | |
func.getPrefix(), func.getName()); | |
} | |
return signature.substring(start+1, end).trim(); | |
} | |
/** | |
* Get the parameters types from the function signature. | |
* @return An array of parameter class names | |
*/ | |
private String[] getParameters(ELNode.Function func) | |
throws JasperException { | |
FunctionInfo funcInfo = func.getFunctionInfo(); | |
String signature = funcInfo.getFunctionSignature(); | |
ArrayList params = new ArrayList(); | |
// Signature is of the form | |
// <return-type> S <method-name S? '(' | |
// < <arg-type> ( ',' <arg-type> )* )? ')' | |
int start = signature.indexOf('(') + 1; | |
boolean lastArg = false; | |
while (true) { | |
int p = signature.indexOf(',', start); | |
if (p < 0) { | |
p = signature.indexOf(')', start); | |
if (p < 0) { | |
err.jspError("jsp.error.tld.fn.invalid.signature", | |
func.getPrefix(), func.getName()); | |
} | |
lastArg = true; | |
} | |
String arg = signature.substring(start, p).trim(); | |
if (!"".equals(arg)) { | |
params.add(arg); | |
} | |
if (lastArg) { | |
break; | |
} | |
start = p+1; | |
} | |
return (String[]) params.toArray(new String[params.size()]); | |
} | |
private FunctionMapper getFunctionMapper(ELNode.Nodes el) | |
throws JasperException { | |
class ValidateFunctionMapper implements FunctionMapper { | |
private HashMap fnmap = new java.util.HashMap(); | |
public void mapFunction(String fnQName, Method method) { | |
fnmap.put(fnQName, method); | |
} | |
public Method resolveFunction(String prefix, | |
String localName) { | |
return (Method) this.fnmap.get(prefix + ":" + localName); | |
} | |
} | |
class MapperELVisitor extends ELNode.Visitor { | |
ValidateFunctionMapper fmapper; | |
MapperELVisitor(ValidateFunctionMapper fmapper) { | |
this.fmapper = fmapper; | |
} | |
public void visit(ELNode.Function n) throws JasperException { | |
Class c = null; | |
Method method = null; | |
try { | |
c = loader.loadClass( | |
n.getFunctionInfo().getFunctionClass()); | |
} catch (ClassNotFoundException e) { | |
err.jspError("jsp.error.function.classnotfound", | |
n.getFunctionInfo().getFunctionClass(), | |
n.getPrefix() + ':' + n.getName(), | |
e.getMessage()); | |
} | |
String paramTypes[] = n.getParameters(); | |
int size = paramTypes.length; | |
Class params[] = new Class[size]; | |
int i = 0; | |
try { | |
for (i = 0; i < size; i++) { | |
params[i] = JspUtil.toClass(paramTypes[i], loader); | |
} | |
method = c.getDeclaredMethod(n.getMethodName(), | |
params); | |
} catch (ClassNotFoundException e) { | |
err.jspError("jsp.error.signature.classnotfound", | |
paramTypes[i], | |
n.getPrefix() + ':' + n.getName(), | |
e.getMessage()); | |
} catch (NoSuchMethodException e ) { | |
err.jspError("jsp.error.noFunctionMethod", | |
n.getMethodName(), n.getName(), | |
c.getName()); | |
} | |
fmapper.mapFunction(n.getPrefix() + ':' + n.getName(), | |
method); | |
} | |
} | |
ValidateFunctionMapper fmapper = new ValidateFunctionMapper(); | |
el.visit(new MapperELVisitor(fmapper)); | |
return fmapper; | |
} | |
} // End of ValidateVisitor | |
/** | |
* A visitor for validating TagExtraInfo classes of all tags | |
*/ | |
static class TagExtraInfoVisitor extends Node.Visitor { | |
private ErrorDispatcher err; | |
/* | |
* Constructor | |
*/ | |
TagExtraInfoVisitor(Compiler compiler) { | |
this.err = compiler.getErrorDispatcher(); | |
} | |
public void visit(Node.CustomTag n) throws JasperException { | |
TagInfo tagInfo = n.getTagInfo(); | |
if (tagInfo == null) { | |
err.jspError(n, "jsp.error.missing.tagInfo", n.getQName()); | |
} | |
ValidationMessage[] errors = tagInfo.validate(n.getTagData()); | |
if (errors != null && errors.length != 0) { | |
StringBuffer errMsg = new StringBuffer(); | |
errMsg.append("<h3>"); | |
errMsg.append(Localizer.getMessage( | |
"jsp.error.tei.invalid.attributes", n.getQName())); | |
errMsg.append("</h3>"); | |
for (int i=0; i<errors.length; i++) { | |
errMsg.append("<p>"); | |
if (errors[i].getId() != null) { | |
errMsg.append(errors[i].getId()); | |
errMsg.append(": "); | |
} | |
errMsg.append(errors[i].getMessage()); | |
errMsg.append("</p>"); | |
} | |
err.jspError(n, errMsg.toString()); | |
} | |
visitBody(n); | |
} | |
} | |
public static void validate(Compiler compiler, | |
Node.Nodes page) throws JasperException { | |
/* | |
* Visit the page/tag directives first, as they are global to the page | |
* and are position independent. | |
*/ | |
page.visit(new DirectiveVisitor(compiler)); | |
// Determine the default output content type | |
PageInfo pageInfo = compiler.getPageInfo(); | |
String contentType = pageInfo.getContentType(); | |
if (contentType == null || contentType.indexOf("charset=") < 0) { | |
boolean isXml = page.getRoot().isXmlSyntax(); | |
String defaultType; | |
if (contentType == null) { | |
defaultType = isXml? "text/xml": "text/html"; | |
} else { | |
defaultType = contentType; | |
} | |
String charset = null; | |
if (isXml) { | |
charset = "UTF-8"; | |
} else { | |
if (!page.getRoot().isDefaultPageEncoding()) { | |
charset = page.getRoot().getPageEncoding(); | |
} | |
} | |
if (charset != null) { | |
pageInfo.setContentType(defaultType + ";charset=" + charset); | |
} else { | |
pageInfo.setContentType(defaultType); | |
} | |
} | |
/* | |
* Validate all other nodes. | |
* This validation step includes checking a custom tag's mandatory and | |
* optional attributes against information in the TLD (first validation | |
* step for custom tags according to JSP.10.5). | |
*/ | |
page.visit(new ValidateVisitor(compiler)); | |
/* | |
* Invoke TagLibraryValidator classes of all imported tags | |
* (second validation step for custom tags according to JSP.10.5). | |
*/ | |
validateXmlView(new PageDataImpl(page, compiler), compiler); | |
/* | |
* Invoke TagExtraInfo method isValid() for all imported tags | |
* (third validation step for custom tags according to JSP.10.5). | |
*/ | |
page.visit(new TagExtraInfoVisitor(compiler)); | |
} | |
//********************************************************************* | |
// Private (utility) methods | |
/** | |
* Validate XML view against the TagLibraryValidator classes of all | |
* imported tag libraries. | |
*/ | |
private static void validateXmlView(PageData xmlView, Compiler compiler) | |
throws JasperException { | |
StringBuffer errMsg = null; | |
ErrorDispatcher errDisp = compiler.getErrorDispatcher(); | |
for (Iterator iter=compiler.getPageInfo().getTaglibs().iterator(); | |
iter.hasNext(); ) { | |
Object o = iter.next(); | |
if (!(o instanceof TagLibraryInfoImpl)) | |
continue; | |
TagLibraryInfoImpl tli = (TagLibraryInfoImpl) o; | |
ValidationMessage[] errors = tli.validate(xmlView); | |
if ((errors != null) && (errors.length != 0)) { | |
if (errMsg == null) { | |
errMsg = new StringBuffer(); | |
} | |
errMsg.append("<h3>"); | |
errMsg.append(Localizer.getMessage("jsp.error.tlv.invalid.page", | |
tli.getShortName(), | |
compiler.getPageInfo().getJspFile())); | |
errMsg.append("</h3>"); | |
for (int i=0; i<errors.length; i++) { | |
if (errors[i] != null) { | |
errMsg.append("<p>"); | |
errMsg.append(errors[i].getId()); | |
errMsg.append(": "); | |
errMsg.append(errors[i].getMessage()); | |
errMsg.append("</p>"); | |
} | |
} | |
} | |
} | |
if (errMsg != null) { | |
errDisp.jspError(errMsg.toString()); | |
} | |
} | |
} | |