struts-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From musa...@apache.org
Subject svn commit: r800614 [11/24] - in /struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2: ./ jasper/ jasper/compiler/ jasper/compiler/tagplugin/ jasper/resources/ jasper/runtime/ jasper/security/ jasper/servlet/ jasper/tagplugins/ jas...
Date Mon, 03 Aug 2009 23:16:53 GMT
Added: struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/TagFileProcessor.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/TagFileProcessor.java?rev=800614&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/TagFileProcessor.java (added)
+++ struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/TagFileProcessor.java Mon Aug  3 23:16:50 2009
@@ -0,0 +1,609 @@
+/*
+ * 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.URL;
+import java.net.URLClassLoader;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+import java.util.HashMap;
+
+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("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("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);
+
+            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";
+            }
+
+            TagAttributeInfo tagAttributeInfo =
+                    new TagAttributeInfo(attrName, required, type, rtexprvalue,
+                                         fragment);
+            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, 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.
+     */
+    public static TagInfo parseTagFileDirectives(ParserController pc,
+						 String name,
+						 String path,
+						 TagLibraryInfo tagLibInfo)
+                        throws JasperException {
+
+        ErrorDispatcher err = pc.getCompiler().getErrorDispatcher();
+
+        Node.Nodes page = null;
+        try {
+            page = pc.parseTagFileDirectives(path);
+        } 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 {
+
+        JspCompilationContext ctxt = compiler.getCompilationContext();
+        JspRuntimeContext rctxt = ctxt.getRuntimeContext();
+        JspServletWrapper wrapper =
+                (JspServletWrapper) rctxt.getWrapper(tagFilePath);
+
+        synchronized(rctxt) {
+            if (wrapper == null) {
+                wrapper = new JspServletWrapper(ctxt.getServletContext(),
+                                                ctxt.getOptions(),
+                                                tagFilePath,
+                                                tagInfo,
+                                                ctxt.getRuntimeContext(),
+                                                (URL) ctxt.getTagFileJarUrls().get(tagFilePath));
+                    rctxt.addWrapper(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(),
+                                                (URL) ctxt.getTagFileJarUrls().get(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();
+		JspCompilationContext ctxt = compiler.getCompilationContext();
+		if (ctxt.getTagFileJarUrls().get(tagFilePath) == null) {
+		    // Omit tag file dependency info on jar files for now.
+                    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/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/TagLibraryInfoImpl.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/TagLibraryInfoImpl.java?rev=800614&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/TagLibraryInfoImpl.java (added)
+++ struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/TagLibraryInfoImpl.java Mon Aug  3 23:16:50 2009
@@ -0,0 +1,764 @@
+/*
+ * 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.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.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+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;
+
+/**
+ * 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 Hashtable jarEntries;
+    private JspCompilationContext ctxt;
+    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,
+                              String prefix, 
+                              String uriIn,
+                              String[] location,
+                              ErrorDispatcher err) throws JasperException {
+        super(prefix, uriIn);
+
+        this.ctxt = ctxt;
+        this.parserController = pc;
+        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) {}
+            }
+        }
+
+    }
+    
+    /*
+     * @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) {
+            //TODO: implement for tld
+            //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
+            // check if jarFileUrl is not null before adding it, or this will
+            // cause a NPE.
+            if (jarFileUrl != null) {
+                ctxt.getTagFileJarUrls().put(path, jarFileUrl);
+            }
+        } else if (!path.startsWith("/WEB-INF/tags")) {
+            err.jspError("jsp.error.tagfile.illegalPath", path);
+        }
+
+        TagInfo tagInfo
+            = TagFileProcessor.parseTagFileDirectives(parserController, name,
+                                                      path, this);
+        return new TagFileInfo(name, path, tagInfo);
+    }
+
+    TagAttributeInfo createAttribute(TreeNode elem, String jspVersion) {
+        String name = null;
+        String type = null;
+        boolean required = false, rtexprvalue = false, reqTime = false,
+            isFragment = 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 ("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) {
+            // 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);
+    }
+
+    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/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/TagPluginManager.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/TagPluginManager.java?rev=800614&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/TagPluginManager.java (added)
+++ struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/TagPluginManager.java Mon Aug  3 23:16:50 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 JspUtil.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/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/TextOptimizer.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/TextOptimizer.java?rev=800614&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/TextOptimizer.java (added)
+++ struts/sandbox/trunk/struts2-jsp-plugin/src/main/java/org/apache/struts2/jasper/compiler/TextOptimizer.java Mon Aug  3 23:16:50 2009
@@ -0,0 +1,113 @@
+/*
+ * 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 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();
+        }
+
+        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() && 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();
+    }
+}



Mime
View raw message