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 [7/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/JspCompilationContext.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/JspCompilationContext.java?rev=819444&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/JspCompilationContext.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/JspCompilationContext.java Mon Sep 28 01:55:26 2009
@@ -0,0 +1,719 @@
+/*
+ * 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;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.ServletContext;
+import javax.servlet.jsp.tagext.TagInfo;
+
+import org.apache.struts2.jasper.compiler.Compiler;
+import org.apache.struts2.jasper.compiler.JspRuntimeContext;
+import org.apache.struts2.jasper.compiler.JspUtil;
+import org.apache.struts2.jasper.compiler.Localizer;
+import org.apache.struts2.jasper.compiler.ServletWriter;
+import org.apache.struts2.jasper.servlet.JasperLoader;
+import org.apache.struts2.jasper.servlet.JspServletWrapper;
+import org.apache.commons.lang.xwork.StringUtils;
+import com.opensymphony.xwork2.util.finder.ClassLoaderInterface;
+
+/**
+ * A place holder for various things that are used through out the JSP
+ * engine. This is a per-request/per-context data structure. Some of
+ * the instance variables are set at different points.
+ *
+ * Most of the path-related stuff is here - mangling names, versions, dirs,
+ * loading resources and dealing with uris.
+ *
+ * @author Anil K. Vijendran
+ * @author Harish Prabandham
+ * @author Pierre Delisle
+ * @author Costin Manolache
+ * @author Kin-man Chung
+ */
+public class JspCompilationContext {
+
+ protected org.apache.juli.logging.Log log =
+ org.apache.juli.logging.LogFactory.getLog(JspCompilationContext.class);
+
+ protected Map<String, URL> tagFileJarUrls;
+ protected boolean isPackagedTagFile;
+
+ protected String className;
+ protected String jspUri;
+ protected boolean isErrPage;
+ protected String basePackageName;
+ protected String derivedPackageName;
+ protected String servletJavaFileName;
+ protected String javaPath;
+ protected String classFileName;
+ protected String contentType;
+ protected ServletWriter writer;
+ protected Options options;
+ protected JspServletWrapper jsw;
+ protected Compiler jspCompiler;
+ protected String classPath;
+
+ protected String baseURI;
+ protected String outputDir;
+ protected ServletContext context;
+ protected URLClassLoader loader;
+
+ protected JspRuntimeContext rctxt;
+
+ protected int removed = 0;
+
+ protected URLClassLoader jspLoader;
+ protected URL baseUrl;
+ protected Class servletClass;
+
+ protected boolean isTagFile;
+ protected boolean protoTypeMode;
+ protected TagInfo tagInfo;
+ protected URL tagFileJarUrl;
+ private ClassLoaderInterface classLoaderInterface;
+ private String sourceCode;
+
+ // jspURI _must_ be relative to the context
+ public JspCompilationContext(String jspUri,
+ boolean isErrPage,
+ Options options,
+ ServletContext context,
+ JspServletWrapper jsw,
+ JspRuntimeContext rctxt,
+ ClassLoaderInterface classLoaderInterface) {
+
+ this.jspUri = canonicalURI(jspUri);
+ this.isErrPage = isErrPage;
+ this.options = options;
+ this.jsw = jsw;
+ this.context = context;
+
+ this.baseURI = jspUri.substring(0, jspUri.lastIndexOf('/') + 1);
+ // hack fix for resolveRelativeURI
+ if (baseURI == null) {
+ baseURI = "/";
+ } else if (baseURI.charAt(0) != '/') {
+ // strip the basde slash since it will be combined with the
+ // uriBase to generate a file
+ baseURI = "/" + baseURI;
+ }
+ if (baseURI.charAt(baseURI.length() - 1) != '/') {
+ baseURI += '/';
+ }
+
+ this.rctxt = rctxt;
+ this.tagFileJarUrls = new HashMap<String, URL>();
+ this.basePackageName = Constants.JSP_PACKAGE_NAME;
+ this.classLoaderInterface = classLoaderInterface;
+ }
+
+ public JspCompilationContext(String tagfile,
+ TagInfo tagInfo,
+ Options options,
+ ServletContext context,
+ JspServletWrapper jsw,
+ JspRuntimeContext rctxt,
+ URL tagFileJarUrl) {
+ this(tagfile, false, options, context, jsw, rctxt, null);
+ this.isTagFile = true;
+ this.tagInfo = tagInfo;
+ this.tagFileJarUrl = tagFileJarUrl;
+ if (tagFileJarUrl != null) {
+ isPackagedTagFile = true;
+ }
+ }
+
+ /* ==================== Methods to override ==================== */
+
+ /** ---------- Class path and loader ---------- */
+
+ /**
+ * The classpath that is passed off to the Java compiler.
+ */
+ public String getClassPath() {
+ if( classPath != null )
+ return classPath;
+ return rctxt.getClassPath();
+ }
+
+ /**
+ * The classpath that is passed off to the Java compiler.
+ */
+ public void setClassPath(String classPath) {
+ this.classPath = classPath;
+ }
+
+ /**
+ * What class loader to use for loading classes while compiling
+ * this JSP?
+ */
+ public ClassLoader getClassLoader() {
+ if( loader != null )
+ return loader;
+ return rctxt.getParentClassLoader();
+ }
+
+ public void setClassLoader(URLClassLoader loader) {
+ this.loader = loader;
+ }
+
+ public ClassLoader getJspLoader() {
+ if( jspLoader == null ) {
+ jspLoader = new JasperLoader
+ (new URL[] {baseUrl},
+ getClassLoader(),
+ rctxt.getPermissionCollection(),
+ rctxt.getCodeSource());
+ }
+ return jspLoader;
+ }
+
+ /** ---------- Input/Output ---------- */
+
+ /**
+ * The output directory to generate code into. The output directory
+ * is make up of the scratch directory, which is provide in Options,
+ * plus the directory derived from the package name.
+ */
+ public String getOutputDir() {
+ if (outputDir == null) {
+ createOutputDir();
+ }
+
+ return outputDir;
+ }
+
+ /**
+ * Create a "Compiler" object based on some init param data. This
+ * is not done yet. Right now we're just hardcoding the actual
+ * compilers that are created.
+ */
+ public Compiler createCompiler() throws JasperException {
+ jspCompiler = new CustomCompiler();
+ jspCompiler.init(this, jsw);
+ return jspCompiler;
+ }
+
+ protected Compiler createCompiler(String className) {
+ Compiler compiler = null;
+ try {
+ compiler = (Compiler) Class.forName(className).newInstance();
+ } catch (InstantiationException e) {
+ log.warn(Localizer.getMessage("jsp.error.compiler"), e);
+ } catch (IllegalAccessException e) {
+ log.warn(Localizer.getMessage("jsp.error.compiler"), e);
+ } catch (NoClassDefFoundError e) {
+ if (log.isDebugEnabled()) {
+ log.debug(Localizer.getMessage("jsp.error.compiler"), e);
+ }
+ } catch (ClassNotFoundException e) {
+ if (log.isDebugEnabled()) {
+ log.debug(Localizer.getMessage("jsp.error.compiler"), e);
+ }
+ }
+ return compiler;
+ }
+
+ public Compiler getCompiler() {
+ return jspCompiler;
+ }
+
+ /** ---------- Access resources in the webapp ---------- */
+
+ /**
+ * Get the full value of a URI relative to this compilations context
+ * uses current file as the base.
+ */
+ public String resolveRelativeUri(String uri) {
+ // sometimes we get uri's massaged from File(String), so check for
+ // a root directory deperator char
+ if (uri.startsWith("/") || uri.startsWith(File.separator)) {
+ return uri;
+ } else {
+ return baseURI + uri;
+ }
+ }
+
+ /**
+ * Gets a resource as a stream, relative to the meanings of this
+ * context's implementation.
+ * @return a null if the resource cannot be found or represented
+ * as an InputStream.
+ */
+ public java.io.InputStream getResourceAsStream(String res) {
+ try {
+ return classLoaderInterface.getResourceAsStream(canonicalURI(StringUtils.removeStart(res, "/")));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+
+ public URL getResource(String res) throws MalformedURLException {
+ return classLoaderInterface.getResource(canonicalURI(StringUtils.removeStart(res, "/")));
+ }
+
+
+ public Set getResourcePaths(String path) {
+ return context.getResourcePaths(canonicalURI(path));
+ }
+
+ /**
+ * Gets the actual path of a URI relative to the context of
+ * the compilation.
+ */
+ public String getRealPath(String path) {
+ if (context != null) {
+ return context.getRealPath(path);
+ }
+ return path;
+ }
+
+ /**
+ * Returns the tag-file-name-to-JAR-file map of this compilation unit,
+ * which maps tag file names to the JAR files in which the tag files are
+ * packaged.
+ *
+ * The map is populated when parsing the tag-file elements of the TLDs
+ * of any imported taglibs.
+ */
+ public URL getTagFileJarUrl(String tagFile) {
+ return this.tagFileJarUrls.get(tagFile);
+ }
+
+ public void setTagFileJarUrl(String tagFile, URL tagFileURL) {
+ this.tagFileJarUrls.put(tagFile, tagFileURL);
+ }
+
+ /**
+ * Returns the JAR file in which the tag file for which this
+ * JspCompilationContext was created is packaged, or null if this
+ * JspCompilationContext does not correspond to a tag file, or if the
+ * corresponding tag file is not packaged in a JAR.
+ */
+ public URL getTagFileJarUrl() {
+ return this.tagFileJarUrl;
+ }
+
+ /* ==================== Common implementation ==================== */
+
+ /**
+ * Just the class name (does not include package name) of the
+ * generated class.
+ */
+ public String getServletClassName() {
+
+ if (className != null) {
+ return className;
+ }
+
+ if (isTagFile) {
+ className = tagInfo.getTagClassName();
+ int lastIndex = className.lastIndexOf('.');
+ if (lastIndex != -1) {
+ className = className.substring(lastIndex + 1);
+ }
+ } else {
+ int iSep = jspUri.lastIndexOf('/') + 1;
+ className = JspUtil.makeJavaIdentifier(jspUri.substring(iSep));
+ }
+ return className;
+ }
+
+ public void setServletClassName(String className) {
+ this.className = className;
+ }
+
+ /**
+ * Path of the JSP URI. Note that this is not a file name. This is
+ * the context rooted URI of the JSP file.
+ */
+ public String getJspFile() {
+ return jspUri;
+ }
+
+ /**
+ * Are we processing something that has been declared as an
+ * errorpage?
+ */
+ public boolean isErrorPage() {
+ return isErrPage;
+ }
+
+ public void setErrorPage(boolean isErrPage) {
+ this.isErrPage = isErrPage;
+ }
+
+ public boolean isTagFile() {
+ return isTagFile;
+ }
+
+ public TagInfo getTagInfo() {
+ return tagInfo;
+ }
+
+ public void setTagInfo(TagInfo tagi) {
+ tagInfo = tagi;
+ }
+
+ /**
+ * True if we are compiling a tag file in prototype mode.
+ * ie we only generate codes with class for the tag handler with empty
+ * method bodies.
+ */
+ public boolean isPrototypeMode() {
+ return protoTypeMode;
+ }
+
+ public void setPrototypeMode(boolean pm) {
+ protoTypeMode = pm;
+ }
+
+ /**
+ * Package name for the generated class is make up of the base package
+ * name, which is user settable, and the derived package name. The
+ * derived package name directly mirrors the file heirachy of the JSP page.
+ */
+ public String getServletPackageName() {
+ if (isTagFile()) {
+ String className = tagInfo.getTagClassName();
+ int lastIndex = className.lastIndexOf('.');
+ String pkgName = "";
+ if (lastIndex != -1) {
+ pkgName = className.substring(0, lastIndex);
+ }
+ return pkgName;
+ } else {
+ String dPackageName = getDerivedPackageName();
+ if (dPackageName.length() == 0) {
+ return basePackageName;
+ }
+ return basePackageName + '.' + getDerivedPackageName();
+ }
+ }
+
+ protected String getDerivedPackageName() {
+ if (derivedPackageName == null) {
+ int iSep = jspUri.lastIndexOf('/');
+ derivedPackageName = (iSep > 0) ?
+ JspUtil.makeJavaPackage(jspUri.substring(1,iSep)) : "";
+ }
+ return derivedPackageName;
+ }
+
+ /**
+ * The package name into which the servlet class is generated.
+ */
+ public void setServletPackageName(String servletPackageName) {
+ this.basePackageName = servletPackageName;
+ }
+
+ /**
+ * Full path name of the Java file into which the servlet is being
+ * generated.
+ */
+ public String getServletJavaFileName() {
+ if (servletJavaFileName == null) {
+ servletJavaFileName = getOutputDir() + getServletClassName() + ".java";
+ }
+ return servletJavaFileName;
+ }
+
+ /**
+ * Get hold of the Options object for this context.
+ */
+ public Options getOptions() {
+ return options;
+ }
+
+ public ServletContext getServletContext() {
+ return context;
+ }
+
+ public JspRuntimeContext getRuntimeContext() {
+ return rctxt;
+ }
+
+ /**
+ * Path of the Java file relative to the work directory.
+ */
+ public String getJavaPath() {
+
+ if (javaPath != null) {
+ return javaPath;
+ }
+
+ if (isTagFile()) {
+ String tagName = tagInfo.getTagClassName();
+ javaPath = tagName.replace('.', '/') + ".java";
+ } else {
+ javaPath = getServletPackageName().replace('.', '/') + '/' +
+ getServletClassName() + ".java";
+ }
+ return javaPath;
+ }
+
+ public String getClassFileName() {
+ if (classFileName == null) {
+ classFileName = getOutputDir() + getServletClassName() + ".class";
+ }
+ return classFileName;
+ }
+
+ /**
+ * Get the content type of this JSP.
+ *
+ * Content type includes content type and encoding.
+ */
+ public String getContentType() {
+ return contentType;
+ }
+
+ public void setContentType(String contentType) {
+ this.contentType = contentType;
+ }
+
+ /**
+ * Where is the servlet being generated?
+ */
+ public ServletWriter getWriter() {
+ return writer;
+ }
+
+ public void setWriter(ServletWriter writer) {
+ this.writer = writer;
+ }
+
+ /**
+ * Gets the 'location' of the TLD associated with the given taglib 'uri'.
+ *
+ * @return An array of two Strings: The first element denotes the real
+ * 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.
+ * Returns null if the given uri is not associated with any tag library
+ * 'exposed' in the web application.
+ */
+ public String[] getTldLocation(String uri) throws JasperException {
+ String[] location =
+ getOptions().getTldLocationsCache().getLocation(uri);
+ return location;
+ }
+
+ /**
+ * Are we keeping generated code around?
+ */
+ public boolean keepGenerated() {
+ return getOptions().getKeepGenerated();
+ }
+
+ // ==================== Removal ====================
+
+ public void incrementRemoved() {
+ if (removed == 0 && rctxt != null) {
+ rctxt.removeWrapper(jspUri);
+ }
+ removed++;
+ }
+
+ public boolean isRemoved() {
+ if (removed > 1 ) {
+ return true;
+ }
+ return false;
+ }
+
+ // ==================== Compile and reload ====================
+
+ public void compile() throws JasperException, FileNotFoundException {
+ createCompiler();
+ if (jspCompiler.isOutDated()) {
+ try {
+ jspCompiler.removeGeneratedFiles();
+ jspLoader = null;
+ jspCompiler.compile();
+ jsw.setReload(true);
+ jsw.setCompilationException(null);
+ } catch (JasperException ex) {
+ // Cache compilation exception
+ jsw.setCompilationException(ex);
+ throw ex;
+ } catch (Exception ex) {
+ JasperException je = new JasperException(
+ Localizer.getMessage("jsp.error.unable.compile"),
+ ex);
+ // Cache compilation exception
+ jsw.setCompilationException(je);
+ throw je;
+ }
+ }
+ }
+
+ // ==================== Manipulating the class ====================
+
+ public Class load()
+ throws JasperException, FileNotFoundException
+ {
+ try {
+ getJspLoader();
+
+ String name;
+ if (isTagFile()) {
+ name = tagInfo.getTagClassName();
+ } else {
+ name = getServletPackageName() + "." + getServletClassName();
+ }
+ servletClass = jspLoader.loadClass(name);
+ } catch (ClassNotFoundException cex) {
+ throw new JasperException(Localizer.getMessage("jsp.error.unable.load"),
+ cex);
+ } catch (Exception ex) {
+ throw new JasperException(Localizer.getMessage("jsp.error.unable.compile"),
+ ex);
+ }
+ removed = 0;
+ return servletClass;
+ }
+
+ // ==================== protected methods ====================
+
+ static Object outputDirLock = new Object();
+
+ public void checkOutputDir() {
+ if (outputDir != null) {
+ if (!(new File(outputDir)).exists()) {
+ makeOutputDir();
+ }
+ } else {
+ createOutputDir();
+ }
+ }
+
+ protected boolean makeOutputDir() {
+ synchronized(outputDirLock) {
+ File outDirFile = new File(outputDir);
+ return (outDirFile.exists() || outDirFile.mkdirs());
+ }
+ }
+
+ protected void createOutputDir() {
+ String path = null;
+ if (isTagFile()) {
+ String tagName = tagInfo.getTagClassName();
+ path = tagName.replace('.', File.separatorChar);
+ path = path.substring(0, path.lastIndexOf(File.separatorChar));
+ } else {
+ path = getServletPackageName().replace('.',File.separatorChar);
+ }
+
+ // Append servlet or tag handler path to scratch dir
+ try {
+ File base = options.getScratchDir();
+ baseUrl = base.toURI().toURL();
+ outputDir = base.getAbsolutePath() + File.separator + path +
+ File.separator;
+ if (!makeOutputDir()) {
+ throw new IllegalStateException(Localizer.getMessage("jsp.error.outputfolder"));
+ }
+ } catch (MalformedURLException e) {
+ throw new IllegalStateException(Localizer.getMessage("jsp.error.outputfolder"), e);
+ }
+ }
+
+ protected static final boolean isPathSeparator(char c) {
+ return (c == '/' || c == '\\');
+ }
+
+ protected static final String canonicalURI(String s) {
+ if (s == null) return null;
+ StringBuffer result = new StringBuffer();
+ final int len = s.length();
+ int pos = 0;
+ while (pos < len) {
+ char c = s.charAt(pos);
+ if ( isPathSeparator(c) ) {
+ /*
+ * multiple path separators.
+ * 'foo///bar' -> 'foo/bar'
+ */
+ while (pos+1 < len && isPathSeparator(s.charAt(pos+1))) {
+ ++pos;
+ }
+
+ if (pos+1 < len && s.charAt(pos+1) == '.') {
+ /*
+ * a single dot at the end of the path - we are done.
+ */
+ if (pos+2 >= len) break;
+
+ switch (s.charAt(pos+2)) {
+ /*
+ * self directory in path
+ * foo/./bar -> foo/bar
+ */
+ case '/':
+ case '\\':
+ pos += 2;
+ continue;
+
+ /*
+ * two dots in a path: go back one hierarchy.
+ * foo/bar/../baz -> foo/baz
+ */
+ case '.':
+ // only if we have exactly _two_ dots.
+ if (pos+3 < len && isPathSeparator(s.charAt(pos+3))) {
+ pos += 3;
+ int separatorPos = result.length()-1;
+ while (separatorPos >= 0 &&
+ ! isPathSeparator(result
+ .charAt(separatorPos))) {
+ --separatorPos;
+ }
+ if (separatorPos >= 0)
+ result.setLength(separatorPos);
+ continue;
+ }
+ }
+ }
+ }
+ result.append(c);
+ ++pos;
+ }
+ return result.toString();
+ }
+
+ public String getSourceCode() {
+ return sourceCode;
+ }
+
+ public void setSourceCode(String sourceCode) {
+ this.sourceCode = sourceCode;
+ }
+
+}
+
Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/Options.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/Options.java?rev=819444&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/Options.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/Options.java Mon Sep 28 01:55:26 2009
@@ -0,0 +1,202 @@
+/*
+ * 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;
+
+import java.io.File;
+import java.util.Map;
+
+import org.apache.struts2.jasper.compiler.JspConfig;
+import org.apache.struts2.jasper.compiler.TagPluginManager;
+import org.apache.struts2.jasper.compiler.TldLocationsCache;
+
+/**
+ * A class to hold all init parameters specific to the JSP engine.
+ *
+ * @author Anil K. Vijendran
+ * @author Hans Bergsten
+ * @author Pierre Delisle
+ */
+public interface Options {
+
+ /**
+ * Returns true if Jasper issues a compilation error instead of a runtime
+ * Instantiation error if the class attribute specified in useBean action
+ * is invalid.
+ */
+ public boolean getErrorOnUseBeanInvalidClassAttribute();
+
+ /**
+ * Are we keeping generated code around?
+ */
+ public boolean getKeepGenerated();
+
+ /**
+ * Returns true if tag handler pooling is enabled, false otherwise.
+ */
+ public boolean isPoolingEnabled();
+
+ /**
+ * Are we supporting HTML mapped servlets?
+ */
+ public boolean getMappedFile();
+
+ /**
+ * Should errors be sent to client or thrown into stderr?
+ * @deprecated
+ */
+ @Deprecated
+ public boolean getSendErrorToClient();
+
+ /**
+ * Should we include debug information in compiled class?
+ */
+ public boolean getClassDebugInfo();
+
+ /**
+ * Background compile thread check interval in seconds
+ */
+ public int getCheckInterval();
+
+ /**
+ * Is Jasper being used in development mode?
+ */
+ public boolean getDevelopment();
+
+ /**
+ * Should we include a source fragment in exception messages, which could be displayed
+ * to the developer ?
+ */
+ public boolean getDisplaySourceFragment();
+
+ /**
+ * Is the generation of SMAP info for JSR45 debugging suppressed?
+ */
+ public boolean isSmapSuppressed();
+
+ /**
+ * Indicates whether SMAP info for JSR45 debugging should be dumped to a
+ * file.
+ * Ignored is suppressSmap() is true
+ */
+ public boolean isSmapDumped();
+
+ /**
+ * Should white spaces between directives or actions be trimmed?
+ */
+ public boolean getTrimSpaces();
+
+ /**
+ * Class ID for use in the plugin tag when the browser is IE.
+ */
+ public String getIeClassId();
+
+ /**
+ * What is my scratch dir?
+ */
+ public File getScratchDir();
+
+ /**
+ * What classpath should I use while compiling the servlets
+ * generated from JSP files?
+ */
+ public String getClassPath();
+
+ /**
+ * Compiler to use.
+ */
+ public String getCompiler();
+
+ /**
+ * The compiler target VM, e.g. 1.1, 1.2, 1.3, 1.4, or 1.5.
+ */
+ public String getCompilerTargetVM();
+
+ /**
+ * Compiler source VM, e.g. 1.3, 1.4, or 1.5.
+ */
+ public String getCompilerSourceVM();
+
+ /**
+ * Java compiler class to use.
+ */
+ public String getCompilerClassName();
+
+ /**
+ * The cache for the location of the TLD's
+ * for the various tag libraries 'exposed'
+ * by the web application.
+ * A tag library is 'exposed' either explicitely in
+ * web.xml or implicitely via the uri tag in the TLD
+ * of a taglib deployed in a jar file (WEB-INF/lib).
+ *
+ * @return the instance of the TldLocationsCache
+ * for the web-application.
+ */
+ public TldLocationsCache getTldLocationsCache();
+
+ /**
+ * Java platform encoding to generate the JSP
+ * page servlet.
+ */
+ public String getJavaEncoding();
+
+ /**
+ * boolean flag to tell Ant whether to fork JSP page compilations.
+ */
+ public boolean getFork();
+
+ /**
+ * Obtain JSP configuration informantion specified in web.xml.
+ */
+ public JspConfig getJspConfig();
+
+ /**
+ * Is generation of X-Powered-By response header enabled/disabled?
+ */
+ public boolean isXpoweredBy();
+
+ /**
+ * Obtain a Tag Plugin Manager
+ */
+ public TagPluginManager getTagPluginManager();
+
+ /**
+ * Are Text strings to be generated as char arrays?
+ */
+ public boolean genStringAsCharArray();
+
+ /**
+ * Modification test interval.
+ */
+ public int getModificationTestInterval();
+
+ /**
+ * Is caching enabled (used for precompilation).
+ */
+ public boolean isCaching();
+
+ /**
+ * The web-application wide cache for the returned TreeNode
+ * by parseXMLDocument in TagLibraryInfoImpl.parseTLD,
+ * if isCaching returns true.
+ *
+ * @return the Map(String uri, TreeNode tld) instance.
+ */
+ public Map getCache();
+
+}
Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/BeanRepository.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/BeanRepository.java?rev=819444&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/BeanRepository.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/BeanRepository.java Mon Sep 28 01:55:26 2009
@@ -0,0 +1,75 @@
+/*
+ * 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.HashMap;
+
+import org.apache.struts2.jasper.JasperException;
+
+/**
+ * Repository of {page, request, session, application}-scoped beans
+ *
+ * @author Mandar Raje
+ * @author Remy Maucherat
+ */
+public class BeanRepository {
+
+ protected HashMap<String, String> beanTypes;
+ protected ClassLoader loader;
+ protected ErrorDispatcher errDispatcher;
+
+ /**
+ * Constructor.
+ */
+ public BeanRepository(ClassLoader loader, ErrorDispatcher err) {
+ this.loader = loader;
+ this.errDispatcher = err;
+ beanTypes = new HashMap<String, String>();
+ }
+
+ public void addBean(Node.UseBean n, String s, String type, String scope)
+ throws JasperException {
+
+ if (!(scope == null || scope.equals("page") || scope.equals("request")
+ || scope.equals("session") || scope.equals("application"))) {
+ errDispatcher.jspError(n, "jsp.error.usebean.badScope");
+ }
+
+ beanTypes.put(s, type);
+ }
+
+ public Class getBeanType(String bean)
+ throws JasperException {
+ Class clazz = null;
+ try {
+ clazz = loader.loadClass(beanTypes.get(bean));
+ } catch (ClassNotFoundException ex) {
+ throw new JasperException (ex);
+ }
+ return clazz;
+ }
+
+ public boolean checkVariable(String bean) {
+ return beanTypes.containsKey(bean);
+ }
+
+}
+
+
+
+
Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/Collector.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/Collector.java?rev=819444&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/Collector.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/Collector.java Mon Sep 28 01:55:26 2009
@@ -0,0 +1,204 @@
+/*
+ * 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;
+
+/**
+ * Collect info about the page and nodes, and make them availabe through
+ * the PageInfo object.
+ *
+ * @author Kin-man Chung
+ * @author Mark Roth
+ */
+
+class Collector {
+
+ /**
+ * A visitor for collecting information on the page and the body of
+ * the custom tags.
+ */
+ static class CollectVisitor extends Node.Visitor {
+
+ private boolean scriptingElementSeen = false;
+ private boolean usebeanSeen = false;
+ private boolean includeActionSeen = false;
+ private boolean paramActionSeen = false;
+ private boolean setPropertySeen = false;
+ private boolean hasScriptingVars = false;
+
+ public void visit(Node.ParamAction n) throws JasperException {
+ if (n.getValue().isExpression()) {
+ scriptingElementSeen = true;
+ }
+ paramActionSeen = true;
+ }
+
+ public void visit(Node.IncludeAction n) throws JasperException {
+ if (n.getPage().isExpression()) {
+ scriptingElementSeen = true;
+ }
+ includeActionSeen = true;
+ visitBody(n);
+ }
+
+ public void visit(Node.ForwardAction n) throws JasperException {
+ if (n.getPage().isExpression()) {
+ scriptingElementSeen = true;
+ }
+ visitBody(n);
+ }
+
+ public void visit(Node.SetProperty n) throws JasperException {
+ if (n.getValue() != null && n.getValue().isExpression()) {
+ scriptingElementSeen = true;
+ }
+ setPropertySeen = true;
+ }
+
+ public void visit(Node.UseBean n) throws JasperException {
+ if (n.getBeanName() != null && n.getBeanName().isExpression()) {
+ scriptingElementSeen = true;
+ }
+ usebeanSeen = true;
+ visitBody(n);
+ }
+
+ public void visit(Node.PlugIn n) throws JasperException {
+ if (n.getHeight() != null && n.getHeight().isExpression()) {
+ scriptingElementSeen = true;
+ }
+ if (n.getWidth() != null && n.getWidth().isExpression()) {
+ scriptingElementSeen = true;
+ }
+ visitBody(n);
+ }
+
+ public void visit(Node.CustomTag n) throws JasperException {
+ // Check to see what kinds of element we see as child elements
+ checkSeen( n.getChildInfo(), n );
+ }
+
+ /**
+ * Check all child nodes for various elements and update the given
+ * ChildInfo object accordingly. Visits body in the process.
+ */
+ private void checkSeen( Node.ChildInfo ci, Node n )
+ throws JasperException
+ {
+ // save values collected so far
+ boolean scriptingElementSeenSave = scriptingElementSeen;
+ scriptingElementSeen = false;
+ boolean usebeanSeenSave = usebeanSeen;
+ usebeanSeen = false;
+ boolean includeActionSeenSave = includeActionSeen;
+ includeActionSeen = false;
+ boolean paramActionSeenSave = paramActionSeen;
+ paramActionSeen = false;
+ boolean setPropertySeenSave = setPropertySeen;
+ setPropertySeen = false;
+ boolean hasScriptingVarsSave = hasScriptingVars;
+ hasScriptingVars = false;
+
+ // Scan attribute list for expressions
+ if( n instanceof Node.CustomTag ) {
+ Node.CustomTag ct = (Node.CustomTag)n;
+ Node.JspAttribute[] attrs = ct.getJspAttributes();
+ for (int i = 0; attrs != null && i < attrs.length; i++) {
+ if (attrs[i].isExpression()) {
+ scriptingElementSeen = true;
+ break;
+ }
+ }
+ }
+
+ visitBody(n);
+
+ if( (n instanceof Node.CustomTag) && !hasScriptingVars) {
+ Node.CustomTag ct = (Node.CustomTag)n;
+ hasScriptingVars = ct.getVariableInfos().length > 0 ||
+ ct.getTagVariableInfos().length > 0;
+ }
+
+ // Record if the tag element and its body contains any scriptlet.
+ ci.setScriptless(! scriptingElementSeen);
+ ci.setHasUseBean(usebeanSeen);
+ ci.setHasIncludeAction(includeActionSeen);
+ ci.setHasParamAction(paramActionSeen);
+ ci.setHasSetProperty(setPropertySeen);
+ ci.setHasScriptingVars(hasScriptingVars);
+
+ // Propagate value of scriptingElementSeen up.
+ scriptingElementSeen = scriptingElementSeen || scriptingElementSeenSave;
+ usebeanSeen = usebeanSeen || usebeanSeenSave;
+ setPropertySeen = setPropertySeen || setPropertySeenSave;
+ includeActionSeen = includeActionSeen || includeActionSeenSave;
+ paramActionSeen = paramActionSeen || paramActionSeenSave;
+ hasScriptingVars = hasScriptingVars || hasScriptingVarsSave;
+ }
+
+ public void visit(Node.JspElement n) throws JasperException {
+ if (n.getNameAttribute().isExpression())
+ scriptingElementSeen = true;
+
+ Node.JspAttribute[] attrs = n.getJspAttributes();
+ for (int i = 0; i < attrs.length; i++) {
+ if (attrs[i].isExpression()) {
+ scriptingElementSeen = true;
+ break;
+ }
+ }
+ visitBody(n);
+ }
+
+ public void visit(Node.JspBody n) throws JasperException {
+ checkSeen( n.getChildInfo(), n );
+ }
+
+ public void visit(Node.NamedAttribute n) throws JasperException {
+ checkSeen( n.getChildInfo(), n );
+ }
+
+ public void visit(Node.Declaration n) throws JasperException {
+ scriptingElementSeen = true;
+ }
+
+ public void visit(Node.Expression n) throws JasperException {
+ scriptingElementSeen = true;
+ }
+
+ public void visit(Node.Scriptlet n) throws JasperException {
+ scriptingElementSeen = true;
+ }
+
+ public void updatePageInfo(PageInfo pageInfo) {
+ pageInfo.setScriptless(! scriptingElementSeen);
+ }
+ }
+
+
+ public static void collect(Compiler compiler, Node.Nodes page)
+ throws JasperException {
+
+ CollectVisitor collectVisitor = new CollectVisitor();
+ page.visit(collectVisitor);
+ collectVisitor.updatePageInfo(compiler.getPageInfo());
+
+ }
+}
+
Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/Compiler.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/Compiler.java?rev=819444&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/Compiler.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/Compiler.java Mon Sep 28 01:55:26 2009
@@ -0,0 +1,548 @@
+/*
+ * 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.*;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.struts2.jasper.JasperException;
+import org.apache.struts2.jasper.JspCompilationContext;
+import org.apache.struts2.jasper.Options;
+import org.apache.struts2.jasper.servlet.JspServletWrapper;
+
+/**
+ * Main JSP compiler class. This class uses Ant for compiling.
+ *
+ * @author Anil K. Vijendran
+ * @author Mandar Raje
+ * @author Pierre Delisle
+ * @author Kin-man Chung
+ * @author Remy Maucherat
+ * @author Mark Roth
+ */
+public abstract class Compiler {
+
+ protected org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
+ .getLog(Compiler.class);
+
+ // ----------------------------------------------------- Instance Variables
+
+ protected JspCompilationContext ctxt;
+
+ protected ErrorDispatcher errDispatcher;
+
+ protected PageInfo pageInfo;
+
+ protected JspServletWrapper jsw;
+
+ protected TagFileProcessor tfp;
+
+ protected Options options;
+
+ protected Node.Nodes pageNodes;
+
+ // ------------------------------------------------------------ Constructor
+
+ public void init(JspCompilationContext ctxt, JspServletWrapper jsw) {
+ this.jsw = jsw;
+ this.ctxt = ctxt;
+ this.options = ctxt.getOptions();
+ }
+
+ // --------------------------------------------------------- Public Methods
+
+ /**
+ * <p>
+ * Retrieves the parsed nodes of the JSP page, if they are available. May
+ * return null. Used in development mode for generating detailed error
+ * messages. http://issues.apache.org/bugzilla/show_bug.cgi?id=37062.
+ * </p>
+ */
+ public Node.Nodes getPageNodes() {
+ return this.pageNodes;
+ }
+
+ /**
+ * Compile the jsp file into equivalent servlet in .java file
+ *
+ * @return a smap for the current JSP page, if one is generated, null
+ * otherwise
+ */
+ protected String[] generateJava() throws Exception {
+
+ String[] smapStr = null;
+
+ long t1, t2, t3, t4;
+
+ t1 = t2 = t3 = t4 = 0;
+
+ if (log.isDebugEnabled()) {
+ t1 = System.currentTimeMillis();
+ }
+
+ // Setup page info area
+ pageInfo = new PageInfo(new BeanRepository(ctxt.getClassLoader(),
+ errDispatcher), ctxt.getJspFile());
+
+ JspConfig jspConfig = options.getJspConfig();
+ JspConfig.JspProperty jspProperty = jspConfig.findJspProperty(ctxt
+ .getJspFile());
+
+ /*
+ * If the current uri is matched by a pattern specified in a
+ * jsp-property-group in web.xml, initialize pageInfo with those
+ * properties.
+ */
+ if (jspProperty.isELIgnored() != null) {
+ pageInfo.setELIgnored(JspUtil.booleanValue(jspProperty
+ .isELIgnored()));
+ }
+ if (jspProperty.isScriptingInvalid() != null) {
+ pageInfo.setScriptingInvalid(JspUtil.booleanValue(jspProperty
+ .isScriptingInvalid()));
+ }
+ if (jspProperty.getIncludePrelude() != null) {
+ pageInfo.setIncludePrelude(jspProperty.getIncludePrelude());
+ }
+ if (jspProperty.getIncludeCoda() != null) {
+ pageInfo.setIncludeCoda(jspProperty.getIncludeCoda());
+ }
+ if (jspProperty.isDeferedSyntaxAllowedAsLiteral() != null) {
+ pageInfo.setDeferredSyntaxAllowedAsLiteral(JspUtil.booleanValue(jspProperty
+ .isDeferedSyntaxAllowedAsLiteral()));
+ }
+ if (jspProperty.isTrimDirectiveWhitespaces() != null) {
+ pageInfo.setTrimDirectiveWhitespaces(JspUtil.booleanValue(jspProperty
+ .isTrimDirectiveWhitespaces()));
+ }
+
+ ctxt.checkOutputDir();
+ String javaFileName = ctxt.getServletJavaFileName();
+
+ ServletWriter writer = null;
+ try {
+ /*
+ * The setting of isELIgnored changes the behaviour of the parser
+ * in subtle ways. To add to the 'fun', isELIgnored can be set in
+ * any file that forms part of the translation unit so setting it
+ * in a file included towards the end of the translation unit can
+ * change how the parser should have behaved when parsing content
+ * up to the point where isELIgnored was set. Arghh!
+ * Previous attempts to hack around this have only provided partial
+ * solutions. We now use two passes to parse the translation unit.
+ * The first just parses the directives and the second parses the
+ * whole translation unit once we know how isELIgnored has been set.
+ * TODO There are some possible optimisations of this process.
+ */
+ // Parse the file
+ ParserController parserCtl = new ParserController(ctxt, this);
+
+ // Pass 1 - the directives
+ Node.Nodes directives =
+ parserCtl.parseDirectives(ctxt.getJspFile());
+ Validator.validateDirectives(this, directives);
+
+ // Pass 2 - the whole translation unit
+ pageNodes = parserCtl.parse(ctxt.getJspFile());
+
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(1024);
+
+ if (ctxt.isPrototypeMode()) {
+ // generate prototype .java file for the tag file
+ writer = setupContextWriter(byteArrayOutputStream);
+ Generator.generate(writer, this, pageNodes);
+ writer.close();
+ writer = null;
+ return null;
+ }
+
+ // Validate and process attributes - don't re-validate the
+ // directives we validated in pass 1
+ Validator.validateExDirectives(this, pageNodes);
+
+ if (log.isDebugEnabled()) {
+ t2 = System.currentTimeMillis();
+ }
+
+ // Collect page info
+ Collector.collect(this, pageNodes);
+
+ // Compile (if necessary) and load the tag files referenced in
+ // this compilation unit.
+ tfp = new TagFileProcessor();
+ tfp.loadTagFiles(this, pageNodes);
+
+ if (log.isDebugEnabled()) {
+ t3 = System.currentTimeMillis();
+ }
+
+ // Determine which custom tag needs to declare which scripting vars
+ ScriptingVariabler.set(pageNodes, errDispatcher);
+
+ // Optimizations by Tag Plugins
+ TagPluginManager tagPluginManager = options.getTagPluginManager();
+ tagPluginManager.apply(pageNodes, errDispatcher, pageInfo);
+
+ // Optimization: concatenate contiguous template texts.
+ TextOptimizer.concatenate(this, pageNodes);
+
+ // Generate static function mapper codes.
+ ELFunctionMapper.map(this, pageNodes);
+
+ // generate servlet .java file
+ writer = setupContextWriter(byteArrayOutputStream);
+ Generator.generate(writer, this, pageNodes);
+ writer.close();
+ writer = null;
+
+ // The writer is only used during the compile, dereference
+ // it in the JspCompilationContext when done to allow it
+ // to be GC'd and save memory.
+ ctxt.setWriter(null);
+ ctxt.setSourceCode(byteArrayOutputStream.toString());
+
+ if (log.isDebugEnabled()) {
+ t4 = System.currentTimeMillis();
+ log.debug("Generated " + javaFileName + " total=" + (t4 - t1)
+ + " generate=" + (t4 - t3) + " validate=" + (t2 - t1));
+ }
+
+ } catch (Exception e) {
+ if (writer != null) {
+ try {
+ writer.close();
+ writer = null;
+ } catch (Exception e1) {
+ // do nothing
+ }
+ }
+ // Remove the generated .java file
+ new File(javaFileName).delete();
+ throw e;
+ } finally {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (Exception e2) {
+ // do nothing
+ }
+ }
+ }
+
+ // JSR45 Support
+ if (!options.isSmapSuppressed()) {
+ smapStr = SmapUtil.generateSmap(ctxt, pageNodes);
+ }
+
+ // If any proto type .java and .class files was generated,
+ // the prototype .java may have been replaced by the current
+ // compilation (if the tag file is self referencing), but the
+ // .class file need to be removed, to make sure that javac would
+ // generate .class again from the new .java file just generated.
+ tfp.removeProtoTypeFiles(ctxt.getClassFileName());
+
+ return smapStr;
+ }
+
+ private ServletWriter setupContextWriter(OutputStream os)
+ throws FileNotFoundException, JasperException {
+ ServletWriter writer;
+ // Setup the ServletWriter
+ String javaEncoding = ctxt.getOptions().getJavaEncoding();
+ OutputStreamWriter osw = null;
+
+ try {
+ osw = new OutputStreamWriter(os, javaEncoding);
+ } catch (UnsupportedEncodingException ex) {
+ errDispatcher.jspError("jsp.error.needAlternateJavaEncoding",
+ javaEncoding);
+ }
+
+ writer = new ServletWriter(new PrintWriter(osw));
+ ctxt.setWriter(writer);
+ return writer;
+ }
+
+ /**
+ * Compile the servlet from .java file to .class file
+ */
+ protected abstract void generateClass(String[] smap)
+ throws FileNotFoundException, JasperException, Exception;
+
+ /**
+ * Compile the jsp file from the current engine context
+ */
+ public void compile() throws FileNotFoundException, JasperException,
+ Exception {
+ compile(true);
+ }
+
+ /**
+ * Compile the jsp file from the current engine context. As an side- effect,
+ * tag files that are referenced by this page are also compiled.
+ *
+ * @param compileClass
+ * If true, generate both .java and .class file If false,
+ * generate only .java file
+ */
+ public void compile(boolean compileClass) throws FileNotFoundException,
+ JasperException, Exception {
+ compile(compileClass, false);
+ }
+
+ /**
+ * Compile the jsp file from the current engine context. As an side- effect,
+ * tag files that are referenced by this page are also compiled.
+ *
+ * @param compileClass
+ * If true, generate both .java and .class file If false,
+ * generate only .java file
+ * @param jspcMode
+ * true if invoked from JspC, false otherwise
+ */
+ public void compile(boolean compileClass, boolean jspcMode)
+ throws FileNotFoundException, JasperException, Exception {
+ if (errDispatcher == null) {
+ this.errDispatcher = new ErrorDispatcher(jspcMode);
+ }
+
+ try {
+ String[] smap = generateJava();
+ if (compileClass) {
+ generateClass(smap);
+ // Fix for bugzilla 41606
+ // Set JspServletWrapper.servletClassLastModifiedTime after successful compile
+ String targetFileName = ctxt.getClassFileName();
+ if (targetFileName != null) {
+ File targetFile = new File(targetFileName);
+ if (targetFile.exists() && jsw != null) {
+ jsw.setServletClassLastModifiedTime(targetFile.lastModified());
+ }
+ }
+ }
+ } finally {
+ if (tfp != null && ctxt.isPrototypeMode()) {
+ tfp.removeProtoTypeFiles(null);
+ }
+ // Make sure these object which are only used during the
+ // generation and compilation of the JSP page get
+ // dereferenced so that they can be GC'd and reduce the
+ // memory footprint.
+ tfp = null;
+ errDispatcher = null;
+ pageInfo = null;
+
+ // Only get rid of the pageNodes if in production.
+ // In development mode, they are used for detailed
+ // error messages.
+ // http://issues.apache.org/bugzilla/show_bug.cgi?id=37062
+ if (!this.options.getDevelopment()) {
+ pageNodes = null;
+ }
+
+ if (ctxt.getWriter() != null) {
+ ctxt.getWriter().close();
+ ctxt.setWriter(null);
+ }
+ }
+ }
+
+ /**
+ * This is a protected method intended to be overridden by subclasses of
+ * Compiler. This is used by the compile method to do all the compilation.
+ */
+ public boolean isOutDated() {
+ return isOutDated(true);
+ }
+
+ /**
+ * Determine if a compilation is necessary by checking the time stamp of the
+ * JSP page with that of the corresponding .class or .java file. If the page
+ * has dependencies, the check is also extended to its dependeants, and so
+ * on. This method can by overidden by a subclasses of Compiler.
+ *
+ * @param checkClass
+ * If true, check against .class file, if false, check against
+ * .java file.
+ */
+ public boolean isOutDated(boolean checkClass) {
+
+ String jsp = ctxt.getJspFile();
+
+ if (jsw != null
+ && (ctxt.getOptions().getModificationTestInterval() > 0)) {
+
+ if (jsw.getLastModificationTest()
+ + (ctxt.getOptions().getModificationTestInterval() * 1000) > System
+ .currentTimeMillis()) {
+ return false;
+ } else {
+ jsw.setLastModificationTest(System.currentTimeMillis());
+ }
+ }
+
+ long jspRealLastModified = 0;
+ try {
+ URL jspUrl = ctxt.getResource(jsp);
+ if (jspUrl == null) {
+ ctxt.incrementRemoved();
+ return false;
+ }
+ URLConnection uc = jspUrl.openConnection();
+ if (uc instanceof JarURLConnection) {
+ jspRealLastModified =
+ ((JarURLConnection) uc).getJarEntry().getTime();
+ } else {
+ jspRealLastModified = uc.getLastModified();
+ }
+ uc.getInputStream().close();
+ } catch (Exception e) {
+ return true;
+ }
+
+ long targetLastModified = 0;
+ File targetFile;
+
+ if (checkClass) {
+ targetFile = new File(ctxt.getClassFileName());
+ } else {
+ targetFile = new File(ctxt.getServletJavaFileName());
+ }
+
+ if (!targetFile.exists()) {
+ return true;
+ }
+
+ targetLastModified = targetFile.lastModified();
+ if (checkClass && jsw != null) {
+ jsw.setServletClassLastModifiedTime(targetLastModified);
+ }
+ if (targetLastModified < jspRealLastModified) {
+ if (log.isDebugEnabled()) {
+ log.debug("Compiler: outdated: " + targetFile + " "
+ + targetLastModified);
+ }
+ return true;
+ }
+
+ // determine if source dependent files (e.g. includes using include
+ // directives) have been changed.
+ if (jsw == null) {
+ return false;
+ }
+
+ List depends = jsw.getDependants();
+ if (depends == null) {
+ return false;
+ }
+
+ Iterator it = depends.iterator();
+ while (it.hasNext()) {
+ String include = (String) it.next();
+ try {
+ URL includeUrl = ctxt.getResource(include);
+ if (includeUrl == null) {
+ return true;
+ }
+
+ URLConnection iuc = includeUrl.openConnection();
+ long includeLastModified = 0;
+ if (iuc instanceof JarURLConnection) {
+ includeLastModified =
+ ((JarURLConnection) iuc).getJarEntry().getTime();
+ } else {
+ includeLastModified = iuc.getLastModified();
+ }
+ iuc.getInputStream().close();
+
+ if (includeLastModified > targetLastModified) {
+ return true;
+ }
+ } catch (Exception e) {
+ return true;
+ }
+ }
+
+ return false;
+
+ }
+
+ /**
+ * Gets the error dispatcher.
+ */
+ public ErrorDispatcher getErrorDispatcher() {
+ return errDispatcher;
+ }
+
+ /**
+ * Gets the info about the page under compilation
+ */
+ public PageInfo getPageInfo() {
+ return pageInfo;
+ }
+
+ public JspCompilationContext getCompilationContext() {
+ return ctxt;
+ }
+
+ /**
+ * Remove generated files
+ */
+ public void removeGeneratedFiles() {
+ try {
+ String classFileName = ctxt.getClassFileName();
+ if (classFileName != null) {
+ File classFile = new File(classFileName);
+ if (log.isDebugEnabled())
+ log.debug("Deleting " + classFile);
+ classFile.delete();
+ }
+ } catch (Exception e) {
+ // Remove as much as possible, ignore possible exceptions
+ }
+ try {
+ String javaFileName = ctxt.getServletJavaFileName();
+ if (javaFileName != null) {
+ File javaFile = new File(javaFileName);
+ if (log.isDebugEnabled())
+ log.debug("Deleting " + javaFile);
+ javaFile.delete();
+ }
+ } catch (Exception e) {
+ // Remove as much as possible, ignore possible exceptions
+ }
+ }
+
+ public void removeGeneratedClassFiles() {
+ try {
+ String classFileName = ctxt.getClassFileName();
+ if (classFileName != null) {
+ File classFile = new File(classFileName);
+ if (log.isDebugEnabled())
+ log.debug("Deleting " + classFile);
+ classFile.delete();
+ }
+ } catch (Exception e) {
+ // Remove as much as possible, ignore possible exceptions
+ }
+ }
+}
Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/DefaultErrorHandler.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/DefaultErrorHandler.java?rev=819444&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/DefaultErrorHandler.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/DefaultErrorHandler.java Mon Sep 28 01:55:26 2009
@@ -0,0 +1,109 @@
+/*
+ * 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;
+
+/**
+ * Default implementation of ErrorHandler interface.
+ *
+ * @author Jan Luehe
+ */
+class DefaultErrorHandler implements ErrorHandler {
+
+ /*
+ * Processes the given JSP parse error.
+ *
+ * @param fname Name of the JSP file in which the parse error occurred
+ * @param line Parse error line number
+ * @param column Parse error column number
+ * @param errMsg Parse error message
+ * @param exception Parse exception
+ */
+ public void jspError(String fname, int line, int column, String errMsg,
+ Exception ex) throws JasperException {
+ throw new JasperException(fname + "(" + line + "," + column + ")"
+ + " " + errMsg, ex);
+ }
+
+ /*
+ * Processes the given JSP parse error.
+ *
+ * @param errMsg Parse error message
+ * @param exception Parse exception
+ */
+ public void jspError(String errMsg, Exception ex) throws JasperException {
+ throw new JasperException(errMsg, ex);
+ }
+
+ /*
+ * Processes the given javac compilation errors.
+ *
+ * @param details Array of JavacErrorDetail instances corresponding to the
+ * compilation errors
+ */
+ public void javacError(JavacErrorDetail[] details) throws JasperException {
+
+ if (details == null) {
+ return;
+ }
+
+ Object[] args = null;
+ StringBuffer buf = new StringBuffer();
+
+ for (int i=0; i < details.length; i++) {
+ if (details[i].getJspBeginLineNumber() >= 0) {
+ args = new Object[] {
+ new Integer(details[i].getJspBeginLineNumber()),
+ details[i].getJspFileName() };
+ buf.append("\n\n");
+ buf.append(Localizer.getMessage("jsp.error.single.line.number",
+ args));
+ buf.append("\n");
+ buf.append(details[i].getErrorMessage());
+ buf.append("\n");
+ buf.append(details[i].getJspExtract());
+ } else {
+ args = new Object[] {
+ new Integer(details[i].getJavaLineNumber()) };
+ buf.append("\n\n");
+ buf.append(Localizer.getMessage("jsp.error.java.line.number",
+ args));
+ buf.append("\n");
+ buf.append(details[i].getErrorMessage());
+ }
+ }
+ buf.append("\n\nStacktrace:");
+ throw new JasperException(
+ Localizer.getMessage("jsp.error.unable.compile") + ": " + buf);
+ }
+
+ /**
+ * Processes the given javac error report and exception.
+ *
+ * @param errorReport Compilation error report
+ * @param exception Compilation exception
+ */
+ public void javacError(String errorReport, Exception exception)
+ throws JasperException {
+
+ throw new JasperException(
+ Localizer.getMessage("jsp.error.unable.compile"), exception);
+ }
+
+}
Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/Dumper.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/Dumper.java?rev=819444&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/Dumper.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/Dumper.java Mon Sep 28 01:55:26 2009
@@ -0,0 +1,207 @@
+/*
+ * 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.xml.sax.Attributes;
+import org.apache.struts2.jasper.JasperException;
+
+class Dumper {
+
+ static class DumpVisitor extends Node.Visitor {
+ private int indent = 0;
+
+ private String getAttributes(Attributes attrs) {
+ if (attrs == null)
+ return "";
+
+ StringBuffer buf = new StringBuffer();
+ for (int i=0; i < attrs.getLength(); i++) {
+ buf.append(" " + attrs.getQName(i) + "=\""
+ + attrs.getValue(i) + "\"");
+ }
+ return buf.toString();
+ }
+
+ private void printString(String str) {
+ printIndent();
+ System.out.print(str);
+ }
+
+ private void printString(String prefix, char[] chars, String suffix) {
+ String str = null;
+ if (chars != null) {
+ str = new String(chars);
+ }
+ printString(prefix, str, suffix);
+ }
+
+ private void printString(String prefix, String str, String suffix) {
+ printIndent();
+ if (str != null) {
+ System.out.print(prefix + str + suffix);
+ } else {
+ System.out.print(prefix + suffix);
+ }
+ }
+
+ private void printAttributes(String prefix, Attributes attrs,
+ String suffix) {
+ printString(prefix, getAttributes(attrs), suffix);
+ }
+
+ private void dumpBody(Node n) throws JasperException {
+ Node.Nodes page = n.getBody();
+ if (page != null) {
+// indent++;
+ page.visit(this);
+// indent--;
+ }
+ }
+
+ public void visit(Node.PageDirective n) throws JasperException {
+ printAttributes("<%@ page", n.getAttributes(), "%>");
+ }
+
+ public void visit(Node.TaglibDirective n) throws JasperException {
+ printAttributes("<%@ taglib", n.getAttributes(), "%>");
+ }
+
+ public void visit(Node.IncludeDirective n) throws JasperException {
+ printAttributes("<%@ include", n.getAttributes(), "%>");
+ dumpBody(n);
+ }
+
+ public void visit(Node.Comment n) throws JasperException {
+ printString("<%--", n.getText(), "--%>");
+ }
+
+ public void visit(Node.Declaration n) throws JasperException {
+ printString("<%!", n.getText(), "%>");
+ }
+
+ public void visit(Node.Expression n) throws JasperException {
+ printString("<%=", n.getText(), "%>");
+ }
+
+ public void visit(Node.Scriptlet n) throws JasperException {
+ printString("<%", n.getText(), "%>");
+ }
+
+ public void visit(Node.IncludeAction n) throws JasperException {
+ printAttributes("<jsp:include", n.getAttributes(), ">");
+ dumpBody(n);
+ printString("</jsp:include>");
+ }
+
+ public void visit(Node.ForwardAction n) throws JasperException {
+ printAttributes("<jsp:forward", n.getAttributes(), ">");
+ dumpBody(n);
+ printString("</jsp:forward>");
+ }
+
+ public void visit(Node.GetProperty n) throws JasperException {
+ printAttributes("<jsp:getProperty", n.getAttributes(), "/>");
+ }
+
+ public void visit(Node.SetProperty n) throws JasperException {
+ printAttributes("<jsp:setProperty", n.getAttributes(), ">");
+ dumpBody(n);
+ printString("</jsp:setProperty>");
+ }
+
+ public void visit(Node.UseBean n) throws JasperException {
+ printAttributes("<jsp:useBean", n.getAttributes(), ">");
+ dumpBody(n);
+ printString("</jsp:useBean>");
+ }
+
+ public void visit(Node.PlugIn n) throws JasperException {
+ printAttributes("<jsp:plugin", n.getAttributes(), ">");
+ dumpBody(n);
+ printString("</jsp:plugin>");
+ }
+
+ public void visit(Node.ParamsAction n) throws JasperException {
+ printAttributes("<jsp:params", n.getAttributes(), ">");
+ dumpBody(n);
+ printString("</jsp:params>");
+ }
+
+ public void visit(Node.ParamAction n) throws JasperException {
+ printAttributes("<jsp:param", n.getAttributes(), ">");
+ dumpBody(n);
+ printString("</jsp:param>");
+ }
+
+ public void visit(Node.NamedAttribute n) throws JasperException {
+ printAttributes("<jsp:attribute", n.getAttributes(), ">");
+ dumpBody(n);
+ printString("</jsp:attribute>");
+ }
+
+ public void visit(Node.JspBody n) throws JasperException {
+ printAttributes("<jsp:body", n.getAttributes(), ">");
+ dumpBody(n);
+ printString("</jsp:body>");
+ }
+
+ public void visit(Node.ELExpression n) throws JasperException {
+ printString( "${" + new String( n.getText() ) + "}" );
+ }
+
+ public void visit(Node.CustomTag n) throws JasperException {
+ printAttributes("<" + n.getQName(), n.getAttributes(), ">");
+ dumpBody(n);
+ printString("</" + n.getQName() + ">");
+ }
+
+ public void visit(Node.UninterpretedTag n) throws JasperException {
+ String tag = n.getQName();
+ printAttributes("<"+tag, n.getAttributes(), ">");
+ dumpBody(n);
+ printString("</" + tag + ">");
+ }
+
+ public void visit(Node.TemplateText n) throws JasperException {
+ printString(new String(n.getText()));
+ }
+
+ private void printIndent() {
+ for (int i=0; i < indent; i++) {
+ System.out.print(" ");
+ }
+ }
+ }
+
+ public static void dump(Node n) {
+ try {
+ n.accept(new DumpVisitor());
+ } catch (JasperException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void dump(Node.Nodes page) {
+ try {
+ page.visit(new DumpVisitor());
+ } catch (JasperException e) {
+ e.printStackTrace();
+ }
+ }
+}
+
Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/ELFunctionMapper.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/ELFunctionMapper.java?rev=819444&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/ELFunctionMapper.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/ELFunctionMapper.java Mon Sep 28 01:55:26 2009
@@ -0,0 +1,281 @@
+/*
+ * 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 javax.servlet.jsp.tagext.FunctionInfo;
+import org.apache.struts2.jasper.JasperException;
+
+/**
+ * This class generates functions mappers for the EL expressions in the page.
+ * Instead of a global mapper, a mapper is used for ecah call to EL
+ * evaluator, thus avoiding the prefix overlapping and redefinition
+ * issues.
+ *
+ * @author Kin-man Chung
+ */
+
+public class ELFunctionMapper {
+ private int currFunc = 0;
+ StringBuffer ds; // Contains codes to initialize the functions mappers.
+ StringBuffer ss; // Contains declarations of the functions mappers.
+
+ /**
+ * Creates the functions mappers for all EL expressions in the JSP page.
+ *
+ * @param compiler Current compiler, mainly for accessing error dispatcher.
+ * @param page The current compilation unit.
+ */
+ public static void map(Compiler compiler, Node.Nodes page)
+ throws JasperException {
+
+ ELFunctionMapper map = new ELFunctionMapper();
+ map.ds = new StringBuffer();
+ map.ss = new StringBuffer();
+
+ page.visit(map.new ELFunctionVisitor());
+
+ // Append the declarations to the root node
+ String ds = map.ds.toString();
+ if (ds.length() > 0) {
+ Node root = page.getRoot();
+ new Node.Declaration(map.ss.toString(), null, root);
+ new Node.Declaration("static {\n" + ds + "}\n", null, root);
+ }
+ }
+
+ /**
+ * A visitor for the page. The places where EL is allowed are scanned
+ * for functions, and if found functions mappers are created.
+ */
+ class ELFunctionVisitor extends Node.Visitor {
+
+ /**
+ * Use a global name map to facilitate reuse of function maps.
+ * The key used is prefix:function:uri.
+ */
+ private HashMap<String, String> gMap = new HashMap<String, String>();
+
+ public void visit(Node.ParamAction n) throws JasperException {
+ doMap(n.getValue());
+ visitBody(n);
+ }
+
+ public void visit(Node.IncludeAction n) throws JasperException {
+ doMap(n.getPage());
+ visitBody(n);
+ }
+
+ public void visit(Node.ForwardAction n) throws JasperException {
+ doMap(n.getPage());
+ visitBody(n);
+ }
+
+ public void visit(Node.SetProperty n) throws JasperException {
+ doMap(n.getValue());
+ visitBody(n);
+ }
+
+ public void visit(Node.UseBean n) throws JasperException {
+ doMap(n.getBeanName());
+ visitBody(n);
+ }
+
+ public void visit(Node.PlugIn n) throws JasperException {
+ doMap(n.getHeight());
+ doMap(n.getWidth());
+ visitBody(n);
+ }
+
+ public void visit(Node.JspElement n) throws JasperException {
+
+ Node.JspAttribute[] attrs = n.getJspAttributes();
+ for (int i = 0; attrs != null && i < attrs.length; i++) {
+ doMap(attrs[i]);
+ }
+ doMap(n.getNameAttribute());
+ visitBody(n);
+ }
+
+ public void visit(Node.UninterpretedTag n) throws JasperException {
+
+ Node.JspAttribute[] attrs = n.getJspAttributes();
+ for (int i = 0; attrs != null && i < attrs.length; i++) {
+ doMap(attrs[i]);
+ }
+ visitBody(n);
+ }
+
+ public void visit(Node.CustomTag n) throws JasperException {
+ Node.JspAttribute[] attrs = n.getJspAttributes();
+ for (int i = 0; attrs != null && i < attrs.length; i++) {
+ doMap(attrs[i]);
+ }
+ visitBody(n);
+ }
+
+ public void visit(Node.ELExpression n) throws JasperException {
+ doMap(n.getEL());
+ }
+
+ private void doMap(Node.JspAttribute attr)
+ throws JasperException {
+ if (attr != null) {
+ doMap(attr.getEL());
+ }
+ }
+
+ /**
+ * Creates function mappers, if needed, from ELNodes
+ */
+ private void doMap(ELNode.Nodes el)
+ throws JasperException {
+
+ // Only care about functions in ELNode's
+ class Fvisitor extends ELNode.Visitor {
+ ArrayList<ELNode.Function> funcs =
+ new ArrayList<ELNode.Function>();
+ HashMap<String, String> keyMap = new HashMap<String, String>();
+ public void visit(ELNode.Function n) throws JasperException {
+ String key = n.getPrefix() + ":" + n.getName();
+ if (! keyMap.containsKey(key)) {
+ keyMap.put(key,"");
+ funcs.add(n);
+ }
+ }
+ }
+
+ if (el == null) {
+ return;
+ }
+
+ // First locate all unique functions in this EL
+ Fvisitor fv = new Fvisitor();
+ el.visit(fv);
+ ArrayList functions = fv.funcs;
+
+ if (functions.size() == 0) {
+ return;
+ }
+
+ // Reuse a previous map if possible
+ String decName = matchMap(functions);
+ if (decName != null) {
+ el.setMapName(decName);
+ return;
+ }
+
+ // Generate declaration for the map statically
+ decName = getMapName();
+ ss.append("static private org.apache.struts2.jasper.runtime.ProtectedFunctionMapper " + decName + ";\n");
+
+ ds.append(" " + decName + "= ");
+ ds.append("org.apache.struts2.jasper.runtime.ProtectedFunctionMapper");
+
+ // Special case if there is only one function in the map
+ String funcMethod = null;
+ if (functions.size() == 1) {
+ funcMethod = ".getMapForFunction";
+ } else {
+ ds.append(".getInstance();\n");
+ funcMethod = " " + decName + ".mapFunction";
+ }
+
+ // Setup arguments for either getMapForFunction or mapFunction
+ for (int i = 0; i < functions.size(); i++) {
+ ELNode.Function f = (ELNode.Function)functions.get(i);
+ FunctionInfo funcInfo = f.getFunctionInfo();
+ String key = f.getPrefix()+ ":" + f.getName();
+ ds.append(funcMethod + "(\"" + key + "\", " +
+ funcInfo.getFunctionClass() + ".class, " +
+ '\"' + f.getMethodName() + "\", " +
+ "new Class[] {");
+ String params[] = f.getParameters();
+ for (int k = 0; k < params.length; k++) {
+ if (k != 0) {
+ ds.append(", ");
+ }
+ int iArray = params[k].indexOf('[');
+ if (iArray < 0) {
+ ds.append(params[k] + ".class");
+ }
+ else {
+ String baseType = params[k].substring(0, iArray);
+ ds.append("java.lang.reflect.Array.newInstance(");
+ ds.append(baseType);
+ ds.append(".class,");
+
+ // Count the number of array dimension
+ int aCount = 0;
+ for (int jj = iArray; jj < params[k].length(); jj++ ) {
+ if (params[k].charAt(jj) == '[') {
+ aCount++;
+ }
+ }
+ if (aCount == 1) {
+ ds.append("0).getClass()");
+ } else {
+ ds.append("new int[" + aCount + "]).getClass()");
+ }
+ }
+ }
+ ds.append("});\n");
+ // Put the current name in the global function map
+ gMap.put(f.getPrefix() + ':' + f.getName() + ':' + f.getUri(),
+ decName);
+ }
+ el.setMapName(decName);
+ }
+
+ /**
+ * Find the name of the function mapper for an EL. Reuse a
+ * previously generated one if possible.
+ * @param functions An ArrayList of ELNode.Function instances that
+ * represents the functions in an EL
+ * @return A previous generated function mapper name that can be used
+ * by this EL; null if none found.
+ */
+ private String matchMap(ArrayList functions) {
+
+ String mapName = null;
+ for (int i = 0; i < functions.size(); i++) {
+ ELNode.Function f = (ELNode.Function)functions.get(i);
+ String temName = (String) gMap.get(f.getPrefix() + ':' +
+ f.getName() + ':' + f.getUri());
+ if (temName == null) {
+ return null;
+ }
+ if (mapName == null) {
+ mapName = temName;
+ } else if (!temName.equals(mapName)) {
+ // If not all in the previous match, then no match.
+ return null;
+ }
+ }
+ return mapName;
+ }
+
+ /*
+ * @return An unique name for a function mapper.
+ */
+ private String getMapName() {
+ return "_jspx_fnmap_" + currFunc++;
+ }
+ }
+}
+