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 02:43:39 UTC
svn commit: r819435 [3/23] - in
/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper:
./ compiler/ compiler/tagplugin/ el/ runtime/ security/ servlet/
tagplugins/ tagplugins/jstl/ tagplugins/jstl/core/ util/ xmlparser/
Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/AntCompiler.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/AntCompiler.java?rev=819435&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/AntCompiler.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/AntCompiler.java Mon Sep 28 00:43:34 2009
@@ -0,0 +1,493 @@
+/*
+ * 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.jasper.compiler;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.StringTokenizer;
+
+import org.apache.jasper.JasperException;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DefaultLogger;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Javac;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.PatternSet;
+
+/**
+ * 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 class AntCompiler extends Compiler {
+
+ protected static Object javacLock = new Object();
+
+ static {
+ System.setErr(new SystemLogHandler(System.err));
+ }
+
+ // ----------------------------------------------------- Instance Variables
+
+ protected Project project = null;
+ protected JasperAntLogger logger;
+
+ // ------------------------------------------------------------ Constructor
+
+ // Lazy eval - if we don't need to compile we probably don't need the project
+ protected Project getProject() {
+
+ if (project != null)
+ return project;
+
+ // Initializing project
+ project = new Project();
+ logger = new JasperAntLogger();
+ logger.setOutputPrintStream(System.out);
+ logger.setErrorPrintStream(System.err);
+ logger.setMessageOutputLevel(Project.MSG_INFO);
+ project.addBuildListener( logger);
+ if (System.getProperty("catalina.home") != null) {
+ project.setBasedir( System.getProperty("catalina.home"));
+ }
+
+ if( options.getCompiler() != null ) {
+ if( log.isDebugEnabled() )
+ log.debug("Compiler " + options.getCompiler() );
+ project.setProperty("build.compiler", options.getCompiler() );
+ }
+ project.init();
+ return project;
+ }
+
+ public class JasperAntLogger extends DefaultLogger {
+
+ protected StringBuffer reportBuf = new StringBuffer();
+
+ protected void printMessage(final String message,
+ final PrintStream stream,
+ final int priority) {
+ }
+
+ protected void log(String message) {
+ reportBuf.append(message);
+ reportBuf.append(System.getProperty("line.separator"));
+ }
+
+ protected String getReport() {
+ String report = reportBuf.toString();
+ reportBuf.setLength(0);
+ return report;
+ }
+ }
+
+ // --------------------------------------------------------- Public Methods
+
+
+ /**
+ * Compile the servlet from .java file to .class file
+ */
+ protected void generateClass(String[] smap)
+ throws FileNotFoundException, JasperException, Exception {
+
+ long t1 = 0;
+ if (log.isDebugEnabled()) {
+ t1 = System.currentTimeMillis();
+ }
+
+ String javaEncoding = ctxt.getOptions().getJavaEncoding();
+ String javaFileName = ctxt.getServletJavaFileName();
+ String classpath = ctxt.getClassPath();
+
+ String sep = System.getProperty("path.separator");
+
+ StringBuffer errorReport = new StringBuffer();
+
+ StringBuffer info=new StringBuffer();
+ info.append("Compile: javaFileName=" + javaFileName + "\n" );
+ info.append(" classpath=" + classpath + "\n" );
+
+ // Start capturing the System.err output for this thread
+ SystemLogHandler.setThread();
+
+ // Initializing javac task
+ getProject();
+ Javac javac = (Javac) project.createTask("javac");
+
+ // Initializing classpath
+ Path path = new Path(project);
+ path.setPath(System.getProperty("java.class.path"));
+ info.append(" cp=" + System.getProperty("java.class.path") + "\n");
+ StringTokenizer tokenizer = new StringTokenizer(classpath, sep);
+ while (tokenizer.hasMoreElements()) {
+ String pathElement = tokenizer.nextToken();
+ File repository = new File(pathElement);
+ path.setLocation(repository);
+ info.append(" cp=" + repository + "\n");
+ }
+
+ if( log.isDebugEnabled() )
+ log.debug( "Using classpath: " + System.getProperty("java.class.path") + sep
+ + classpath);
+
+ // Initializing sourcepath
+ Path srcPath = new Path(project);
+ srcPath.setLocation(options.getScratchDir());
+
+ info.append(" work dir=" + options.getScratchDir() + "\n");
+
+ // Initialize and set java extensions
+ String exts = System.getProperty("java.ext.dirs");
+ if (exts != null) {
+ Path extdirs = new Path(project);
+ extdirs.setPath(exts);
+ javac.setExtdirs(extdirs);
+ info.append(" extension dir=" + exts + "\n");
+ }
+
+ // Add endorsed directories if any are specified and we're forking
+ // See Bugzilla 31257
+ if(ctxt.getOptions().getFork()) {
+ String endorsed = System.getProperty("java.endorsed.dirs");
+ if(endorsed != null) {
+ Javac.ImplementationSpecificArgument endorsedArg =
+ javac.createCompilerArg();
+ endorsedArg.setLine("-J-Djava.endorsed.dirs=" +
+ quotePathList(endorsed));
+ info.append(" endorsed dir=" + quotePathList(endorsed) +
+ "\n");
+ } else {
+ info.append(" no endorsed dirs specified\n");
+ }
+ }
+
+ // Configure the compiler object
+ javac.setEncoding(javaEncoding);
+ javac.setClasspath(path);
+ javac.setDebug(ctxt.getOptions().getClassDebugInfo());
+ javac.setSrcdir(srcPath);
+ javac.setTempdir(options.getScratchDir());
+ javac.setOptimize(! ctxt.getOptions().getClassDebugInfo() );
+ javac.setFork(ctxt.getOptions().getFork());
+ info.append(" srcDir=" + srcPath + "\n" );
+
+ // Set the Java compiler to use
+ if (options.getCompiler() != null) {
+ javac.setCompiler(options.getCompiler());
+ info.append(" compiler=" + options.getCompiler() + "\n");
+ }
+
+ if (options.getCompilerTargetVM() != null) {
+ javac.setTarget(options.getCompilerTargetVM());
+ info.append(" compilerTargetVM=" + options.getCompilerTargetVM() + "\n");
+ }
+
+ if (options.getCompilerSourceVM() != null) {
+ javac.setSource(options.getCompilerSourceVM());
+ info.append(" compilerSourceVM=" + options.getCompilerSourceVM() + "\n");
+ }
+
+ // Build includes path
+ PatternSet.NameEntry includes = javac.createInclude();
+
+ includes.setName(ctxt.getJavaPath());
+ info.append(" include="+ ctxt.getJavaPath() + "\n" );
+
+ BuildException be = null;
+
+ try {
+ if (ctxt.getOptions().getFork()) {
+ javac.execute();
+ } else {
+ synchronized(javacLock) {
+ javac.execute();
+ }
+ }
+ } catch (BuildException e) {
+ be = e;
+ log.error(Localizer.getMessage("jsp.error.javac"), e);
+ log.error(Localizer.getMessage("jsp.error.javac.env") + info.toString());
+ }
+
+ errorReport.append(logger.getReport());
+
+ // Stop capturing the System.err output for this thread
+ String errorCapture = SystemLogHandler.unsetThread();
+ if (errorCapture != null) {
+ errorReport.append(System.getProperty("line.separator"));
+ errorReport.append(errorCapture);
+ }
+
+ if (!ctxt.keepGenerated()) {
+ File javaFile = new File(javaFileName);
+ javaFile.delete();
+ }
+
+ if (be != null) {
+ String errorReportString = errorReport.toString();
+ log.error(Localizer.getMessage("jsp.error.compilation", javaFileName, errorReportString));
+ JavacErrorDetail[] javacErrors = ErrorDispatcher.parseJavacErrors(
+ errorReportString, javaFileName, pageNodes);
+ if (javacErrors != null) {
+ errDispatcher.javacError(javacErrors);
+ } else {
+ errDispatcher.javacError(errorReportString, be);
+ }
+ }
+
+ if( log.isDebugEnabled() ) {
+ long t2 = System.currentTimeMillis();
+ log.debug("Compiled " + ctxt.getServletJavaFileName() + " "
+ + (t2-t1) + "ms");
+ }
+
+ logger = null;
+ project = null;
+
+ if (ctxt.isPrototypeMode()) {
+ return;
+ }
+
+ // JSR45 Support
+ if (! options.isSmapSuppressed()) {
+ SmapUtil.installSmap(smap);
+ }
+ }
+
+ private String quotePathList(String list) {
+ StringBuffer result = new StringBuffer(list.length() + 10);
+ StringTokenizer st = new StringTokenizer(list, File.pathSeparator);
+ while (st.hasMoreTokens()) {
+ String token = st.nextToken();
+ if (token.indexOf(' ') == -1) {
+ result.append(token);
+ } else {
+ result.append('\"');
+ result.append(token);
+ result.append('\"');
+ }
+ if (st.hasMoreTokens()) {
+ result.append(File.pathSeparatorChar);
+ }
+ }
+ return result.toString();
+ }
+
+
+ protected static class SystemLogHandler extends PrintStream {
+
+
+ // ----------------------------------------------------------- Constructors
+
+
+ /**
+ * Construct the handler to capture the output of the given steam.
+ */
+ public SystemLogHandler(PrintStream wrapped) {
+ super(wrapped);
+ this.wrapped = wrapped;
+ }
+
+
+ // ----------------------------------------------------- Instance Variables
+
+
+ /**
+ * Wrapped PrintStream.
+ */
+ protected PrintStream wrapped = null;
+
+
+ /**
+ * Thread <-> PrintStream associations.
+ */
+ protected static ThreadLocal streams = new ThreadLocal();
+
+
+ /**
+ * Thread <-> ByteArrayOutputStream associations.
+ */
+ protected static ThreadLocal data = new ThreadLocal();
+
+
+ // --------------------------------------------------------- Public Methods
+
+
+ public PrintStream getWrapped() {
+ return wrapped;
+ }
+
+ /**
+ * Start capturing thread's output.
+ */
+ public static void setThread() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ data.set(baos);
+ streams.set(new PrintStream(baos));
+ }
+
+
+ /**
+ * Stop capturing thread's output and return captured data as a String.
+ */
+ public static String unsetThread() {
+ ByteArrayOutputStream baos =
+ (ByteArrayOutputStream) data.get();
+ if (baos == null) {
+ return null;
+ }
+ streams.set(null);
+ data.set(null);
+ return baos.toString();
+ }
+
+
+ // ------------------------------------------------------ Protected Methods
+
+
+ /**
+ * Find PrintStream to which the output must be written to.
+ */
+ protected PrintStream findStream() {
+ PrintStream ps = (PrintStream) streams.get();
+ if (ps == null) {
+ ps = wrapped;
+ }
+ return ps;
+ }
+
+
+ // ---------------------------------------------------- PrintStream Methods
+
+
+ public void flush() {
+ findStream().flush();
+ }
+
+ public void close() {
+ findStream().close();
+ }
+
+ public boolean checkError() {
+ return findStream().checkError();
+ }
+
+ protected void setError() {
+ //findStream().setError();
+ }
+
+ public void write(int b) {
+ findStream().write(b);
+ }
+
+ public void write(byte[] b)
+ throws IOException {
+ findStream().write(b);
+ }
+
+ public void write(byte[] buf, int off, int len) {
+ findStream().write(buf, off, len);
+ }
+
+ public void print(boolean b) {
+ findStream().print(b);
+ }
+
+ public void print(char c) {
+ findStream().print(c);
+ }
+
+ public void print(int i) {
+ findStream().print(i);
+ }
+
+ public void print(long l) {
+ findStream().print(l);
+ }
+
+ public void print(float f) {
+ findStream().print(f);
+ }
+
+ public void print(double d) {
+ findStream().print(d);
+ }
+
+ public void print(char[] s) {
+ findStream().print(s);
+ }
+
+ public void print(String s) {
+ findStream().print(s);
+ }
+
+ public void print(Object obj) {
+ findStream().print(obj);
+ }
+
+ public void println() {
+ findStream().println();
+ }
+
+ public void println(boolean x) {
+ findStream().println(x);
+ }
+
+ public void println(char x) {
+ findStream().println(x);
+ }
+
+ public void println(int x) {
+ findStream().println(x);
+ }
+
+ public void println(long x) {
+ findStream().println(x);
+ }
+
+ public void println(float x) {
+ findStream().println(x);
+ }
+
+ public void println(double x) {
+ findStream().println(x);
+ }
+
+ public void println(char[] x) {
+ findStream().println(x);
+ }
+
+ public void println(String x) {
+ findStream().println(x);
+ }
+
+ public void println(Object x) {
+ findStream().println(x);
+ }
+
+ }
+
+}
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=819435&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 00:43:34 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.jasper.compiler;
+
+
+import java.util.HashMap;
+
+import org.apache.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=819435&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 00:43:34 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.jasper.compiler;
+
+import org.apache.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=819435&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 00:43:34 2009
@@ -0,0 +1,551 @@
+/*
+ * 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.jasper.compiler;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.jasper.JasperException;
+import org.apache.jasper.JspCompilationContext;
+import org.apache.jasper.Options;
+import org.apache.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());
+
+ if (ctxt.isPrototypeMode()) {
+ // generate prototype .java file for the tag file
+ writer = setupContextWriter(javaFileName);
+ 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(javaFileName);
+ 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);
+
+ 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(String javaFileName)
+ throws FileNotFoundException, JasperException {
+ ServletWriter writer;
+ // Setup the ServletWriter
+ String javaEncoding = ctxt.getOptions().getJavaEncoding();
+ OutputStreamWriter osw = null;
+
+ try {
+ osw = new OutputStreamWriter(
+ new FileOutputStream(javaFileName), 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=819435&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 00:43:34 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.jasper.compiler;
+
+import org.apache.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=819435&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 00:43:34 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.jasper.compiler;
+
+import org.xml.sax.Attributes;
+import org.apache.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=819435&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 00:43:34 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.jasper.compiler;
+
+import java.util.*;
+import javax.servlet.jsp.tagext.FunctionInfo;
+import org.apache.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.jasper.runtime.ProtectedFunctionMapper " + decName + ";\n");
+
+ ds.append(" " + decName + "= ");
+ ds.append("org.apache.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++;
+ }
+ }
+}
+
Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/ELNode.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/ELNode.java?rev=819435&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/ELNode.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/ELNode.java Mon Sep 28 00:43:34 2009
@@ -0,0 +1,255 @@
+/*
+ * 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.jasper.compiler;
+
+import java.util.*;
+import javax.servlet.jsp.tagext.FunctionInfo;
+import org.apache.jasper.JasperException;
+
+/**
+ * This class defines internal representation for an EL Expression
+ *
+ * It currently only defines functions. It can be expanded to define
+ * all the components of an EL expression, if need to.
+ *
+ * @author Kin-man Chung
+ */
+
+abstract class ELNode {
+
+ abstract public void accept(Visitor v) throws JasperException;
+
+ /**
+ * Child classes
+ */
+
+
+ /**
+ * Represents an EL expression: anything in ${ and }.
+ */
+ public static class Root extends ELNode {
+
+ private ELNode.Nodes expr;
+ private char type;
+
+ Root(ELNode.Nodes expr, char type) {
+ this.expr = expr;
+ this.type = type;
+ }
+
+ public void accept(Visitor v) throws JasperException {
+ v.visit(this);
+ }
+
+ public ELNode.Nodes getExpression() {
+ return expr;
+ }
+
+ public char getType() {
+ return type;
+ }
+ }
+
+ /**
+ * Represents text outside of EL expression.
+ */
+ public static class Text extends ELNode {
+
+ private String text;
+
+ Text(String text) {
+ this.text = text;
+ }
+
+ public void accept(Visitor v) throws JasperException {
+ v.visit(this);
+ }
+
+ public String getText() {
+ return text;
+ }
+ }
+
+ /**
+ * Represents anything in EL expression, other than functions, including
+ * function arguments etc
+ */
+ public static class ELText extends ELNode {
+
+ private String text;
+
+ ELText(String text) {
+ this.text = text;
+ }
+
+ public void accept(Visitor v) throws JasperException {
+ v.visit(this);
+ }
+
+ public String getText() {
+ return text;
+ }
+ }
+
+ /**
+ * Represents a function
+ * Currently only include the prefix and function name, but not its
+ * arguments.
+ */
+ public static class Function extends ELNode {
+
+ private String prefix;
+ private String name;
+ private String uri;
+ private FunctionInfo functionInfo;
+ private String methodName;
+ private String[] parameters;
+
+ Function(String prefix, String name) {
+ this.prefix = prefix;
+ this.name = name;
+ }
+
+ public void accept(Visitor v) throws JasperException {
+ v.visit(this);
+ }
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setUri(String uri) {
+ this.uri = uri;
+ }
+
+ public String getUri() {
+ return uri;
+ }
+
+ public void setFunctionInfo(FunctionInfo f) {
+ this.functionInfo = f;
+ }
+
+ public FunctionInfo getFunctionInfo() {
+ return functionInfo;
+ }
+
+ public void setMethodName(String methodName) {
+ this.methodName = methodName;
+ }
+
+ public String getMethodName() {
+ return methodName;
+ }
+
+ public void setParameters(String[] parameters) {
+ this.parameters = parameters;
+ }
+
+ public String[] getParameters() {
+ return parameters;
+ }
+ }
+
+ /**
+ * An ordered list of ELNode.
+ */
+ public static class Nodes {
+
+ /* Name used for creating a map for the functions in this
+ EL expression, for communication to Generator.
+ */
+ String mapName = null; // The function map associated this EL
+ private List<ELNode> list;
+
+ public Nodes() {
+ list = new ArrayList<ELNode>();
+ }
+
+ public void add(ELNode en) {
+ list.add(en);
+ }
+
+ /**
+ * Visit the nodes in the list with the supplied visitor
+ * @param v The visitor used
+ */
+ public void visit(Visitor v) throws JasperException {
+ Iterator<ELNode> iter = list.iterator();
+ while (iter.hasNext()) {
+ ELNode n = iter.next();
+ n.accept(v);
+ }
+ }
+
+ public Iterator<ELNode> iterator() {
+ return list.iterator();
+ }
+
+ public boolean isEmpty() {
+ return list.size() == 0;
+ }
+
+ /**
+ * @return true if the expression contains a ${...}
+ */
+ public boolean containsEL() {
+ Iterator<ELNode> iter = list.iterator();
+ while (iter.hasNext()) {
+ ELNode n = iter.next();
+ if (n instanceof Root) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void setMapName(String name) {
+ this.mapName = name;
+ }
+
+ public String getMapName() {
+ return mapName;
+ }
+
+ }
+
+ /*
+ * A visitor class for traversing ELNodes
+ */
+ public static class Visitor {
+
+ public void visit(Root n) throws JasperException {
+ n.getExpression().visit(this);
+ }
+
+ public void visit(Function n) throws JasperException {
+ }
+
+ public void visit(Text n) throws JasperException {
+ }
+
+ public void visit(ELText n) throws JasperException {
+ }
+ }
+}
+
Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/ELParser.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/ELParser.java?rev=819435&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/ELParser.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/compiler/ELParser.java Mon Sep 28 00:43:34 2009
@@ -0,0 +1,382 @@
+/*
+ * 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.jasper.compiler;
+
+/**
+ * This class implements a parser for EL expressions.
+ *
+ * It takes strings of the form xxx${..}yyy${..}zzz etc, and turn it into a
+ * ELNode.Nodes.
+ *
+ * Currently, it only handles text outside ${..} and functions in ${ ..}.
+ *
+ * @author Kin-man Chung
+ */
+
+public class ELParser {
+
+ private Token curToken; // current token
+
+ private ELNode.Nodes expr;
+
+ private ELNode.Nodes ELexpr;
+
+ private int index; // Current index of the expression
+
+ private String expression; // The EL expression
+
+ private char type;
+
+ private boolean escapeBS; // is '\' an escape char in text outside EL?
+
+ private static final String reservedWords[] = { "and", "div", "empty",
+ "eq", "false", "ge", "gt", "instanceof", "le", "lt", "mod", "ne",
+ "not", "null", "or", "true" };
+
+ public ELParser(String expression) {
+ index = 0;
+ this.expression = expression;
+ expr = new ELNode.Nodes();
+ }
+
+ /**
+ * Parse an EL expression
+ *
+ * @param expression
+ * The input expression string of the form Char* ('${' Char*
+ * '}')* Char*
+ * @return Parsed EL expression in ELNode.Nodes
+ */
+ public static ELNode.Nodes parse(String expression) {
+ ELParser parser = new ELParser(expression);
+ while (parser.hasNextChar()) {
+ String text = parser.skipUntilEL();
+ if (text.length() > 0) {
+ parser.expr.add(new ELNode.Text(text));
+ }
+ ELNode.Nodes elexpr = parser.parseEL();
+ if (!elexpr.isEmpty()) {
+ parser.expr.add(new ELNode.Root(elexpr, parser.type));
+ }
+ }
+ return parser.expr;
+ }
+
+ /**
+ * Parse an EL expression string '${...}'
+ *
+ * @return An ELNode.Nodes representing the EL expression TODO: Currently
+ * only parsed into functions and text strings. This should be
+ * rewritten for a full parser.
+ */
+ private ELNode.Nodes parseEL() {
+
+ StringBuffer buf = new StringBuffer();
+ ELexpr = new ELNode.Nodes();
+ while (hasNext()) {
+ curToken = nextToken();
+ if (curToken instanceof Char) {
+ if (curToken.toChar() == '}') {
+ break;
+ }
+ buf.append(curToken.toChar());
+ } else {
+ // Output whatever is in buffer
+ if (buf.length() > 0) {
+ ELexpr.add(new ELNode.ELText(buf.toString()));
+ }
+ if (!parseFunction()) {
+ ELexpr.add(new ELNode.ELText(curToken.toString()));
+ }
+ }
+ }
+ if (buf.length() > 0) {
+ ELexpr.add(new ELNode.ELText(buf.toString()));
+ }
+
+ return ELexpr;
+ }
+
+ /**
+ * Parse for a function FunctionInvokation ::= (identifier ':')? identifier
+ * '(' (Expression (,Expression)*)? ')' Note: currently we don't parse
+ * arguments
+ */
+ private boolean parseFunction() {
+ if (!(curToken instanceof Id) || isELReserved(curToken.toString())) {
+ return false;
+ }
+ String s1 = null; // Function prefix
+ String s2 = curToken.toString(); // Function name
+ int mark = getIndex();
+ if (hasNext()) {
+ Token t = nextToken();
+ if (t.toChar() == ':') {
+ if (hasNext()) {
+ Token t2 = nextToken();
+ if (t2 instanceof Id) {
+ s1 = s2;
+ s2 = t2.toString();
+ if (hasNext()) {
+ t = nextToken();
+ }
+ }
+ }
+ }
+ if (t.toChar() == '(') {
+ ELexpr.add(new ELNode.Function(s1, s2));
+ return true;
+ }
+ }
+ setIndex(mark);
+ return false;
+ }
+
+ /**
+ * Test if an id is a reserved word in EL
+ */
+ private boolean isELReserved(String id) {
+ int i = 0;
+ int j = reservedWords.length;
+ while (i < j) {
+ int k = (i + j) / 2;
+ int result = reservedWords[k].compareTo(id);
+ if (result == 0) {
+ return true;
+ }
+ if (result < 0) {
+ i = k + 1;
+ } else {
+ j = k;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Skip until an EL expression ('${' || '#{') is reached, allowing escape
+ * sequences '\\' and '\$' and '\#'.
+ *
+ * @return The text string up to the EL expression
+ */
+ private String skipUntilEL() {
+ char prev = 0;
+ StringBuffer buf = new StringBuffer();
+ while (hasNextChar()) {
+ char ch = nextChar();
+ if (prev == '\\') {
+ prev = 0;
+ if (ch == '\\') {
+ buf.append('\\');
+ if (!escapeBS)
+ prev = '\\';
+ } else if (ch == '$' || ch == '#') {
+ buf.append(ch);
+ }
+ // else error!
+ } else if (prev == '$' || prev == '#') {
+ if (ch == '{') {
+ this.type = prev;
+ prev = 0;
+ break;
+ }
+ buf.append(prev);
+ prev = 0;
+ }
+ if (ch == '\\' || ch == '$' || ch == '#') {
+ prev = ch;
+ } else {
+ buf.append(ch);
+ }
+ }
+ if (prev != 0) {
+ buf.append(prev);
+ }
+ return buf.toString();
+ }
+
+ /*
+ * @return true if there is something left in EL expression buffer other
+ * than white spaces.
+ */
+ private boolean hasNext() {
+ skipSpaces();
+ return hasNextChar();
+ }
+
+ /*
+ * @return The next token in the EL expression buffer.
+ */
+ private Token nextToken() {
+ skipSpaces();
+ if (hasNextChar()) {
+ char ch = nextChar();
+ if (Character.isJavaIdentifierStart(ch)) {
+ StringBuffer buf = new StringBuffer();
+ buf.append(ch);
+ while ((ch = peekChar()) != -1
+ && Character.isJavaIdentifierPart(ch)) {
+ buf.append(ch);
+ nextChar();
+ }
+ return new Id(buf.toString());
+ }
+
+ if (ch == '\'' || ch == '"') {
+ return parseQuotedChars(ch);
+ } else {
+ // For now...
+ return new Char(ch);
+ }
+ }
+ return null;
+ }
+
+ /*
+ * Parse a string in single or double quotes, allowing for escape sequences
+ * '\\', and ('\"', or "\'")
+ */
+ private Token parseQuotedChars(char quote) {
+ StringBuffer buf = new StringBuffer();
+ buf.append(quote);
+ while (hasNextChar()) {
+ char ch = nextChar();
+ if (ch == '\\') {
+ ch = nextChar();
+ if (ch == '\\' || ch == quote) {
+ buf.append(ch);
+ }
+ // else error!
+ } else if (ch == quote) {
+ buf.append(ch);
+ break;
+ } else {
+ buf.append(ch);
+ }
+ }
+ return new QuotedString(buf.toString());
+ }
+
+ /*
+ * A collection of low level parse methods dealing with character in the EL
+ * expression buffer.
+ */
+
+ private void skipSpaces() {
+ while (hasNextChar()) {
+ if (expression.charAt(index) > ' ')
+ break;
+ index++;
+ }
+ }
+
+ private boolean hasNextChar() {
+ return index < expression.length();
+ }
+
+ private char nextChar() {
+ if (index >= expression.length()) {
+ return (char) -1;
+ }
+ return expression.charAt(index++);
+ }
+
+ private char peekChar() {
+ if (index >= expression.length()) {
+ return (char) -1;
+ }
+ return expression.charAt(index);
+ }
+
+ private int getIndex() {
+ return index;
+ }
+
+ private void setIndex(int i) {
+ index = i;
+ }
+
+ /*
+ * Represents a token in EL expression string
+ */
+ private static class Token {
+
+ char toChar() {
+ return 0;
+ }
+
+ public String toString() {
+ return "";
+ }
+ }
+
+ /*
+ * Represents an ID token in EL
+ */
+ private static class Id extends Token {
+ String id;
+
+ Id(String id) {
+ this.id = id;
+ }
+
+ public String toString() {
+ return id;
+ }
+ }
+
+ /*
+ * Represents a character token in EL
+ */
+ private static class Char extends Token {
+
+ private char ch;
+
+ Char(char ch) {
+ this.ch = ch;
+ }
+
+ char toChar() {
+ return ch;
+ }
+
+ public String toString() {
+ return (new Character(ch)).toString();
+ }
+ }
+
+ /*
+ * Represents a quoted (single or double) string token in EL
+ */
+ private static class QuotedString extends Token {
+
+ private String value;
+
+ QuotedString(String v) {
+ this.value = v;
+ }
+
+ public String toString() {
+ return value;
+ }
+ }
+
+ public char getType() {
+ return type;
+ }
+}