You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by mu...@apache.org on 2009/09/28 03:55:35 UTC
svn commit: r819444 [17/27] - in /struts/struts2/trunk/plugins/embeddedjsp:
./ src/main/java/org/apache/struts2/el/
src/main/java/org/apache/struts2/el/lang/
src/main/java/org/apache/struts2/el/parser/
src/main/java/org/apache/struts2/el/util/ src/main...
Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/TagFileProcessor.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/TagFileProcessor.java?rev=819444&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/TagFileProcessor.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/TagFileProcessor.java Mon Sep 28 01:55:26 2009
@@ -0,0 +1,726 @@
+/*
+ * 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 java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+import java.util.HashMap;
+
+import javax.el.MethodExpression;
+import javax.el.ValueExpression;
+import javax.servlet.jsp.tagext.TagAttributeInfo;
+import javax.servlet.jsp.tagext.TagExtraInfo;
+import javax.servlet.jsp.tagext.TagFileInfo;
+import javax.servlet.jsp.tagext.TagInfo;
+import javax.servlet.jsp.tagext.TagLibraryInfo;
+import javax.servlet.jsp.tagext.TagVariableInfo;
+import javax.servlet.jsp.tagext.VariableInfo;
+
+import org.apache.struts2.jasper.JasperException;
+import org.apache.struts2.jasper.JspCompilationContext;
+import org.apache.struts2.jasper.servlet.JspServletWrapper;
+import org.apache.struts2.jasper.runtime.JspSourceDependent;
+
+/**
+ * 1. Processes and extracts the directive info in a tag file. 2. Compiles and
+ * loads tag files used in a JSP file.
+ *
+ * @author Kin-man Chung
+ */
+
+class TagFileProcessor {
+
+ private Vector tempVector;
+
+ /**
+ * A visitor the tag file
+ */
+ private static class TagFileDirectiveVisitor extends Node.Visitor {
+
+ private static final JspUtil.ValidAttribute[] tagDirectiveAttrs = {
+ new JspUtil.ValidAttribute("display-name"),
+ new JspUtil.ValidAttribute("body-content"),
+ new JspUtil.ValidAttribute("dynamic-attributes"),
+ new JspUtil.ValidAttribute("small-icon"),
+ new JspUtil.ValidAttribute("large-icon"),
+ new JspUtil.ValidAttribute("description"),
+ new JspUtil.ValidAttribute("example"),
+ new JspUtil.ValidAttribute("pageEncoding"),
+ new JspUtil.ValidAttribute("language"),
+ new JspUtil.ValidAttribute("import"),
+ new JspUtil.ValidAttribute("deferredSyntaxAllowedAsLiteral"), // JSP 2.1
+ new JspUtil.ValidAttribute("trimDirectiveWhitespaces"), // JSP 2.1
+ new JspUtil.ValidAttribute("isELIgnored") };
+
+ private static final JspUtil.ValidAttribute[] attributeDirectiveAttrs = {
+ new JspUtil.ValidAttribute("name", true),
+ new JspUtil.ValidAttribute("required"),
+ new JspUtil.ValidAttribute("fragment"),
+ new JspUtil.ValidAttribute("rtexprvalue"),
+ new JspUtil.ValidAttribute("type"),
+ new JspUtil.ValidAttribute("deferredValue"), // JSP 2.1
+ new JspUtil.ValidAttribute("deferredValueType"), // JSP 2.1
+ new JspUtil.ValidAttribute("deferredMethod"), // JSP 2
+ new JspUtil.ValidAttribute("deferredMethodSignature"), // JSP 21
+ new JspUtil.ValidAttribute("description") };
+
+ private static final JspUtil.ValidAttribute[] variableDirectiveAttrs = {
+ new JspUtil.ValidAttribute("name-given"),
+ new JspUtil.ValidAttribute("name-from-attribute"),
+ new JspUtil.ValidAttribute("alias"),
+ new JspUtil.ValidAttribute("variable-class"),
+ new JspUtil.ValidAttribute("scope"),
+ new JspUtil.ValidAttribute("declare"),
+ new JspUtil.ValidAttribute("description") };
+
+ private ErrorDispatcher err;
+
+ private TagLibraryInfo tagLibInfo;
+
+ private String name = null;
+
+ private String path = null;
+
+ private TagExtraInfo tei = null;
+
+ private String bodycontent = null;
+
+ private String description = null;
+
+ private String displayName = null;
+
+ private String smallIcon = null;
+
+ private String largeIcon = null;
+
+ private String dynamicAttrsMapName;
+
+ private String example = null;
+
+ private Vector attributeVector;
+
+ private Vector variableVector;
+
+ private static final String ATTR_NAME = "the name attribute of the attribute directive";
+
+ private static final String VAR_NAME_GIVEN = "the name-given attribute of the variable directive";
+
+ private static final String VAR_NAME_FROM = "the name-from-attribute attribute of the variable directive";
+
+ private static final String VAR_ALIAS = "the alias attribute of the variable directive";
+
+ private static final String TAG_DYNAMIC = "the dynamic-attributes attribute of the tag directive";
+
+ private HashMap nameTable = new HashMap();
+
+ private HashMap nameFromTable = new HashMap();
+
+ public TagFileDirectiveVisitor(Compiler compiler,
+ TagLibraryInfo tagLibInfo, String name, String path) {
+ err = compiler.getErrorDispatcher();
+ this.tagLibInfo = tagLibInfo;
+ this.name = name;
+ this.path = path;
+ attributeVector = new Vector();
+ variableVector = new Vector();
+ }
+
+ public void visit(Node.TagDirective n) throws JasperException {
+
+ JspUtil.checkAttributes("Tag directive", n, tagDirectiveAttrs, err);
+
+ bodycontent = checkConflict(n, bodycontent, "body-content");
+ if (bodycontent != null
+ && !bodycontent
+ .equalsIgnoreCase(TagInfo.BODY_CONTENT_EMPTY)
+ && !bodycontent
+ .equalsIgnoreCase(TagInfo.BODY_CONTENT_TAG_DEPENDENT)
+ && !bodycontent
+ .equalsIgnoreCase(TagInfo.BODY_CONTENT_SCRIPTLESS)) {
+ err.jspError(n, "jsp.error.tagdirective.badbodycontent",
+ bodycontent);
+ }
+ dynamicAttrsMapName = checkConflict(n, dynamicAttrsMapName,
+ "dynamic-attributes");
+ if (dynamicAttrsMapName != null) {
+ checkUniqueName(dynamicAttrsMapName, TAG_DYNAMIC, n);
+ }
+ smallIcon = checkConflict(n, smallIcon, "small-icon");
+ largeIcon = checkConflict(n, largeIcon, "large-icon");
+ description = checkConflict(n, description, "description");
+ displayName = checkConflict(n, displayName, "display-name");
+ example = checkConflict(n, example, "example");
+ }
+
+ private String checkConflict(Node n, String oldAttrValue, String attr)
+ throws JasperException {
+
+ String result = oldAttrValue;
+ String attrValue = n.getAttributeValue(attr);
+ if (attrValue != null) {
+ if (oldAttrValue != null && !oldAttrValue.equals(attrValue)) {
+ err.jspError(n, "jsp.error.tag.conflict.attr", attr,
+ oldAttrValue, attrValue);
+ }
+ result = attrValue;
+ }
+ return result;
+ }
+
+ public void visit(Node.AttributeDirective n) throws JasperException {
+
+ JspUtil.checkAttributes("Attribute directive", n,
+ attributeDirectiveAttrs, err);
+
+ // JSP 2.1 Table JSP.8-3
+ // handle deferredValue and deferredValueType
+ boolean deferredValue = false;
+ boolean deferredValueSpecified = false;
+ String deferredValueString = n.getAttributeValue("deferredValue");
+ if (deferredValueString != null) {
+ deferredValueSpecified = true;
+ deferredValue = JspUtil.booleanValue(deferredValueString);
+ }
+ String deferredValueType = n.getAttributeValue("deferredValueType");
+ if (deferredValueType != null) {
+ if (deferredValueSpecified && !deferredValue) {
+ err.jspError(n, "jsp.error.deferredvaluetypewithoutdeferredvalue");
+ } else {
+ deferredValue = true;
+ }
+ } else if (deferredValue) {
+ deferredValueType = "java.lang.Object";
+ } else {
+ deferredValueType = "java.lang.String";
+ }
+
+ // JSP 2.1 Table JSP.8-3
+ // handle deferredMethod and deferredMethodSignature
+ boolean deferredMethod = false;
+ boolean deferredMethodSpecified = false;
+ String deferredMethodString = n.getAttributeValue("deferredMethod");
+ if (deferredMethodString != null) {
+ deferredMethodSpecified = true;
+ deferredMethod = JspUtil.booleanValue(deferredMethodString);
+ }
+ String deferredMethodSignature = n
+ .getAttributeValue("deferredMethodSignature");
+ if (deferredMethodSignature != null) {
+ if (deferredMethodSpecified && !deferredMethod) {
+ err.jspError(n, "jsp.error.deferredmethodsignaturewithoutdeferredmethod");
+ } else {
+ deferredMethod = true;
+ }
+ } else if (deferredMethod) {
+ deferredMethodSignature = "void methodname()";
+ }
+
+ if (deferredMethod && deferredValue) {
+ err.jspError(n, "jsp.error.deferredmethodandvalue");
+ }
+
+ String attrName = n.getAttributeValue("name");
+ boolean required = JspUtil.booleanValue(n
+ .getAttributeValue("required"));
+ boolean rtexprvalue = true;
+ String rtexprvalueString = n.getAttributeValue("rtexprvalue");
+ if (rtexprvalueString != null) {
+ rtexprvalue = JspUtil.booleanValue(rtexprvalueString);
+ }
+ boolean fragment = JspUtil.booleanValue(n
+ .getAttributeValue("fragment"));
+ String type = n.getAttributeValue("type");
+ if (fragment) {
+ // type is fixed to "JspFragment" and a translation error
+ // must occur if specified.
+ if (type != null) {
+ err.jspError(n, "jsp.error.fragmentwithtype");
+ }
+ // rtexprvalue is fixed to "true" and a translation error
+ // must occur if specified.
+ rtexprvalue = true;
+ if (rtexprvalueString != null) {
+ err.jspError(n, "jsp.error.frgmentwithrtexprvalue");
+ }
+ } else {
+ if (type == null)
+ type = "java.lang.String";
+
+ if (deferredValue) {
+ type = ValueExpression.class.getName();
+ } else if (deferredMethod) {
+ type = MethodExpression.class.getName();
+ }
+ }
+
+ if (("2.0".equals(tagLibInfo.getRequiredVersion()) || ("1.2".equals(tagLibInfo.getRequiredVersion())))
+ && (deferredMethodSpecified || deferredMethod
+ || deferredValueSpecified || deferredValue)) {
+ err.jspError("jsp.error.invalid.version", path);
+ }
+
+ TagAttributeInfo tagAttributeInfo = new TagAttributeInfo(attrName,
+ required, type, rtexprvalue, fragment, null, deferredValue,
+ deferredMethod, deferredValueType, deferredMethodSignature);
+ attributeVector.addElement(tagAttributeInfo);
+ checkUniqueName(attrName, ATTR_NAME, n, tagAttributeInfo);
+ }
+
+ public void visit(Node.VariableDirective n) throws JasperException {
+
+ JspUtil.checkAttributes("Variable directive", n,
+ variableDirectiveAttrs, err);
+
+ String nameGiven = n.getAttributeValue("name-given");
+ String nameFromAttribute = n
+ .getAttributeValue("name-from-attribute");
+ if (nameGiven == null && nameFromAttribute == null) {
+ err.jspError("jsp.error.variable.either.name");
+ }
+
+ if (nameGiven != null && nameFromAttribute != null) {
+ err.jspError("jsp.error.variable.both.name");
+ }
+
+ String alias = n.getAttributeValue("alias");
+ if (nameFromAttribute != null && alias == null
+ || nameFromAttribute == null && alias != null) {
+ err.jspError("jsp.error.variable.alias");
+ }
+
+ String className = n.getAttributeValue("variable-class");
+ if (className == null)
+ className = "java.lang.String";
+
+ String declareStr = n.getAttributeValue("declare");
+ boolean declare = true;
+ if (declareStr != null)
+ declare = JspUtil.booleanValue(declareStr);
+
+ int scope = VariableInfo.NESTED;
+ String scopeStr = n.getAttributeValue("scope");
+ if (scopeStr != null) {
+ if ("NESTED".equals(scopeStr)) {
+ // Already the default
+ } else if ("AT_BEGIN".equals(scopeStr)) {
+ scope = VariableInfo.AT_BEGIN;
+ } else if ("AT_END".equals(scopeStr)) {
+ scope = VariableInfo.AT_END;
+ }
+ }
+
+ if (nameFromAttribute != null) {
+ /*
+ * An alias has been specified. We use 'nameGiven' to hold the
+ * value of the alias, and 'nameFromAttribute' to hold the name
+ * of the attribute whose value (at invocation-time) denotes the
+ * name of the variable that is being aliased
+ */
+ nameGiven = alias;
+ checkUniqueName(nameFromAttribute, VAR_NAME_FROM, n);
+ checkUniqueName(alias, VAR_ALIAS, n);
+ } else {
+ // name-given specified
+ checkUniqueName(nameGiven, VAR_NAME_GIVEN, n);
+ }
+
+ variableVector.addElement(new TagVariableInfo(nameGiven,
+ nameFromAttribute, className, declare, scope));
+ }
+
+ /*
+ * Returns the vector of attributes corresponding to attribute
+ * directives.
+ */
+ public Vector getAttributesVector() {
+ return attributeVector;
+ }
+
+ /*
+ * Returns the vector of variables corresponding to variable directives.
+ */
+ public Vector getVariablesVector() {
+ return variableVector;
+ }
+
+ /*
+ * Returns the value of the dynamic-attributes tag directive attribute.
+ */
+ public String getDynamicAttributesMapName() {
+ return dynamicAttrsMapName;
+ }
+
+ public TagInfo getTagInfo() throws JasperException {
+
+ if (name == null) {
+ // XXX Get it from tag file name
+ }
+
+ if (bodycontent == null) {
+ bodycontent = TagInfo.BODY_CONTENT_SCRIPTLESS;
+ }
+
+ String tagClassName = JspUtil.getTagHandlerClassName(
+ path, tagLibInfo.getReliableURN(), err);
+
+ TagVariableInfo[] tagVariableInfos = new TagVariableInfo[variableVector
+ .size()];
+ variableVector.copyInto(tagVariableInfos);
+
+ TagAttributeInfo[] tagAttributeInfo = new TagAttributeInfo[attributeVector
+ .size()];
+ attributeVector.copyInto(tagAttributeInfo);
+
+ return new JasperTagInfo(name, tagClassName, bodycontent,
+ description, tagLibInfo, tei, tagAttributeInfo,
+ displayName, smallIcon, largeIcon, tagVariableInfos,
+ dynamicAttrsMapName);
+ }
+
+ static class NameEntry {
+ private String type;
+
+ private Node node;
+
+ private TagAttributeInfo attr;
+
+ NameEntry(String type, Node node, TagAttributeInfo attr) {
+ this.type = type;
+ this.node = node;
+ this.attr = attr;
+ }
+
+ String getType() {
+ return type;
+ }
+
+ Node getNode() {
+ return node;
+ }
+
+ TagAttributeInfo getTagAttributeInfo() {
+ return attr;
+ }
+ }
+
+ /**
+ * Reports a translation error if names specified in attributes of
+ * directives are not unique in this translation unit.
+ *
+ * The value of the following attributes must be unique. 1. 'name'
+ * attribute of an attribute directive 2. 'name-given' attribute of a
+ * variable directive 3. 'alias' attribute of variable directive 4.
+ * 'dynamic-attributes' of a tag directive except that
+ * 'dynamic-attributes' can (and must) have the same value when it
+ * appears in multiple tag directives.
+ *
+ * Also, 'name-from' attribute of a variable directive cannot have the
+ * same value as that from another variable directive.
+ */
+ private void checkUniqueName(String name, String type, Node n)
+ throws JasperException {
+ checkUniqueName(name, type, n, null);
+ }
+
+ private void checkUniqueName(String name, String type, Node n,
+ TagAttributeInfo attr) throws JasperException {
+
+ HashMap table = (type == VAR_NAME_FROM) ? nameFromTable : nameTable;
+ NameEntry nameEntry = (NameEntry) table.get(name);
+ if (nameEntry != null) {
+ if (type != TAG_DYNAMIC || nameEntry.getType() != TAG_DYNAMIC) {
+ int line = nameEntry.getNode().getStart().getLineNumber();
+ err.jspError(n, "jsp.error.tagfile.nameNotUnique", type,
+ nameEntry.getType(), Integer.toString(line));
+ }
+ } else {
+ table.put(name, new NameEntry(type, n, attr));
+ }
+ }
+
+ /**
+ * Perform miscellean checks after the nodes are visited.
+ */
+ void postCheck() throws JasperException {
+ // Check that var.name-from-attributes has valid values.
+ Iterator iter = nameFromTable.keySet().iterator();
+ while (iter.hasNext()) {
+ String nameFrom = (String) iter.next();
+ NameEntry nameEntry = (NameEntry) nameTable.get(nameFrom);
+ NameEntry nameFromEntry = (NameEntry) nameFromTable
+ .get(nameFrom);
+ Node nameFromNode = nameFromEntry.getNode();
+ if (nameEntry == null) {
+ err.jspError(nameFromNode,
+ "jsp.error.tagfile.nameFrom.noAttribute", nameFrom);
+ } else {
+ Node node = nameEntry.getNode();
+ TagAttributeInfo tagAttr = nameEntry.getTagAttributeInfo();
+ if (!"java.lang.String".equals(tagAttr.getTypeName())
+ || !tagAttr.isRequired()
+ || tagAttr.canBeRequestTime()) {
+ err.jspError(nameFromNode,
+ "jsp.error.tagfile.nameFrom.badAttribute",
+ nameFrom, Integer.toString(node.getStart()
+ .getLineNumber()));
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Parses the tag file, and collects information on the directives included
+ * in it. The method is used to obtain the info on the tag file, when the
+ * handler that it represents is referenced. The tag file is not compiled
+ * here.
+ *
+ * @param pc
+ * the current ParserController used in this compilation
+ * @param name
+ * the tag name as specified in the TLD
+ * @param tagfile
+ * the path for the tagfile
+ * @param tagLibInfo
+ * the TagLibraryInfo object associated with this TagInfo
+ * @return a TagInfo object assembled from the directives in the tag file.
+ * @deprecated Use {@link TagFileProcessor#parseTagFileDirectives(
+ * ParserController, String, String, URL, TagLibraryInfo)}
+ * See https://issues.apache.org/bugzilla/show_bug.cgi?id=46471
+ */
+ public static TagInfo parseTagFileDirectives(ParserController pc,
+ String name, String path, TagLibraryInfo tagLibInfo)
+ throws JasperException {
+ return parseTagFileDirectives(pc, name, path,
+ pc.getJspCompilationContext().getTagFileJarUrl(path),
+ tagLibInfo);
+ }
+
+ /**
+ * Parses the tag file, and collects information on the directives included
+ * in it. The method is used to obtain the info on the tag file, when the
+ * handler that it represents is referenced. The tag file is not compiled
+ * here.
+ *
+ * @param pc
+ * the current ParserController used in this compilation
+ * @param name
+ * the tag name as specified in the TLD
+ * @param tagfile
+ * the path for the tagfile
+ * @param tagFileJarUrl
+ * the url for the Jar containign the tag file
+ * @param tagLibInfo
+ * the TagLibraryInfo object associated with this TagInfo
+ * @return a TagInfo object assembled from the directives in the tag file.
+ */
+ public static TagInfo parseTagFileDirectives(ParserController pc,
+ String name, String path, URL tagFileJarUrl, TagLibraryInfo tagLibInfo)
+ throws JasperException {
+
+ ErrorDispatcher err = pc.getCompiler().getErrorDispatcher();
+
+ Node.Nodes page = null;
+ try {
+ page = pc.parseTagFileDirectives(path, tagFileJarUrl);
+ } catch (FileNotFoundException e) {
+ err.jspError("jsp.error.file.not.found", path);
+ } catch (IOException e) {
+ err.jspError("jsp.error.file.not.found", path);
+ }
+
+ TagFileDirectiveVisitor tagFileVisitor = new TagFileDirectiveVisitor(pc
+ .getCompiler(), tagLibInfo, name, path);
+ page.visit(tagFileVisitor);
+ tagFileVisitor.postCheck();
+
+ return tagFileVisitor.getTagInfo();
+ }
+
+ /**
+ * Compiles and loads a tagfile.
+ */
+ private Class loadTagFile(Compiler compiler, String tagFilePath,
+ TagInfo tagInfo, PageInfo parentPageInfo) throws JasperException {
+
+ URL tagFileJarUrl = null;
+ if (tagFilePath.startsWith("/META-INF/")) {
+ try {
+ tagFileJarUrl = new URL("jar:" +
+ compiler.getCompilationContext().getTldLocation(
+ tagInfo.getTagLibrary().getURI())[0] + "!/");
+ } catch (MalformedURLException e) {
+ // Ignore - tagFileJarUrl will be null
+ }
+ }
+ String tagFileJarPath;
+ if (tagFileJarUrl == null) {
+ tagFileJarPath = "";
+ } else {
+ tagFileJarPath = tagFileJarUrl.toString();
+ }
+
+ JspCompilationContext ctxt = compiler.getCompilationContext();
+ JspRuntimeContext rctxt = ctxt.getRuntimeContext();
+ JspServletWrapper wrapper = rctxt.getWrapper(tagFileJarPath + tagFilePath);
+
+ synchronized (rctxt) {
+ if (wrapper == null) {
+ wrapper = new JspServletWrapper(ctxt.getServletContext(), ctxt
+ .getOptions(), tagFilePath, tagInfo, ctxt
+ .getRuntimeContext(), tagFileJarUrl);
+ rctxt.addWrapper(tagFileJarPath + tagFilePath, wrapper);
+
+ // Use same classloader and classpath for compiling tag files
+ wrapper.getJspEngineContext().setClassLoader(
+ (URLClassLoader) ctxt.getClassLoader());
+ wrapper.getJspEngineContext().setClassPath(ctxt.getClassPath());
+ } else {
+ // Make sure that JspCompilationContext gets the latest TagInfo
+ // for the tag file. TagInfo instance was created the last
+ // time the tag file was scanned for directives, and the tag
+ // file may have been modified since then.
+ wrapper.getJspEngineContext().setTagInfo(tagInfo);
+ }
+
+ Class tagClazz;
+ int tripCount = wrapper.incTripCount();
+ try {
+ if (tripCount > 0) {
+ // When tripCount is greater than zero, a circular
+ // dependency exists. The circularily dependant tag
+ // file is compiled in prototype mode, to avoid infinite
+ // recursion.
+
+ JspServletWrapper tempWrapper = new JspServletWrapper(ctxt
+ .getServletContext(), ctxt.getOptions(),
+ tagFilePath, tagInfo, ctxt.getRuntimeContext(),
+ ctxt.getTagFileJarUrl(tagFilePath));
+ tagClazz = tempWrapper.loadTagFilePrototype();
+ tempVector.add(tempWrapper.getJspEngineContext()
+ .getCompiler());
+ } else {
+ tagClazz = wrapper.loadTagFile();
+ }
+ } finally {
+ wrapper.decTripCount();
+ }
+
+ // Add the dependants for this tag file to its parent's
+ // dependant list. The only reliable dependency information
+ // can only be obtained from the tag instance.
+ try {
+ Object tagIns = tagClazz.newInstance();
+ if (tagIns instanceof JspSourceDependent) {
+ Iterator iter = ((List) ((JspSourceDependent) tagIns)
+ .getDependants()).iterator();
+ while (iter.hasNext()) {
+ parentPageInfo.addDependant((String) iter.next());
+ }
+ }
+ } catch (Exception e) {
+ // ignore errors
+ }
+
+ return tagClazz;
+ }
+ }
+
+ /*
+ * Visitor which scans the page and looks for tag handlers that are tag
+ * files, compiling (if necessary) and loading them.
+ */
+ private class TagFileLoaderVisitor extends Node.Visitor {
+
+ private Compiler compiler;
+
+ private PageInfo pageInfo;
+
+ TagFileLoaderVisitor(Compiler compiler) {
+
+ this.compiler = compiler;
+ this.pageInfo = compiler.getPageInfo();
+ }
+
+ public void visit(Node.CustomTag n) throws JasperException {
+ TagFileInfo tagFileInfo = n.getTagFileInfo();
+ if (tagFileInfo != null) {
+ String tagFilePath = tagFileInfo.getPath();
+ if (tagFilePath.startsWith("/META-INF/")) {
+ // For tags in JARs, add the TLD and the tag as a dependency
+ String[] location =
+ compiler.getCompilationContext().getTldLocation(
+ tagFileInfo.getTagInfo().getTagLibrary().getURI());
+ // Add TLD
+ pageInfo.addDependant("jar:" + location[0] + "!/" +
+ location[1]);
+ // Add Tag
+ pageInfo.addDependant("jar:" + location[0] + "!" +
+ tagFilePath);
+ } else {
+ pageInfo.addDependant(tagFilePath);
+ }
+ Class c = loadTagFile(compiler, tagFilePath, n.getTagInfo(),
+ pageInfo);
+ n.setTagHandlerClass(c);
+ }
+ visitBody(n);
+ }
+ }
+
+ /**
+ * Implements a phase of the translation that compiles (if necessary) the
+ * tag files used in a JSP files. The directives in the tag files are
+ * assumed to have been proccessed and encapsulated as TagFileInfo in the
+ * CustomTag nodes.
+ */
+ public void loadTagFiles(Compiler compiler, Node.Nodes page)
+ throws JasperException {
+
+ tempVector = new Vector();
+ page.visit(new TagFileLoaderVisitor(compiler));
+ }
+
+ /**
+ * Removed the java and class files for the tag prototype generated from the
+ * current compilation.
+ *
+ * @param classFileName
+ * If non-null, remove only the class file with with this name.
+ */
+ public void removeProtoTypeFiles(String classFileName) {
+ Iterator iter = tempVector.iterator();
+ while (iter.hasNext()) {
+ Compiler c = (Compiler) iter.next();
+ if (classFileName == null) {
+ c.removeGeneratedClassFiles();
+ } else if (classFileName.equals(c.getCompilationContext()
+ .getClassFileName())) {
+ c.removeGeneratedClassFiles();
+ tempVector.remove(c);
+ return;
+ }
+ }
+ }
+}
Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/TagLibraryInfoImpl.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/TagLibraryInfoImpl.java?rev=819444&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/TagLibraryInfoImpl.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/TagLibraryInfoImpl.java Mon Sep 28 01:55:26 2009
@@ -0,0 +1,768 @@
+/*
+ * 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 java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Vector;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+import javax.servlet.jsp.tagext.FunctionInfo;
+import javax.servlet.jsp.tagext.PageData;
+import javax.servlet.jsp.tagext.TagAttributeInfo;
+import javax.servlet.jsp.tagext.TagExtraInfo;
+import javax.servlet.jsp.tagext.TagFileInfo;
+import javax.servlet.jsp.tagext.TagInfo;
+import javax.servlet.jsp.tagext.TagLibraryInfo;
+import javax.servlet.jsp.tagext.TagLibraryValidator;
+import javax.servlet.jsp.tagext.TagVariableInfo;
+import javax.servlet.jsp.tagext.ValidationMessage;
+import javax.servlet.jsp.tagext.VariableInfo;
+
+import org.apache.struts2.jasper.JasperException;
+import org.apache.struts2.jasper.JspCompilationContext;
+import org.apache.struts2.jasper.xmlparser.ParserUtils;
+import org.apache.struts2.jasper.xmlparser.TreeNode;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
+/**
+ * Implementation of the TagLibraryInfo class from the JSP spec.
+ *
+ * @author Anil K. Vijendran
+ * @author Mandar Raje
+ * @author Pierre Delisle
+ * @author Kin-man Chung
+ * @author Jan Luehe
+ */
+class TagLibraryInfoImpl extends TagLibraryInfo implements TagConstants {
+
+ // Logger
+ private Log log = LogFactory.getLog(TagLibraryInfoImpl.class);
+
+ private JspCompilationContext ctxt;
+
+ private PageInfo pi;
+
+ private ErrorDispatcher err;
+
+ private ParserController parserController;
+
+ private final void print(String name, String value, PrintWriter w) {
+ if (value != null) {
+ w.print(name + " = {\n\t");
+ w.print(value);
+ w.print("\n}\n");
+ }
+ }
+
+ public String toString() {
+ StringWriter sw = new StringWriter();
+ PrintWriter out = new PrintWriter(sw);
+ print("tlibversion", tlibversion, out);
+ print("jspversion", jspversion, out);
+ print("shortname", shortname, out);
+ print("urn", urn, out);
+ print("info", info, out);
+ print("uri", uri, out);
+ print("tagLibraryValidator", "" + tagLibraryValidator, out);
+
+ for (int i = 0; i < tags.length; i++)
+ out.println(tags[i].toString());
+
+ for (int i = 0; i < tagFiles.length; i++)
+ out.println(tagFiles[i].toString());
+
+ for (int i = 0; i < functions.length; i++)
+ out.println(functions[i].toString());
+
+ return sw.toString();
+ }
+
+ // XXX FIXME
+ // resolveRelativeUri and/or getResourceAsStream don't seem to properly
+ // handle relative paths when dealing when home and getDocBase are set
+ // the following is a workaround until these problems are resolved.
+ private InputStream getResourceAsStream(String uri)
+ throws FileNotFoundException {
+ try {
+ // see if file exists on the filesystem first
+ String real = ctxt.getRealPath(uri);
+ if (real == null) {
+ return ctxt.getResourceAsStream(uri);
+ } else {
+ return new FileInputStream(real);
+ }
+ } catch (FileNotFoundException ex) {
+ // if file not found on filesystem, get the resource through
+ // the context
+ return ctxt.getResourceAsStream(uri);
+ }
+
+ }
+
+ /**
+ * Constructor.
+ */
+ public TagLibraryInfoImpl(JspCompilationContext ctxt, ParserController pc, PageInfo pi,
+ String prefix, String uriIn, String[] location, ErrorDispatcher err)
+ throws JasperException {
+ super(prefix, uriIn);
+
+ this.ctxt = ctxt;
+ this.parserController = pc;
+ this.pi = pi;
+ this.err = err;
+ InputStream in = null;
+ JarFile jarFile = null;
+
+ if (location == null) {
+ // The URI points to the TLD itself or to a JAR file in which the
+ // TLD is stored
+ location = generateTLDLocation(uri, ctxt);
+ }
+
+ try {
+ if (!location[0].endsWith("jar")) {
+ // Location points to TLD file
+ try {
+ in = getResourceAsStream(location[0]);
+ if (in == null) {
+ throw new FileNotFoundException(location[0]);
+ }
+ } catch (FileNotFoundException ex) {
+ err.jspError("jsp.error.file.not.found", location[0]);
+ }
+
+ parseTLD(ctxt, location[0], in, null);
+ // Add TLD to dependency list
+ PageInfo pageInfo = ctxt.createCompiler().getPageInfo();
+ if (pageInfo != null) {
+ pageInfo.addDependant(location[0]);
+ }
+ } else {
+ // Tag library is packaged in JAR file
+ try {
+ URL jarFileUrl = new URL("jar:" + location[0] + "!/");
+ JarURLConnection conn = (JarURLConnection) jarFileUrl
+ .openConnection();
+ conn.setUseCaches(false);
+ conn.connect();
+ jarFile = conn.getJarFile();
+ ZipEntry jarEntry = jarFile.getEntry(location[1]);
+ in = jarFile.getInputStream(jarEntry);
+ parseTLD(ctxt, location[0], in, jarFileUrl);
+ } catch (Exception ex) {
+ err.jspError("jsp.error.tld.unable_to_read", location[0],
+ location[1], ex.toString());
+ }
+ }
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (Throwable t) {
+ }
+ }
+ if (jarFile != null) {
+ try {
+ jarFile.close();
+ } catch (Throwable t) {
+ }
+ }
+ }
+
+ }
+
+ public TagLibraryInfo[] getTagLibraryInfos() {
+ Collection coll = pi.getTaglibs();
+ return (TagLibraryInfo[]) coll.toArray(new TagLibraryInfo[0]);
+ }
+
+ /*
+ * @param ctxt The JSP compilation context @param uri The TLD's uri @param
+ * in The TLD's input stream @param jarFileUrl The JAR file containing the
+ * TLD, or null if the tag library is not packaged in a JAR
+ */
+ private void parseTLD(JspCompilationContext ctxt, String uri,
+ InputStream in, URL jarFileUrl) throws JasperException {
+ Vector tagVector = new Vector();
+ Vector tagFileVector = new Vector();
+ Hashtable functionTable = new Hashtable();
+
+ // Create an iterator over the child elements of our <taglib> element
+ ParserUtils pu = new ParserUtils();
+ TreeNode tld = pu.parseXMLDocument(uri, in);
+
+ // Check to see if the <taglib> root element contains a 'version'
+ // attribute, which was added in JSP 2.0 to replace the <jsp-version>
+ // subelement
+ this.jspversion = tld.findAttribute("version");
+
+ // Process each child element of our <taglib> element
+ Iterator list = tld.findChildren();
+
+ while (list.hasNext()) {
+ TreeNode element = (TreeNode) list.next();
+ String tname = element.getName();
+
+ if ("tlibversion".equals(tname) // JSP 1.1
+ || "tlib-version".equals(tname)) { // JSP 1.2
+ this.tlibversion = element.getBody();
+ } else if ("jspversion".equals(tname)
+ || "jsp-version".equals(tname)) {
+ this.jspversion = element.getBody();
+ } else if ("shortname".equals(tname) || "short-name".equals(tname))
+ this.shortname = element.getBody();
+ else if ("uri".equals(tname))
+ this.urn = element.getBody();
+ else if ("info".equals(tname) || "description".equals(tname))
+ this.info = element.getBody();
+ else if ("validator".equals(tname))
+ this.tagLibraryValidator = createValidator(element);
+ else if ("tag".equals(tname))
+ tagVector.addElement(createTagInfo(element, jspversion));
+ else if ("tag-file".equals(tname)) {
+ TagFileInfo tagFileInfo = createTagFileInfo(element, uri,
+ jarFileUrl);
+ tagFileVector.addElement(tagFileInfo);
+ } else if ("function".equals(tname)) { // JSP2.0
+ FunctionInfo funcInfo = createFunctionInfo(element);
+ String funcName = funcInfo.getName();
+ if (functionTable.containsKey(funcName)) {
+ err.jspError("jsp.error.tld.fn.duplicate.name", funcName,
+ uri);
+
+ }
+ functionTable.put(funcName, funcInfo);
+ } else if ("display-name".equals(tname) || // Ignored elements
+ "small-icon".equals(tname) || "large-icon".equals(tname)
+ || "listener".equals(tname)) {
+ ;
+ } else if ("taglib-extension".equals(tname)) {
+ // Recognized but ignored
+ } else {
+ if (log.isWarnEnabled()) {
+ log.warn(Localizer.getMessage(
+ "jsp.warning.unknown.element.in.taglib", tname));
+ }
+ }
+
+ }
+
+ if (tlibversion == null) {
+ err.jspError("jsp.error.tld.mandatory.element.missing",
+ "tlib-version");
+ }
+ if (jspversion == null) {
+ err.jspError("jsp.error.tld.mandatory.element.missing",
+ "jsp-version");
+ }
+
+ this.tags = new TagInfo[tagVector.size()];
+ tagVector.copyInto(this.tags);
+
+ this.tagFiles = new TagFileInfo[tagFileVector.size()];
+ tagFileVector.copyInto(this.tagFiles);
+
+ this.functions = new FunctionInfo[functionTable.size()];
+ int i = 0;
+ Enumeration enumeration = functionTable.elements();
+ while (enumeration.hasMoreElements()) {
+ this.functions[i++] = (FunctionInfo) enumeration.nextElement();
+ }
+ }
+
+ /*
+ * @param uri The uri of the TLD @param ctxt The compilation context
+ *
+ * @return String array whose first element denotes the path to the TLD. If
+ * the path to the TLD points to a jar file, then the second element denotes
+ * the name of the TLD entry in the jar file, which is hardcoded to
+ * META-INF/taglib.tld.
+ */
+ private String[] generateTLDLocation(String uri, JspCompilationContext ctxt)
+ throws JasperException {
+
+ int uriType = TldLocationsCache.uriType(uri);
+ if (uriType == TldLocationsCache.ABS_URI) {
+ err.jspError("jsp.error.taglibDirective.absUriCannotBeResolved",
+ uri);
+ } else if (uriType == TldLocationsCache.NOROOT_REL_URI) {
+ uri = ctxt.resolveRelativeUri(uri);
+ }
+
+ String[] location = new String[2];
+ location[0] = uri;
+ if (location[0].endsWith("jar")) {
+ URL url = null;
+ try {
+ url = ctxt.getResource(location[0]);
+ } catch (Exception ex) {
+ err.jspError("jsp.error.tld.unable_to_get_jar", location[0], ex
+ .toString());
+ }
+ if (url == null) {
+ err.jspError("jsp.error.tld.missing_jar", location[0]);
+ }
+ location[0] = url.toString();
+ location[1] = "META-INF/taglib.tld";
+ }
+
+ return location;
+ }
+
+ private TagInfo createTagInfo(TreeNode elem, String jspVersion)
+ throws JasperException {
+
+ String tagName = null;
+ String tagClassName = null;
+ String teiClassName = null;
+
+ /*
+ * Default body content for JSP 1.2 tag handlers (<body-content> has
+ * become mandatory in JSP 2.0, because the default would be invalid for
+ * simple tag handlers)
+ */
+ String bodycontent = "JSP";
+
+ String info = null;
+ String displayName = null;
+ String smallIcon = null;
+ String largeIcon = null;
+ boolean dynamicAttributes = false;
+
+ Vector attributeVector = new Vector();
+ Vector variableVector = new Vector();
+ Iterator list = elem.findChildren();
+ while (list.hasNext()) {
+ TreeNode element = (TreeNode) list.next();
+ String tname = element.getName();
+
+ if ("name".equals(tname)) {
+ tagName = element.getBody();
+ } else if ("tagclass".equals(tname) || "tag-class".equals(tname)) {
+ tagClassName = element.getBody();
+ } else if ("teiclass".equals(tname) || "tei-class".equals(tname)) {
+ teiClassName = element.getBody();
+ } else if ("bodycontent".equals(tname)
+ || "body-content".equals(tname)) {
+ bodycontent = element.getBody();
+ } else if ("display-name".equals(tname)) {
+ displayName = element.getBody();
+ } else if ("small-icon".equals(tname)) {
+ smallIcon = element.getBody();
+ } else if ("large-icon".equals(tname)) {
+ largeIcon = element.getBody();
+ } else if ("icon".equals(tname)) {
+ TreeNode icon = element.findChild("small-icon");
+ if (icon != null) {
+ smallIcon = icon.getBody();
+ }
+ icon = element.findChild("large-icon");
+ if (icon != null) {
+ largeIcon = icon.getBody();
+ }
+ } else if ("info".equals(tname) || "description".equals(tname)) {
+ info = element.getBody();
+ } else if ("variable".equals(tname)) {
+ variableVector.addElement(createVariable(element));
+ } else if ("attribute".equals(tname)) {
+ attributeVector
+ .addElement(createAttribute(element, jspVersion));
+ } else if ("dynamic-attributes".equals(tname)) {
+ dynamicAttributes = JspUtil.booleanValue(element.getBody());
+ } else if ("example".equals(tname)) {
+ // Ignored elements
+ } else if ("tag-extension".equals(tname)) {
+ // Ignored
+ } else {
+ if (log.isWarnEnabled()) {
+ log.warn(Localizer.getMessage(
+ "jsp.warning.unknown.element.in.tag", tname));
+ }
+ }
+ }
+
+ TagExtraInfo tei = null;
+ if (teiClassName != null && !teiClassName.equals("")) {
+ try {
+ Class teiClass = ctxt.getClassLoader().loadClass(teiClassName);
+ tei = (TagExtraInfo) teiClass.newInstance();
+ } catch (Exception e) {
+ err.jspError("jsp.error.teiclass.instantiation", teiClassName,
+ e);
+ }
+ }
+
+ TagAttributeInfo[] tagAttributeInfo = new TagAttributeInfo[attributeVector
+ .size()];
+ attributeVector.copyInto(tagAttributeInfo);
+
+ TagVariableInfo[] tagVariableInfos = new TagVariableInfo[variableVector
+ .size()];
+ variableVector.copyInto(tagVariableInfos);
+
+ TagInfo taginfo = new TagInfo(tagName, tagClassName, bodycontent, info,
+ this, tei, tagAttributeInfo, displayName, smallIcon, largeIcon,
+ tagVariableInfos, dynamicAttributes);
+ return taginfo;
+ }
+
+ /*
+ * Parses the tag file directives of the given TagFile and turns them into a
+ * TagInfo.
+ *
+ * @param elem The <tag-file> element in the TLD @param uri The location of
+ * the TLD, in case the tag file is specified relative to it @param jarFile
+ * The JAR file, in case the tag file is packaged in a JAR
+ *
+ * @return TagInfo correspoding to tag file directives
+ */
+ private TagFileInfo createTagFileInfo(TreeNode elem, String uri,
+ URL jarFileUrl) throws JasperException {
+
+ String name = null;
+ String path = null;
+
+ Iterator list = elem.findChildren();
+ while (list.hasNext()) {
+ TreeNode child = (TreeNode) list.next();
+ String tname = child.getName();
+ if ("name".equals(tname)) {
+ name = child.getBody();
+ } else if ("path".equals(tname)) {
+ path = child.getBody();
+ } else if ("example".equals(tname)) {
+ // Ignore <example> element: Bugzilla 33538
+ } else if ("tag-extension".equals(tname)) {
+ // Ignore <tag-extension> element: Bugzilla 33538
+ } else if ("icon".equals(tname)
+ || "display-name".equals(tname)
+ || "description".equals(tname)) {
+ // Ignore these elements: Bugzilla 38015
+ } else {
+ if (log.isWarnEnabled()) {
+ log.warn(Localizer.getMessage(
+ "jsp.warning.unknown.element.in.tagfile", tname));
+ }
+ }
+ }
+
+ if (path.startsWith("/META-INF/tags")) {
+ // Tag file packaged in JAR
+ // See https://issues.apache.org/bugzilla/show_bug.cgi?id=46471
+ // This needs to be removed once all the broken code that depends on
+ // it has been removed
+ ctxt.setTagFileJarUrl(path, jarFileUrl);
+ } else if (!path.startsWith("/WEB-INF/tags")) {
+ err.jspError("jsp.error.tagfile.illegalPath", path);
+ }
+
+ TagInfo tagInfo = TagFileProcessor.parseTagFileDirectives(
+ parserController, name, path, jarFileUrl, this);
+ return new TagFileInfo(name, path, tagInfo);
+ }
+
+ TagAttributeInfo createAttribute(TreeNode elem, String jspVersion) {
+ String name = null;
+ String type = null;
+ String expectedType = null;
+ String methodSignature = null;
+ boolean required = false, rtexprvalue = false, reqTime = false, isFragment = false, deferredValue = false, deferredMethod = false;
+
+ Iterator list = elem.findChildren();
+ while (list.hasNext()) {
+ TreeNode element = (TreeNode) list.next();
+ String tname = element.getName();
+
+ if ("name".equals(tname)) {
+ name = element.getBody();
+ } else if ("required".equals(tname)) {
+ String s = element.getBody();
+ if (s != null)
+ required = JspUtil.booleanValue(s);
+ } else if ("rtexprvalue".equals(tname)) {
+ String s = element.getBody();
+ if (s != null)
+ rtexprvalue = JspUtil.booleanValue(s);
+ } else if ("type".equals(tname)) {
+ type = element.getBody();
+ if ("1.2".equals(jspVersion)
+ && (type.equals("Boolean") || type.equals("Byte")
+ || type.equals("Character")
+ || type.equals("Double")
+ || type.equals("Float")
+ || type.equals("Integer")
+ || type.equals("Long") || type.equals("Object")
+ || type.equals("Short") || type
+ .equals("String"))) {
+ type = "java.lang." + type;
+ }
+ } else if ("fragment".equals(tname)) {
+ String s = element.getBody();
+ if (s != null) {
+ isFragment = JspUtil.booleanValue(s);
+ }
+ } else if ("deferred-value".equals(tname)) {
+ deferredValue = true;
+ type = "javax.el.ValueExpression";
+ TreeNode child = element.findChild("type");
+ if (child != null) {
+ expectedType = child.getBody();
+ if (expectedType != null) {
+ expectedType = expectedType.trim();
+ }
+ } else {
+ expectedType = "java.lang.Object";
+ }
+ } else if ("deferred-method".equals(tname)) {
+ deferredMethod = true;
+ type = "javax.el.MethodExpression";
+ TreeNode child = element.findChild("method-signature");
+ if (child != null) {
+ methodSignature = child.getBody();
+ if (methodSignature != null) {
+ methodSignature = methodSignature.trim();
+ }
+ } else {
+ methodSignature = "java.lang.Object method()";
+ }
+ } else if ("description".equals(tname) || // Ignored elements
+ false) {
+ ;
+ } else {
+ if (log.isWarnEnabled()) {
+ log.warn(Localizer.getMessage(
+ "jsp.warning.unknown.element.in.attribute", tname));
+ }
+ }
+ }
+
+ if (isFragment) {
+ /*
+ * According to JSP.C-3 ("TLD Schema Element Structure - tag"),
+ * 'type' and 'rtexprvalue' must not be specified if 'fragment' has
+ * been specified (this will be enforced by validating parser).
+ * Also, if 'fragment' is TRUE, 'type' is fixed at
+ * javax.servlet.jsp.tagext.JspFragment, and 'rtexprvalue' is fixed
+ * at true. See also JSP.8.5.2.
+ */
+ type = "javax.servlet.jsp.tagext.JspFragment";
+ rtexprvalue = true;
+ }
+
+ if (!rtexprvalue && type == null) {
+ // According to JSP spec, for static values (those determined at
+ // translation time) the type is fixed at java.lang.String.
+ type = "java.lang.String";
+ }
+
+ return new TagAttributeInfo(name, required, type, rtexprvalue,
+ isFragment, null, deferredValue, deferredMethod, expectedType,
+ methodSignature);
+ }
+
+ TagVariableInfo createVariable(TreeNode elem) {
+ String nameGiven = null;
+ String nameFromAttribute = null;
+ String className = "java.lang.String";
+ boolean declare = true;
+ int scope = VariableInfo.NESTED;
+
+ Iterator list = elem.findChildren();
+ while (list.hasNext()) {
+ TreeNode element = (TreeNode) list.next();
+ String tname = element.getName();
+ if ("name-given".equals(tname))
+ nameGiven = element.getBody();
+ else if ("name-from-attribute".equals(tname))
+ nameFromAttribute = element.getBody();
+ else if ("variable-class".equals(tname))
+ className = element.getBody();
+ else if ("declare".equals(tname)) {
+ String s = element.getBody();
+ if (s != null)
+ declare = JspUtil.booleanValue(s);
+ } else if ("scope".equals(tname)) {
+ String s = element.getBody();
+ if (s != null) {
+ if ("NESTED".equals(s)) {
+ scope = VariableInfo.NESTED;
+ } else if ("AT_BEGIN".equals(s)) {
+ scope = VariableInfo.AT_BEGIN;
+ } else if ("AT_END".equals(s)) {
+ scope = VariableInfo.AT_END;
+ }
+ }
+ } else if ("description".equals(tname) || // Ignored elements
+ false) {
+ } else {
+ if (log.isWarnEnabled()) {
+ log.warn(Localizer.getMessage(
+ "jsp.warning.unknown.element.in.variable", tname));
+ }
+ }
+ }
+ return new TagVariableInfo(nameGiven, nameFromAttribute, className,
+ declare, scope);
+ }
+
+ private TagLibraryValidator createValidator(TreeNode elem)
+ throws JasperException {
+
+ String validatorClass = null;
+ Map initParams = new Hashtable();
+
+ Iterator list = elem.findChildren();
+ while (list.hasNext()) {
+ TreeNode element = (TreeNode) list.next();
+ String tname = element.getName();
+ if ("validator-class".equals(tname))
+ validatorClass = element.getBody();
+ else if ("init-param".equals(tname)) {
+ String[] initParam = createInitParam(element);
+ initParams.put(initParam[0], initParam[1]);
+ } else if ("description".equals(tname) || // Ignored elements
+ false) {
+ } else {
+ if (log.isWarnEnabled()) {
+ log.warn(Localizer.getMessage(
+ "jsp.warning.unknown.element.in.validator", tname));
+ }
+ }
+ }
+
+ TagLibraryValidator tlv = null;
+ if (validatorClass != null && !validatorClass.equals("")) {
+ try {
+ Class tlvClass = ctxt.getClassLoader()
+ .loadClass(validatorClass);
+ tlv = (TagLibraryValidator) tlvClass.newInstance();
+ } catch (Exception e) {
+ err.jspError("jsp.error.tlvclass.instantiation",
+ validatorClass, e);
+ }
+ }
+ if (tlv != null) {
+ tlv.setInitParameters(initParams);
+ }
+ return tlv;
+ }
+
+ String[] createInitParam(TreeNode elem) {
+ String[] initParam = new String[2];
+
+ Iterator list = elem.findChildren();
+ while (list.hasNext()) {
+ TreeNode element = (TreeNode) list.next();
+ String tname = element.getName();
+ if ("param-name".equals(tname)) {
+ initParam[0] = element.getBody();
+ } else if ("param-value".equals(tname)) {
+ initParam[1] = element.getBody();
+ } else if ("description".equals(tname)) {
+ // Do nothing
+ } else {
+ if (log.isWarnEnabled()) {
+ log.warn(Localizer.getMessage(
+ "jsp.warning.unknown.element.in.initParam", tname));
+ }
+ }
+ }
+ return initParam;
+ }
+
+ FunctionInfo createFunctionInfo(TreeNode elem) {
+
+ String name = null;
+ String klass = null;
+ String signature = null;
+
+ Iterator list = elem.findChildren();
+ while (list.hasNext()) {
+ TreeNode element = (TreeNode) list.next();
+ String tname = element.getName();
+
+ if ("name".equals(tname)) {
+ name = element.getBody();
+ } else if ("function-class".equals(tname)) {
+ klass = element.getBody();
+ } else if ("function-signature".equals(tname)) {
+ signature = element.getBody();
+ } else if ("display-name".equals(tname) || // Ignored elements
+ "small-icon".equals(tname) || "large-icon".equals(tname)
+ || "description".equals(tname) || "example".equals(tname)) {
+ } else {
+ if (log.isWarnEnabled()) {
+ log.warn(Localizer.getMessage(
+ "jsp.warning.unknown.element.in.function", tname));
+ }
+ }
+ }
+
+ return new FunctionInfo(name, klass, signature);
+ }
+
+ // *********************************************************************
+ // Until javax.servlet.jsp.tagext.TagLibraryInfo is fixed
+
+ /**
+ * The instance (if any) for the TagLibraryValidator class.
+ *
+ * @return The TagLibraryValidator instance, if any.
+ */
+ public TagLibraryValidator getTagLibraryValidator() {
+ return tagLibraryValidator;
+ }
+
+ /**
+ * Translation-time validation of the XML document associated with the JSP
+ * page. This is a convenience method on the associated TagLibraryValidator
+ * class.
+ *
+ * @param thePage
+ * The JSP page object
+ * @return A string indicating whether the page is valid or not.
+ */
+ public ValidationMessage[] validate(PageData thePage) {
+ TagLibraryValidator tlv = getTagLibraryValidator();
+ if (tlv == null)
+ return null;
+
+ String uri = getURI();
+ if (uri.startsWith("/")) {
+ uri = URN_JSPTLD + uri;
+ }
+
+ return tlv.validate(getPrefixString(), uri, thePage);
+ }
+
+ protected TagLibraryValidator tagLibraryValidator;
+}
Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/TagPluginManager.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/TagPluginManager.java?rev=819444&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/TagPluginManager.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/TagPluginManager.java Mon Sep 28 01:55:26 2009
@@ -0,0 +1,240 @@
+/*
+ * 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 java.util.*;
+import java.io.*;
+import javax.servlet.ServletContext;
+
+import org.apache.struts2.jasper.JasperException;
+import org.apache.struts2.jasper.xmlparser.ParserUtils;
+import org.apache.struts2.jasper.xmlparser.TreeNode;
+import org.apache.struts2.jasper.compiler.tagplugin.TagPlugin;
+import org.apache.struts2.jasper.compiler.tagplugin.TagPluginContext;
+
+/**
+ * Manages tag plugin optimizations.
+ * @author Kin-man Chung
+ */
+
+public class TagPluginManager {
+
+ private static final String TAG_PLUGINS_XML = "/WEB-INF/tagPlugins.xml";
+ private static final String TAG_PLUGINS_ROOT_ELEM = "tag-plugins";
+
+ private boolean initialized = false;
+ private HashMap tagPlugins = null;
+ private ServletContext ctxt;
+ private PageInfo pageInfo;
+
+ public TagPluginManager(ServletContext ctxt) {
+ this.ctxt = ctxt;
+ }
+
+ public void apply(Node.Nodes page, ErrorDispatcher err, PageInfo pageInfo)
+ throws JasperException {
+
+ init(err);
+ if (tagPlugins == null || tagPlugins.size() == 0) {
+ return;
+ }
+
+ this.pageInfo = pageInfo;
+
+ page.visit(new Node.Visitor() {
+ public void visit(Node.CustomTag n)
+ throws JasperException {
+ invokePlugin(n);
+ visitBody(n);
+ }
+ });
+
+ }
+
+ private void init(ErrorDispatcher err) throws JasperException {
+ if (initialized)
+ return;
+
+ InputStream is = ctxt.getResourceAsStream(TAG_PLUGINS_XML);
+ if (is == null)
+ return;
+
+ TreeNode root = (new ParserUtils()).parseXMLDocument(TAG_PLUGINS_XML,
+ is);
+ if (root == null) {
+ return;
+ }
+
+ if (!TAG_PLUGINS_ROOT_ELEM.equals(root.getName())) {
+ err.jspError("jsp.error.plugin.wrongRootElement", TAG_PLUGINS_XML,
+ TAG_PLUGINS_ROOT_ELEM);
+ }
+
+ tagPlugins = new HashMap();
+ Iterator pluginList = root.findChildren("tag-plugin");
+ while (pluginList.hasNext()) {
+ TreeNode pluginNode = (TreeNode) pluginList.next();
+ TreeNode tagClassNode = pluginNode.findChild("tag-class");
+ if (tagClassNode == null) {
+ // Error
+ return;
+ }
+ String tagClass = tagClassNode.getBody().trim();
+ TreeNode pluginClassNode = pluginNode.findChild("plugin-class");
+ if (pluginClassNode == null) {
+ // Error
+ return;
+ }
+
+ String pluginClassStr = pluginClassNode.getBody();
+ TagPlugin tagPlugin = null;
+ try {
+ Class pluginClass = Class.forName(pluginClassStr);
+ tagPlugin = (TagPlugin) pluginClass.newInstance();
+ } catch (Exception e) {
+ throw new JasperException(e);
+ }
+ if (tagPlugin == null) {
+ return;
+ }
+ tagPlugins.put(tagClass, tagPlugin);
+ }
+ initialized = true;
+ }
+
+ /**
+ * Invoke tag plugin for the given custom tag, if a plugin exists for
+ * the custom tag's tag handler.
+ *
+ * The given custom tag node will be manipulated by the plugin.
+ */
+ private void invokePlugin(Node.CustomTag n) {
+ TagPlugin tagPlugin = (TagPlugin)
+ tagPlugins.get(n.getTagHandlerClass().getName());
+ if (tagPlugin == null) {
+ return;
+ }
+
+ TagPluginContext tagPluginContext = new TagPluginContextImpl(n, pageInfo);
+ n.setTagPluginContext(tagPluginContext);
+ tagPlugin.doTag(tagPluginContext);
+ }
+
+ static class TagPluginContextImpl implements TagPluginContext {
+ private Node.CustomTag node;
+ private Node.Nodes curNodes;
+ private PageInfo pageInfo;
+ private HashMap pluginAttributes;
+
+ TagPluginContextImpl(Node.CustomTag n, PageInfo pageInfo) {
+ this.node = n;
+ this.pageInfo = pageInfo;
+ curNodes = new Node.Nodes();
+ n.setAtETag(curNodes);
+ curNodes = new Node.Nodes();
+ n.setAtSTag(curNodes);
+ n.setUseTagPlugin(true);
+ pluginAttributes = new HashMap();
+ }
+
+ public TagPluginContext getParentContext() {
+ Node parent = node.getParent();
+ if (! (parent instanceof Node.CustomTag)) {
+ return null;
+ }
+ return ((Node.CustomTag) parent).getTagPluginContext();
+ }
+
+ public void setPluginAttribute(String key, Object value) {
+ pluginAttributes.put(key, value);
+ }
+
+ public Object getPluginAttribute(String key) {
+ return pluginAttributes.get(key);
+ }
+
+ public boolean isScriptless() {
+ return node.getChildInfo().isScriptless();
+ }
+
+ public boolean isConstantAttribute(String attribute) {
+ Node.JspAttribute attr = getNodeAttribute(attribute);
+ if (attr == null)
+ return false;
+ return attr.isLiteral();
+ }
+
+ public String getConstantAttribute(String attribute) {
+ Node.JspAttribute attr = getNodeAttribute(attribute);
+ if (attr == null)
+ return null;
+ return attr.getValue();
+ }
+
+ public boolean isAttributeSpecified(String attribute) {
+ return getNodeAttribute(attribute) != null;
+ }
+
+ public String getTemporaryVariableName() {
+ return node.getRoot().nextTemporaryVariableName();
+ }
+
+ public void generateImport(String imp) {
+ pageInfo.addImport(imp);
+ }
+
+ public void generateDeclaration(String id, String text) {
+ if (pageInfo.isPluginDeclared(id)) {
+ return;
+ }
+ curNodes.add(new Node.Declaration(text, node.getStart(), null));
+ }
+
+ public void generateJavaSource(String sourceCode) {
+ curNodes.add(new Node.Scriptlet(sourceCode, node.getStart(),
+ null));
+ }
+
+ public void generateAttribute(String attributeName) {
+ curNodes.add(new Node.AttributeGenerator(node.getStart(),
+ attributeName,
+ node));
+ }
+
+ public void dontUseTagPlugin() {
+ node.setUseTagPlugin(false);
+ }
+
+ public void generateBody() {
+ // Since we'll generate the body anyway, this is really a nop,
+ // except for the fact that it lets us put the Java sources the
+ // plugins produce in the correct order (w.r.t the body).
+ curNodes = node.getAtETag();
+ }
+
+ private Node.JspAttribute getNodeAttribute(String attribute) {
+ Node.JspAttribute[] attrs = node.getJspAttributes();
+ for (int i=0; attrs != null && i < attrs.length; i++) {
+ if (attrs[i].getName().equals(attribute)) {
+ return attrs[i];
+ }
+ }
+ return null;
+ }
+ }
+}
Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/TextOptimizer.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/TextOptimizer.java?rev=819444&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/TextOptimizer.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/TextOptimizer.java Mon Sep 28 01:55:26 2009
@@ -0,0 +1,115 @@
+/*
+ * 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.JasperException;
+import org.apache.struts2.jasper.Options;
+
+/**
+ */
+public class TextOptimizer {
+
+ /**
+ * A visitor to concatenate contiguous template texts.
+ */
+ static class TextCatVisitor extends Node.Visitor {
+
+ private Options options;
+ private PageInfo pageInfo;
+ private int textNodeCount = 0;
+ private Node.TemplateText firstTextNode = null;
+ private StringBuffer textBuffer;
+ private final String emptyText = new String("");
+
+ public TextCatVisitor(Compiler compiler) {
+ options = compiler.getCompilationContext().getOptions();
+ pageInfo = compiler.getPageInfo();
+ }
+
+ public void doVisit(Node n) throws JasperException {
+ collectText();
+ }
+
+ /*
+ * The following directis are ignored in text concatenation
+ */
+
+ public void visit(Node.PageDirective n) throws JasperException {
+ }
+
+ public void visit(Node.TagDirective n) throws JasperException {
+ }
+
+ public void visit(Node.TaglibDirective n) throws JasperException {
+ }
+
+ public void visit(Node.AttributeDirective n) throws JasperException {
+ }
+
+ public void visit(Node.VariableDirective n) throws JasperException {
+ }
+
+ /*
+ * Don't concatenate text across body boundaries
+ */
+ public void visitBody(Node n) throws JasperException {
+ super.visitBody(n);
+ collectText();
+ }
+
+ public void visit(Node.TemplateText n) throws JasperException {
+ if ((options.getTrimSpaces() || pageInfo.isTrimDirectiveWhitespaces())
+ && n.isAllSpace()) {
+ n.setText(emptyText);
+ return;
+ }
+
+ if (textNodeCount++ == 0) {
+ firstTextNode = n;
+ textBuffer = new StringBuffer(n.getText());
+ } else {
+ // Append text to text buffer
+ textBuffer.append(n.getText());
+ n.setText(emptyText);
+ }
+ }
+
+ /**
+ * This method breaks concatenation mode. As a side effect it copies
+ * the concatenated string to the first text node
+ */
+ private void collectText() {
+
+ if (textNodeCount > 1) {
+ // Copy the text in buffer into the first template text node.
+ firstTextNode.setText(textBuffer.toString());
+ }
+ textNodeCount = 0;
+ }
+
+ }
+
+ public static void concatenate(Compiler compiler, Node.Nodes page)
+ throws JasperException {
+
+ TextCatVisitor v = new TextCatVisitor(compiler);
+ page.visit(v);
+
+ // Cleanup, in case the page ends with a template text
+ v.collectText();
+ }
+}