You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ki...@apache.org on 2002/07/16 21:30:53 UTC
cvs commit: jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/xmlparser ParserUtils.java
kinman 2002/07/16 12:30:53
Modified: jasper2 BUILDING.txt build.xml
jasper2/src/share/org/apache/jasper Constants.java
EmbededServletOptions.java Options.java
jasper2/src/share/org/apache/jasper/compiler Collector.java
Compiler.java Dumper.java Generator.java
JspDocumentParser.java JspReader.java JspUtil.java
Node.java PageDataImpl.java PageInfo.java
Parser.java TagConstants.java
TagLibraryInfoImpl.java Validator.java
jasper2/src/share/org/apache/jasper/resources
messages.properties
jasper2/src/share/org/apache/jasper/runtime
JspRuntimeLibrary.java PageContextImpl.java
jasper2/src/share/org/apache/jasper/xmlparser
ParserUtils.java
Added: jasper2/src/share/org/apache/jasper/compiler
SmapGenerator.java SmapStratum.java SmapUtil.java
jasper2/src/share/org/apache/jasper/runtime
ExpressionEvaluatorImpl.java
ExpressionEvaluatorManager.java
JspFragmentHelper.java
Log:
- Merged sources with jsr152 EG repository.
This is a JSP 2.0 early access 1 release code base. Supproted features
* New JSP 2.0 APIs
* Expression Language
* JSR-45 Debugging
* JSP 2.0 Tag Library Descriptors
* Support for <jsp:attribute>, <jsp:body> Standard Actions
* SimpleTag Handler Support
* JSP Fragments
JSP 2.0 features not yet supported:
* Tag Files
* Attributes with Dynamic Names
* JSP Configuration
Revision Changes Path
1.3 +67 -7 jakarta-tomcat-jasper/jasper2/BUILDING.txt
Index: BUILDING.txt
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/BUILDING.txt,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- BUILDING.txt 19 Apr 2002 20:35:01 -0000 1.2
+++ BUILDING.txt 16 Jul 2002 19:30:51 -0000 1.3
@@ -1,13 +1,73 @@
-1. Go to a tomcat 4.0/4.1 repository
+$Id$
- cd jakarta-tomcat-4.0
+ Building The Tomcat 4.0 Servlet/JSP Container With Jasper2
+ ==========================================================
-2. Edit jakarta-tomcat-4.0/build.properties to include this line
+Jasper2 is a reimplementation of the JSP Container for Tomcat 4.0 that is
+cleaner and easier to extend. It also forms the basis of the JSP 2.0
+implementation. Jasper2 will eventually replace the Jasper module
+built into the Tomcat distribution. In the meanwhile, you can build Jasper2
+alongside Tomcat 4.0 and replace Jasper.
- jasper.home=../jakarta-tomcat-jasper/jasper2
+To build Jasper2, first make sure you have a working build environment for
+Tomcat 4.0. You can do so by following the instructions in the BUILDING.txt
+file in the Tomcat 4.0 workspace.
-3. Build tomcat 4
+Once you have a working build environment for Tomcat 4.0, do the following:
- ant clean
- ant
+(0) Modify your build.properties file in Tomcat 4.0
+* Add a line to your build.properties file for Tomcat 4.0 that
+ points the build script to Jasper2 instead of Jasper, as follows:
+
+ # ----- Jakarta Tomcat Jasper source path -----
+ jasper.home=${base.path}/jakarta-tomcat-jasper/jasper2
+
+ This directory is relative to the location of build.xml for Tomcat.
+
+(1) Use anonymous CVS (as described on the Jakarta web site at
+ <http://jakarta.apache.org/site/cvsindex.html>, or download a source
+ distribution of the "jakarta-taglibs" repository (7/15/2002 or later).
+
+ http://jakarta.apache.org/builds/jakarta-taglibs/nightly/projects/standard/
+
+* Unpack the package into a convenient location so that
+ it resides in its own subdirectory.
+
+* Follow the instructions in BUILDING.txt to set up the correct
+ build environment. Make sure you have a valid build.properties file.
+
+* Change directory to "standard" and build the special JSP 2.0 build target
+ for producing the JSP 2.0 Expression Language Evaluator. The ant target
+ is called jsp20el.dist.
+
+ cd standard
+ ant jsp20el.dist
+
+(2) Customize Build Properties for this subproject
+
+Most Jakarta subprojects allow you to customize Ant properties (with default
+values defined in the "build.xml" file. This is done by creating a text file
+named "build.properties" in the source distribution directory (for property
+definitions local to this subproject) and/or your user home directory (for
+property definitions shared across subprojects). You can use the included
+"build.properties.sample" file as a starting point for this.
+
+Jasper2 has external dependencies that are satisfied by configuring
+appropriate values in your build.properties file. The easiest
+way to satisfy these dependencies is to copy the "build.properties.sample"
+file (in the top-level Tomcat source directory) to "build.properties", and
+then edit it to suit your environment. On Unix, this would be done as:
+
+ cd ${jasper2.source}
+ cp build.properties.sample build.properties
+ emacs build.properties
+
+NOTE: Be *sure* that you do not check "build.properties" in to the CVS
+repository. This file is local to your own development environment, and
+each developer will have their own version.
+
+(3) Build A Binary Distribution
+
+Jasper2 is built as part of Tomcat. Follow the instructions in
+BUILDING.txt in the Tomcat project to build Tomcat with Jasper2.
1.8 +3 -0 jakarta-tomcat-jasper/jasper2/build.xml
Index: build.xml
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/build.xml,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- build.xml 12 Jun 2002 23:15:27 -0000 1.7
+++ build.xml 16 Jul 2002 19:30:51 -0000 1.8
@@ -28,6 +28,7 @@
<pathelement location="${xmlParserAPIs.jar}"/>
<pathelement location="${commons-collections.jar}"/>
<pathelement location="${jasper.build}/shared/classes"/>
+ <pathelement location="${jsp20el.jar}"/>
</path>
<!-- Construct unit tests classpath -->
@@ -116,6 +117,8 @@
</fileset>
</jar>
+ <!-- Supporting JAR Files -->
+ <copy todir="${jasper.deploy}/common/lib" file="${jsp20el.jar}"/>
</target>
1.5 +18 -0 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/Constants.java
Index: Constants.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/Constants.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- Constants.java 22 Jun 2002 10:19:03 -0000 1.4
+++ Constants.java 16 Jul 2002 19:30:51 -0000 1.5
@@ -67,6 +67,8 @@
*
* @author Anil K. Vijendran
* @author Harish Prabandham
+ * @author Shawn Bayern
+ * @author Mark Roth
*/
public class Constants {
/**
@@ -209,6 +211,22 @@
public static final String IE_PLUGIN_URL =
"http://java.sun.com/products/plugin/1.2.2/jinstall-1_2_2-win.cab#Version=1,2,2,0";
+
+ /**
+ * Information about conduit to EL interpreter.
+ * EL_INTERPRETER_CONDUIT_CLASS: name of class
+ * EL_INTERPRETER_CONDUIT_METHOD: name of static method within class
+ */
+ public static final String EL_INTERPRETER_CONDUIT_CLASS =
+ "org.apache.jasper.runtime.ExpressionEvaluatorManager";
+ public static final String EL_INTERPRETER_CONDUIT_METHOD =
+ "evaluate";
+
+ /**
+ * Prefix to use for generated temporary variable names
+ */
+ public static final String TEMP_VARIABLE_NAME_PREFIX =
+ "_jspx_temp";
/**
* This is where all our error messages and such are stored.
1.9 +16 -18 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/EmbededServletOptions.java
Index: EmbededServletOptions.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/EmbededServletOptions.java,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- EmbededServletOptions.java 26 Jun 2002 16:50:37 -0000 1.8
+++ EmbededServletOptions.java 16 Jul 2002 19:30:51 -0000 1.9
@@ -105,7 +105,7 @@
* Determines whether tag handler pooling is enabled.
*/
public boolean poolingEnabled = true;
-
+
/**
* Do you want support for "mapped" files? This will generate
* servlet that has a print statement per line of the JSP file.
@@ -190,11 +190,11 @@
public boolean getLargeFile() {
return largeFile;
}
-
+
public boolean isPoolingEnabled() {
return poolingEnabled;
}
-
+
/**
* Are we supporting HTML mapped servlets?
*/
@@ -283,9 +283,7 @@
* Create an EmbededServletOptions object using data available from
* ServletConfig and ServletContext.
*/
- public EmbededServletOptions(ServletConfig config,
- ServletContext context) {
-
+ public EmbededServletOptions(ServletConfig config, ServletContext context) {
Enumeration enum=config.getInitParameterNames();
while( enum.hasMoreElements() ) {
String k=(String)enum.nextElement();
@@ -317,15 +315,6 @@
else Constants.message ("jsp.warning.largeFile", Logger.WARNING);
}
- String mapFile = config.getInitParameter("mappedfile");
- if (mapFile != null) {
- if (mapFile.equalsIgnoreCase("true"))
- this.mappedFile = true;
- else if (mapFile.equalsIgnoreCase("false"))
- this.mappedFile = false;
- else Constants.message ("jsp.warning.mappedFile", Logger.WARNING);
- }
-
poolingEnabled = true;
String poolingEnabledParam
= config.getInitParameter("enablePooling");
@@ -335,6 +324,15 @@
this.poolingEnabled = false;
else Constants.message("jsp.warning.enablePooling",
Logger.WARNING);
+ }
+
+ String mapFile = config.getInitParameter("mappedfile");
+ if (mapFile != null) {
+ if (mapFile.equalsIgnoreCase("true"))
+ this.mappedFile = true;
+ else if (mapFile.equalsIgnoreCase("false"))
+ this.mappedFile = false;
+ else Constants.message ("jsp.warning.mappedFile", Logger.WARNING);
}
String senderr = config.getInitParameter("sendErrToClient");
1.7 +3 -18 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/Options.java
Index: Options.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/Options.java,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- Options.java 26 Jun 2002 16:50:38 -0000 1.6
+++ Options.java 16 Jul 2002 19:30:51 -0000 1.7
@@ -77,13 +77,11 @@
*/
public interface Options {
-
/**
* Are we keeping generated code around?
*/
public boolean getKeepGenerated();
-
/**
* Are we supporting large files?
*/
@@ -99,62 +97,52 @@
*/
public boolean getMappedFile();
-
/**
* Should errors be sent to client or thrown into stderr?
*/
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();
-
/**
* JSP reloading check ?
*/
public boolean getReloading();
-
/**
* 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 cache for the location of the TLD's
* for the various tag libraries 'exposed'
@@ -168,12 +156,9 @@
*/
public TldLocationsCache getTldLocationsCache();
-
/**
* Java platform encoding to generate the JSP
* page servlet.
*/
public String getJavaEncoding();
-
-
}
1.3 +44 -21 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Collector.java
Index: Collector.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Collector.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- Collector.java 13 Jun 2002 22:56:11 -0000 1.2
+++ Collector.java 16 Jul 2002 19:30:51 -0000 1.3
@@ -69,6 +69,7 @@
* the PageInfo object.
*
* @author Kin-man Chung
+ * @author Mark Roth
*/
public class Collector {
@@ -137,12 +138,24 @@
}
public void visit(Node.CustomTag n) throws JasperException {
-
curTagNesting++;
if (curTagNesting > maxTagNesting) {
maxTagNesting = curTagNesting;
}
+
+ // Check to see what kinds of element we see as child elements
+ checkSeen( n.getChildInfo(), n );
+ curTagNesting--;
+ }
+
+ /**
+ * 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;
@@ -156,31 +169,35 @@
hasScriptingVars = false;
// Scan attribute list for expressions
- Node.JspAttribute[] attrs = n.getJspAttributes();
- for (int i = 0; i < attrs.length; i++) {
- if (attrs[i].isExpression()) {
- scriptingElementSeen = true;
- break;
- }
- }
+ if( n instanceof Node.CustomTag ) {
+ Node.CustomTag ct = (Node.CustomTag)n;
+ Node.JspAttribute[] attrs = ct.getJspAttributes();
+ for (int i = 0; i < attrs.length; i++) {
+ if (attrs[i].isExpression()) {
+ scriptingElementSeen = true;
+ break;
+ }
+ }
+ }
visitBody(n);
- if (!hasScriptingVars) {
+ if( (n instanceof Node.CustomTag) && !hasScriptingVars) {
+ Node.CustomTag ct = (Node.CustomTag)n;
// For some reason, varInfos is null when var is not defined
// in TEI, but tagVarInfos is empty array when var is not
// defined in tld.
- hasScriptingVars = n.getVariableInfos() != null ||
- (n.getTagVariableInfos() != null
- && n.getTagVariableInfos().length > 0);
+ hasScriptingVars = ct.getVariableInfos() != null ||
+ (ct.getTagVariableInfos() != null
+ && ct.getTagVariableInfos().length > 0);
}
// Record if the tag element and its body contains any scriptlet.
- n.setScriptless(! scriptingElementSeen);
- n.setHasUsebean(usebeanSeen);
- n.setHasIncludeAction(includeActionSeen);
- n.setHasSetProperty(setPropertySeen);
- n.setHasScriptingVars(hasScriptingVars);
+ ci.setScriptless(! scriptingElementSeen);
+ ci.setHasUsebean(usebeanSeen);
+ ci.setHasIncludeAction(includeActionSeen);
+ ci.setHasSetProperty(setPropertySeen);
+ ci.setHasScriptingVars(hasScriptingVars);
// Propagate value of scriptingElementSeen up.
scriptingElementSeen = scriptingElementSeen || scriptingElementSeenSave;
@@ -188,10 +205,16 @@
setPropertySeen = setPropertySeen || setPropertySeenSave;
includeActionSeen = includeActionSeen || includeActionSeenSave;
hasScriptingVars = hasScriptingVars || hasScriptingVarsSave;
-
- curTagNesting--;
}
+ 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;
}
1.19 +13 -3 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Compiler.java
Index: Compiler.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Compiler.java,v
retrieving revision 1.18
retrieving revision 1.19
diff -u -r1.18 -r1.19
--- Compiler.java 21 Jun 2002 02:28:47 -0000 1.18
+++ Compiler.java 16 Jul 2002 19:30:51 -0000 1.19
@@ -94,6 +94,7 @@
* @author Pierre Delisle
* @author Kin-man Chung
* @author Remy Maucherat
+ * @author Mark Roth
*/
public class Compiler {
@@ -223,6 +224,9 @@
ServletWriter writer = new ServletWriter(new PrintWriter(osw));
ctxt.setWriter(writer);
+ // Reset the temporary variable counter for the generator.
+ JspUtil.resetTemporaryVariableName();
+
// Parse the file
ParserController parserCtl = new ParserController(ctxt, this);
pageNodes = parserCtl.parse(ctxt.getJspFile());
@@ -239,6 +243,9 @@
// generate servlet .java file
Generator.generate(writer, this, pageNodes);
writer.close();
+
+ //JSR45 Support - note this needs to be checked by a JSR45 guru
+ SmapUtil.generateSmap(ctxt, pageNodes, true);
}
/**
@@ -310,6 +317,9 @@
if(errorReport!=null )
errDispatcher.javacError(errorReport, javaFileName, pageNodes);
}
+
+ //JSR45 Support - note this needs to be checked by a JSR45 guru
+ SmapUtil.installSmap(ctxt);
}
/**
1.2 +36 -5 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Dumper.java
Index: Dumper.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Dumper.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- Dumper.java 28 Mar 2002 18:46:15 -0000 1.1
+++ Dumper.java 16 Jul 2002 19:30:51 -0000 1.2
@@ -147,8 +147,9 @@
}
public void visit(Node.IncludeAction n) throws JasperException {
- printAttributes("<jsp:include", n.getAttributes(), "/>");
+ printAttributes("<jsp:include", n.getAttributes(), ">");
dumpBody(n);
+ printString("</jsp:include>");
}
public void visit(Node.ForwardAction n) throws JasperException {
@@ -162,7 +163,9 @@
}
public void visit(Node.SetProperty n) throws JasperException {
- printAttributes("<jsp:setProperty", n.getAttributes(), "/>");
+ printAttributes("<jsp:setProperty", n.getAttributes(), ">");
+ dumpBody(n);
+ printString("</jsp:setProperty>");
}
public void visit(Node.UseBean n) throws JasperException {
@@ -176,6 +179,34 @@
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.getName(), n.getAttributes(), ">");
1.36 +951 -165 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Generator.java
Index: Generator.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Generator.java,v
retrieving revision 1.35
retrieving revision 1.36
diff -u -r1.35 -r1.36
--- Generator.java 27 Jun 2002 17:32:17 -0000 1.35
+++ Generator.java 16 Jul 2002 19:30:51 -0000 1.36
@@ -83,6 +83,8 @@
* @author Pierre Delisle
* @author Kin-man Chung
* @author Jan Luehe
+ * @author Shawn Bayern
+ * @author Mark Roth
* @author Denis Benoit
*/
@@ -90,6 +92,7 @@
private ServletWriter out;
private MethodsBuffer methodsBuffer;
+ private HelperClassBuffer helperClassBuffer;
private ErrorDispatcher err;
private BeanRepository beanInfo;
private JspCompilationContext ctxt;
@@ -102,13 +105,24 @@
* @param s the input string
* @return quoted and escaped string, per Java rule
*/
- private static String quote(String s) {
+ static String quote(String s) {
if (s == null)
return "null";
+
+ return '"' + escape( s ) + '"';
+ }
+
+ /**
+ * @param s the input string
+ * @return escaped string, per Java rule
+ */
+ static String escape(String s) {
+
+ if (s == null)
+ return "";
StringBuffer b = new StringBuffer();
- b.append('"');
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '"')
@@ -122,7 +136,6 @@
else
b.append(c);
}
- b.append('"');
return b.toString();
}
@@ -391,6 +404,9 @@
out.printil("}");
out.println();
}
+
+ // Static data for EL function maps:
+ generateELFunctionMap();
// Class variable declarations
@@ -439,7 +455,7 @@
// Local variable declarations
out.printil("JspFactory _jspxFactory = null;");
- out.printil("javax.servlet.jsp.PageContext pageContext = null;");
+ out.printil("PageContext pageContext = null;");
if (pageInfo.isSession())
out.printil("HttpSession session = null;");
@@ -492,6 +508,147 @@
out.println();
}
+ /**
+ * Generates EL Function map section
+ */
+ private void generateELFunctionMap()
+ throws JasperException
+ {
+ Hashtable taglibs = pageInfo.getTagLibraries();
+ Iterator iter = taglibs.keySet().iterator();
+ boolean fnPresent = false;
+
+ // Check to see if at least one function is present.
+ while( iter.hasNext() ) {
+ String key = (String)iter.next();
+ TagLibraryInfo tli = (TagLibraryInfo)taglibs.get( key );
+ if( tli.getFunctions().length > 0 ) {
+ fnPresent = true;
+ break;
+ }
+ }
+
+ out.printil("private static java.util.HashMap _jspx_fnmap = null;");
+ if( fnPresent ) {
+ iter = taglibs.keySet().iterator();
+ out.println();
+ out.printil("static {");
+ out.pushIndent();
+ out.printil("_jspx_fnmap = new java.util.HashMap();");
+ out.printil( "try {" );
+ out.pushIndent();
+ while( iter.hasNext() ) {
+ String key = (String)iter.next();
+ TagLibraryInfo tli = (TagLibraryInfo)taglibs.get( key );
+ FunctionInfo[] fnInfo = tli.getFunctions();
+ String fnPrefix = tli.getPrefixString();
+ out.printil( "// Functions for " + tli.getShortName() );
+ for( int i = 0; i < fnInfo.length; i++ ) {
+ String fnName = fnPrefix + ":" + fnInfo[i].getName();
+ String fnSignature = fnInfo[i].getFunctionSignature();
+ out.printin("_jspx_fnmap.put(");
+ out.print(quote(fnName));
+ out.print(", ");
+ out.print(fnInfo[i].getFunctionClass() +
+ ".class.getDeclaredMethod(");
+
+ try {
+ // Parse function signature, assuming syntax:
+ // <return-type> S <method-name> S? '('
+ // ( <arg-type> ( ',' <arg-type> )* )? ')'
+ String ws = " \t\n\r";
+ StringTokenizer sigTokenizer = new StringTokenizer(
+ fnSignature, ws + "(),", true);
+
+ // Skip <arg-type>:
+ sigTokenizer.nextToken();
+
+ // Skip whitespace and read <method-name>:
+ String methodName;
+ do {
+ methodName = sigTokenizer.nextToken();
+ } while( ws.indexOf( methodName ) != -1 );
+
+ out.print( quote( methodName ) );
+ out.print( ", new Class[] {" );
+
+ // Skip whitespace and read '(':
+ String paren;
+ do {
+ paren = sigTokenizer.nextToken();
+ } while( ws.indexOf( paren ) != -1 );
+
+ if( !paren.equals( "(" ) ) {
+ throw new JasperException( err.getString(
+ "jsp.error.tld.fn.invalid.signature",
+ tli.getShortName(), fnName ) );
+ }
+
+ // ( <arg-type> S? ( ',' S? <arg-type> S? )* )? ')'
+
+ // Skip whitespace and read <arg-type>:
+ String argType;
+ do {
+ argType = sigTokenizer.nextToken();
+ } while( ws.indexOf( argType ) != -1 );
+
+ if( !argType.equals( ")" ) ) {
+ do {
+ if( ",(".indexOf( argType ) != -1 ) {
+ throw new JasperException( err.getString(
+ "jsp.error.tld.fn.invalid.signature",
+ tli.getShortName(), fnName ) );
+ }
+
+ out.print( argType + ".class" );
+
+ String comma;
+ do {
+ comma = sigTokenizer.nextToken();
+ } while( ws.indexOf( comma ) != -1 );
+
+ if( comma.equals( ")" ) ) {
+ break;
+ }
+ if( !comma.equals( "," ) ) {
+ throw new JasperException( err.getString(
+ "jsp.error.tld.fn.invalid.signature",
+ tli.getShortName(), fnName ) );
+ }
+
+ out.print( ", " );
+
+ // <arg-type>
+ do {
+ argType = sigTokenizer.nextToken();
+ } while( ws.indexOf( argType ) != -1 );
+ } while( true );
+ }
+
+ out.println( "} ) );" );
+ }
+ catch( NoSuchElementException e ) {
+ throw new JasperException( err.getString(
+ "jsp.error.tld.fn.invalid.signature",
+ tli.getShortName(), fnName ) );
+ }
+ }
+ }
+ out.popIndent();
+ out.printil( "}" );
+ out.printil( "catch( NoSuchMethodException e ) {" );
+ out.pushIndent();
+ out.printil( "throw new RuntimeException( \"" +
+ "Invalid function mapping - no such method: \" + " +
+ "e.getMessage(), e );" );
+ out.popIndent();
+ out.printil( "}" );
+ out.popIndent();
+ out.printil("}");
+ out.println();
+ }
+ }
+
/*
* Generates the servlet constructor.
*/
@@ -544,18 +701,24 @@
private Hashtable tagVarNumbers;
private String parent;
+ private String simpleTagHandlerVar;
+ private boolean isSimpleTagHandler;
private ServletWriter out;
private MethodsBuffer methodsBuffer;
+ private HelperClassBuffer helperClassBuffer;
private int methodNesting;
/**
* Constructor.
*/
- public GenerateVisitor(ServletWriter out,
- MethodsBuffer methodsBuffer) {
+ public GenerateVisitor(ServletWriter out,
+ MethodsBuffer methodsBuffer,
+ HelperClassBuffer helperClassBuffer )
+ {
this.out = out;
this.methodsBuffer = methodsBuffer;
+ this.helperClassBuffer = helperClassBuffer;
methodNesting = 0;
handlerInfos = new Hashtable();
tagVarNumbers = new Hashtable();
@@ -563,22 +726,38 @@
/**
* Returns an attribute value, optionally URL encoded. If
- * the value is a runtime expression, the result is the string for
- * the expression, otherwise the result is the string literal,
- * quoted and escaped.
+ * the value is a runtime expression, the result is the expression
+ * itself, as a string. If the result is an EL expression, we insert
+ * a call to the interpreter. If the result is a Named Attribute
+ * we insert the generated variable name. Otherwise the result is a
+ * string literal, quoted and escaped.
+ *
* @param attr An JspAttribute object
* @param encode true if to be URL encoded
+ * @param expectedType the expected type for an EL evaluation
+ * (ignored for attributes that aren't EL expressions)
+ * @param defaultPrefix the default prefix for any EL functions
*/
- private String attributeValue(Node.JspAttribute attr, boolean encode) {
+ private String attributeValue(Node.JspAttribute attr,
+ boolean encode,
+ Class expectedType,
+ String defaultPrefix )
+ {
String v = attr.getValue();
- if (v == null)
+ if (!attr.isNamedAttribute() && (v == null))
return "";
- if (attr.isExpression()) {
+ if (attr.isExpression() || attr.isELInterpreterInput()) {
+ if (attr.isELInterpreterInput()) {
+ v = JspUtil.interpreterCall( attr.getValue(),
+ expectedType, "_jspx_fnmap", defaultPrefix );
+ }
if (encode) {
return "java.net.URLEncoder.encode(" + v + ")";
}
return v;
+ } else if( attr.isNamedAttribute() ) {
+ return attr.getNamedAttributeNode().getTemporaryVariableName();
} else {
if (encode) {
v = URLEncoder.encode(v);
@@ -593,7 +772,7 @@
*
* @param n the parent node for the param action nodes.
*/
- private void printParams(Node n, Node.JspAttribute page)
+ private void printParams(Node n, String pageParam, boolean literal)
throws JasperException {
class ParamVisitor extends Node.Visitor {
@@ -610,19 +789,19 @@
out.print(" + \"");
out.print(n.getAttributeValue("name"));
out.print("=\" + ");
- out.print(attributeValue(n.getValue(), true));
+ out.print(attributeValue(n.getValue(), true, String.class,
+ "null" ));
// The separator is '&' after the second use
separator = "\"&\"";
}
}
- String pValue = page.getValue();
String sep;
- if (page.isExpression()) {
- sep = "((" + pValue + ").indexOf('?')>0? '&': '?')";
+ if (literal) {
+ sep = pageParam.indexOf('?')>0? "\"&\"": "\"?\"";
} else {
- sep = pValue.indexOf('?')>0? "\"&\"": "\"?\"";
+ sep = "((" + pageParam + ").indexOf('?')>0? '&': '?')";
}
if (n.getBody() != null) {
n.getBody().visit(new ParamVisitor(sep));
@@ -631,7 +810,7 @@
public void visit(Node.Expression n) throws JasperException {
n.setBeginJavaLine(out.getJavaLine());
- out.printil("out.print(" + new String(n.getText()) + ");");
+ out.printil("out.write(" + new String(n.getText()) + ");");
n.setEndJavaLine(out.getJavaLine());
}
@@ -641,34 +820,139 @@
n.setEndJavaLine(out.getJavaLine());
}
+ public void visit(Node.ELExpression n) throws JasperException {
+ n.setBeginJavaLine(out.getJavaLine());
+ if ( true /*isELEnabled*/ ) {
+ out.printil(
+ "out.write("
+ + JspUtil.interpreterCall(
+ "${" + new String(n.getText()) + "}", String.class,
+ "_jspx_fnmap", "null" )
+ + ");");
+ } else {
+ out.printil("out.write(" +
+ quote("${" + new String(n.getText()) + "}") +
+ ");");
+ }
+ n.setEndJavaLine(out.getJavaLine());
+ }
+
public void visit(Node.IncludeAction n) throws JasperException {
String flush = n.getAttributeValue("flush");
+ Node.JspAttribute page = n.getPage();
boolean isFlush = false; // default to false;
if ("true".equals(flush))
isFlush = true;
n.setBeginJavaLine(out.getJavaLine());
-
- out.printin("JspRuntimeLibrary.include(request, response, ");
- out.print(attributeValue(n.getPage(), false));
- printParams(n, n.getPage());
+
+ String pageParam;
+ if( page.isNamedAttribute() ) {
+ // If the page for jsp:include was specified via
+ // jsp:attribute, first generate code to evaluate
+ // that body.
+ pageParam = generateNamedAttributeValue(
+ page.getNamedAttributeNode() );
+ }
+ else {
+ pageParam = attributeValue(page, false, String.class, "null");
+ }
+
+ // If any of the params have their values specified by
+ // jsp:attribute, prepare those values first.
+ prepareParams( findJspBody( n ) );
+
+ out.printin("JspRuntimeLibrary.include(request, response, " +
+ pageParam );
+ printParams(n, pageParam, page.isLiteral());
out.println(", out, " + isFlush + ");");
n.setEndJavaLine(out.getJavaLine());
}
+
+ /**
+ * Scans through all child nodes of the given parent for
+ * <param> subelements. For each <param> element, if its value
+ * is specified via a Named Attribute (<jsp:attribute>),
+ * generate the code to evaluate those bodies first.
+ * <p>
+ * If parent is null, simply returns.
+ */
+ private void prepareParams( Node parent ) throws JasperException {
+ if( parent == null ) return;
+
+ Node.Nodes subelements = parent.getBody();
+ if( subelements != null ) {
+ for( int i = 0; i < subelements.size(); i++ ) {
+ Node n = subelements.getNode( i );
+ if( n instanceof Node.ParamAction ) {
+ Node.Nodes paramSubElements = n.getBody();
+ for( int j = 0; (paramSubElements != null) &&
+ (j < paramSubElements.size()); j++ )
+ {
+ Node m = paramSubElements.getNode( j );
+ if( m instanceof Node.NamedAttribute ) {
+ generateNamedAttributeValue(
+ (Node.NamedAttribute)m );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Finds the <jsp:body> subelement of the given parent node.
+ * If not found, null is returned.
+ */
+ private Node.JspBody findJspBody( Node parent )
+ throws JasperException
+ {
+ Node.JspBody result = null;
+
+ Node.Nodes subelements = parent.getBody();
+ for( int i = 0; (subelements != null) &&
+ (i < subelements.size()); i++ )
+ {
+ Node n = subelements.getNode( i );
+ if( n instanceof Node.JspBody ) {
+ result = (Node.JspBody)n;
+ break;
+ }
+ }
+
+ return result;
+ }
public void visit(Node.ForwardAction n) throws JasperException {
- String page = n.getAttributeValue("page");
+ Node.JspAttribute page = n.getPage();
n.setBeginJavaLine(out.getJavaLine());
out.printil("if (true) {"); // So that javac won't complain about
out.pushIndent(); // codes after "return"
+
+ String pageParam;
+ if( page.isNamedAttribute() ) {
+ // If the page for jsp:forward was specified via
+ // jsp:attribute, first generate code to evaluate
+ // that body.
+ pageParam = generateNamedAttributeValue(
+ page.getNamedAttributeNode() );
+ }
+ else {
+ pageParam = attributeValue(page, false, String.class, "null");
+ }
+
+ // If any of the params have their values specified by
+ // jsp:attribute, prepare those values first.
+ prepareParams( findJspBody( n ) );
+
out.printin("pageContext.forward(");
- out.print(attributeValue(n.getPage(), false));
- printParams(n, n.getPage());
+ out.print( pageParam );
+ printParams(n, pageParam, page.isLiteral());
out.println(");");
out.printil((methodNesting > 0)? "return true;": "return;");
out.popIndent();
@@ -691,14 +975,14 @@
java.lang.reflect.Method meth =
JspRuntimeLibrary.getReadMethod(bean, property);
String methodName = meth.getName();
- out.printil("out.print(JspRuntimeLibrary.toString(" +
+ out.printil("out.write(JspRuntimeLibrary.toString(" +
"(((" + beanName + ")pageContext.findAttribute(" +
"\"" + name + "\"))." + methodName + "())));");
} else {
// The object could be a custom action with an associated
// VariableInfo entry for this name.
// Get the class name and then introspect at runtime.
- out.printil("out.print(JspRuntimeLibrary.toString" +
+ out.printil("out.write(JspRuntimeLibrary.toString" +
"(JspRuntimeLibrary.handleGetProperty" +
"(pageContext.findAttribute(\"" +
name + "\"), \"" + property + "\")));");
@@ -730,13 +1014,36 @@
out.printil("JspRuntimeLibrary.handleSetProperty(" +
"pageContext.findAttribute(\"" + name + "\"), \""
+ property + "\",");
- out.print(attributeValue(value, false));
+ out.print(attributeValue(value, false, null, "null"));
out.println(");");
+ } else if (value.isELInterpreterInput()) {
+ // We've got to resolve the very call to the interpreter
+ // at runtime since we don't know what type to expect
+ // in the general case; we thus can't hard-wire the call
+ // into the generated code. (XXX We could, however,
+ // optimize the case where the bean is exposed with
+ // <jsp:useBean>, much as the code here does for
+ // getProperty.)
+ out.printil("JspRuntimeLibrary.handleSetPropertyExpression(" +
+ "pageContext.findAttribute(\"" + name + "\"), \""
+ + property + "\", "
+ + quote(value.getValue()) + ", "
+ + "pageContext, _jspx_fnmap);");
+ } else if( value.isNamedAttribute() ) {
+ // If the value for setProperty was specified via
+ // jsp:attribute, first generate code to evaluate
+ // that body.
+ String valueVarName = generateNamedAttributeValue(
+ value.getNamedAttributeNode() );
+ out.println("JspRuntimeLibrary.handleSetProperty(" +
+ "pageContext.findAttribute(\"" + name + "\"), \""
+ + property + "\", " + valueVarName + " );" );
+
} else {
out.printil("JspRuntimeLibrary.introspecthelper(" +
"pageContext.findAttribute(\"" + name + "\"), \"" +
- property + "\",");
- out.print(attributeValue(value, false));
+ property + "\",");
+ out.print(attributeValue(value, false, null, "null"));
out.println(",null, null, false);");
}
@@ -814,7 +1121,17 @@
*/
String className;
if (beanName != null) {
- className = attributeValue(beanName, false);
+ if( beanName.isNamedAttribute() ) {
+ // If the value for beanName was specified via
+ // jsp:attribute, first generate code to evaluate
+ // that body.
+ className = generateNamedAttributeValue(
+ beanName.getNamedAttributeNode() );
+ }
+ else {
+ className = attributeValue(beanName, false,
+ String.class, "null");
+ }
}
else {
// Implies klass is not null
@@ -901,15 +1218,31 @@
else if (name.equalsIgnoreCase ("type"))
name = "java_type";
- String s0 = makeAttr("name", name) + " value=" +
- attributeValue(n.getValue(), false);
-
- if (ie) {
- s0 = "<PARAM" + s0 + '>';
- }
-
n.setBeginJavaLine(out.getJavaLine());
- out.printil("out.println(" + quote(s0) + ");");
+ // XXX - Fixed a bug here - value used to be output
+ // inline, which is only okay if value is not an EL
+ // expression. Also, key/value pairs for the
+ // embed tag were not being generated correctly.
+ // Double check that this is now the correct behavior.
+ if( ie ) {
+ // We want something of the form
+ // out.println( "<PARAM name=\"blah\"
+ // value=\"" + ... + "\">" );
+ out.printil( "out.write( \"<PARAM name=\\\"" +
+ escape( name ) + "\\\" value=\\\"\" + " +
+ attributeValue( n.getValue(), false, String.class,
+ "null" ) + " + \"\\\">\" );" );
+ out.printil( "out.write(\"\n\");" );
+ }
+ else {
+ // We want something of the form
+ // out.print( " blah=\"" + ... + "\"" );
+ out.printil( "out.write( \" " + escape( name ) +
+ "=\\\"\" + " +
+ attributeValue( n.getValue(), false, String.class,
+ "null" ) + " + \"\\\"\" );" );
+ }
+
n.setEndJavaLine(out.getJavaLine());
}
}
@@ -927,6 +1260,30 @@
String codebase = n.getAttributeValue("codebase");
String archive = n.getAttributeValue("archive");
String jreversion = n.getAttributeValue("jreversion");
+
+ String widthStr = null;
+ if( width != null ) {
+ if( width.isNamedAttribute() ) {
+ widthStr = generateNamedAttributeValue(
+ width.getNamedAttributeNode() );
+ }
+ else {
+ widthStr = attributeValue( width, false, String.class,
+ "null" );
+ }
+ }
+
+ String heightStr = null;
+ if( height != null ) {
+ if( height.isNamedAttribute() ) {
+ heightStr = generateNamedAttributeValue(
+ height.getNamedAttributeNode() );
+ }
+ else {
+ heightStr = attributeValue( height, false, String.class,
+ "null" );
+ }
+ }
if (iepluginurl == null)
iepluginurl = Constants.IE_PLUGIN_URL;
@@ -935,50 +1292,66 @@
n.setBeginJavaLine(out.getJavaLine());
+
+ // If any of the params have their values specified by
+ // jsp:attribute, prepare those values first.
+ // Look for a params node and prepare its param subelements:
+ Node.JspBody jspBody = findJspBody( n );
+ if( jspBody != null ) {
+ Node.Nodes subelements = jspBody.getBody();
+ if( subelements != null ) {
+ for( int i = 0; i < subelements.size(); i++ ) {
+ Node m = subelements.getNode( i );
+ if( m instanceof Node.ParamsAction ) {
+ prepareParams( m );
+ break;
+ }
+ }
+ }
+ }
+
+ // XXX - Fixed a bug here - width and height can be set
+ // dynamically. Double-check if this generation is correct.
+
// IE style plugin
// <OBJECT ...>
// First compose the runtime output string
- String s0 = "<OBJECT classid=\"" + ctxt.getOptions().getIeClassId()+
- "\"" + makeAttr("name", name);
- String s1, s2;
- if (width != null) {
- if (width.isExpression()) {
- s1 = quote(s0 + " width=\"") + " + " + width.getValue() +
- " + " + quote("\"");
- } else {
- s1 = quote(s0 + makeAttr("width", width.getValue()));
- }
- } else {
- s1 = quote(s0);
- }
- if (height != null) {
- if (height.isExpression()) {
- s2 = quote(" height=\"") + " + " + height.getValue() +
- " + " + quote("\"");
- } else {
- s2 = quote(makeAttr("height", height.getValue()));
- }
- } else {
- s2 = "\"\"";
- }
- String s3 = quote(makeAttr("hspace", hspace) +
- makeAttr("vspace", vspace) +
- makeAttr("align", align) +
- makeAttr("codebase", iepluginurl) +
- '>');
+ String s0 = "<OBJECT classid=" + ctxt.getOptions().getIeClassId() +
+ makeAttr("name", name);
+
+ String s1 = "";
+ if( width != null ) {
+ s1 = " + \" width=\\\"\" + " + widthStr + " + \"\\\"\"";
+ }
+
+ String s2 = "";
+ if( height != null ) {
+ s2 = " + \" height=\\\"\" + " + heightStr + " + \"\\\"\"";
+ }
+
+ String s3 = makeAttr("hspace", hspace) +
+ makeAttr("vspace", vspace) +
+ makeAttr("align", align) +
+ makeAttr("codebase", iepluginurl) +
+ '>';
+
// Then print the output string to the java file
- out.printil("out.println(" + s1 + " + " + s2 + " + " + s3 + ");");
+ out.printil("out.write(" +
+ quote(s0) + s1 + s2 + " + " + quote(s3) + ");");
+ out.printil("out.write(\"\\n\");");
// <PARAM > for java_code
s0 = "<PARAM name=\"java_code\"" + makeAttr("value", code) + '>';
- out.printil("out.println(" + quote(s0) + ");");
+ out.printil("out.write(" + quote(s0) + ");");
+ out.printil("out.write(\"\\n\");");
// <PARAM > for java_codebase
if (codebase != null) {
s0 = "<PARAM name=\"java_codebase\"" +
makeAttr("value", codebase) +
'>';
- out.printil("out.println(" + quote(s0) + ");");
+ out.printil("out.write(" + quote(s0) + ");");
+ out.printil("out.write(\"\\n\");");
}
// <PARAM > for java_archive
@@ -986,7 +1359,8 @@
s0 = "<PARAM name=\"java_archive\"" +
makeAttr("value", archive) +
'>';
- out.printil("out.println(" + quote(s0) + ");");
+ out.printil("out.write(" + quote(s0) + ");");
+ out.printil("out.write(\"\\n\");");
}
// <PARAM > for type
@@ -994,7 +1368,8 @@
makeAttr("value", "application/x-java-" + type + ";" +
((jreversion==null)? "": "version=" + jreversion)) +
'>';
- out.printil("out.println(" + quote(s0) + ");");
+ out.printil("out.write(" + quote(s0) + ");");
+ out.printil("out.write(\"\\n\");");
/*
* generate a <PARAM> for each <jsp:param> in the plugin body
@@ -1005,41 +1380,25 @@
/*
* Netscape style plugin part
*/
- out.printil("out.println(" + quote("<COMMENT>") + ");");
+ out.printil("out.write(" + quote("<COMMENT>") + ");");
+ out.printil("out.write(\"\\n\");");
s0 = "<EMBED" +
makeAttr("type", "application/x-java-" + type + ";" +
((jreversion==null)? "": "version=" + jreversion)) +
makeAttr("name", name);
-
- if (width != null) {
- if (width.isExpression()) {
- s1 = quote(s0 + " width=\"") + " + " + width.getValue() +
- " + " + quote("\"");
- } else {
- s1 = quote(s0 + makeAttr("width", width.getValue()));
- }
- } else {
- s1 = quote(s0);
- }
- if (height != null) {
- if (height.isExpression()) {
- s2 = quote(" height=\"") + " + " + height.getValue() +
- " + " + quote("\"");
- } else {
- s2 = quote(makeAttr("height", height.getValue()));
- }
- } else {
- s2 = "\"\"";
- }
-
- s3 = quote(makeAttr("hspace", hspace) +
- makeAttr("vspace", vspace) +
- makeAttr("align", align) +
- makeAttr("pluginspage", nspluginurl) +
- makeAttr("java_code", code) +
- makeAttr("java_codebase", codebase) +
- makeAttr("java_archive", archive));
- out.printil("out.println(" + s1 + " + " + s2 + " + " + s3 + ");");
+
+ // s1 and s2 are the same as before.
+
+ s3 = makeAttr("hspace", hspace) +
+ makeAttr("vspace", vspace) +
+ makeAttr("align", align) +
+ makeAttr("pluginspage", nspluginurl) +
+ makeAttr("java_code", code) +
+ makeAttr("java_codebase", codebase) +
+ makeAttr("java_archive", archive);
+ out.printil("out.write(" +
+ quote(s0) + s1 + s2 + " + " + quote(s3) + ");");
+ out.printil("out.write(\"\\n\");");
/*
* Generate a 'attr = "value"' for each <jsp:param> in plugin body
@@ -1047,10 +1406,13 @@
if (n.getBody() != null)
n.getBody().visit(new ParamVisitor(false));
- out.printil("out.println(" + quote(">") + ");");
+ out.printil("out.write(" + quote(">") + ");");
+ out.printil("out.write(\"\\n\");");
- out.printil("out.println(" + quote("<NOEMBED>") + ");");
- out.printil("out.println(" + quote("</COMMENT>") + ");");
+ out.printil("out.write(" + quote("<NOEMBED>") + ");");
+ out.printil("out.write(\"\\n\");");
+ out.printil("out.write(" + quote("</COMMENT>") + ");");
+ out.printil("out.write(\"\\n\");");
/*
* Fallback
@@ -1059,19 +1421,26 @@
n.getBody().visit(new Node.Visitor() {
public void visit(Node.FallBackAction n) {
n.setBeginJavaLine(out.getJavaLine());
- out.printil("out.println(" +
+ out.printil("out.write(" +
quote(new String(n.getText())) + ");");
+ out.printil("out.write(\"\\n\");");
n.setEndJavaLine(out.getJavaLine());
}
});
}
- out.printil("out.println(" + quote("</NOEMBED></EMBED>") + ");");
- out.printil("out.println(" + quote("</OBJECT>") + ");");
+ out.printil("out.write(" + quote("</NOEMBED></EMBED>") + ");");
+ out.printil("out.write(\"\\n\");");
+ out.printil("out.write(" + quote("</OBJECT>") + ");");
+ out.printil("out.write(\"\\n\");");
n.setEndJavaLine(out.getJavaLine());
}
+ public void visit(Node.NamedAttribute n) throws JasperException {
+ // Don't visit body of this tag - we already did earlier.
+ }
+
public void visit(Node.CustomTag n) throws JasperException {
Hashtable handlerInfosByShortName = (Hashtable)
@@ -1097,11 +1466,23 @@
String tagEvalVar = "_jspx_eval_" + baseVar;
String tagHandlerVar = "_jspx_th_" + baseVar;
+ Class tagHandlerClass = handlerInfo.getTagHandlerClass();
+ boolean implementsIterationTag =
+ IterationTag.class.isAssignableFrom(tagHandlerClass);
+ boolean implementsBodyTag =
+ BodyTag.class.isAssignableFrom(tagHandlerClass);
+ boolean implementsTryCatchFinally =
+ TryCatchFinally.class.isAssignableFrom(tagHandlerClass);
+ boolean implementsSimpleTag =
+ SimpleTag.class.isAssignableFrom(tagHandlerClass);
+
// If the tag contains no scripting element, generate its codes
// to a method.
ServletWriter outSave = null;
MethodsBuffer methodsBufferSave = null;
- if (n.isScriptless() && !n.hasScriptingVars()) {
+ Node.ChildInfo ci = n.getChildInfo();
+ if (implementsSimpleTag
+ || (ci.isScriptless() && !ci.hasScriptingVars())) {
// The tag handler and its body code can reside in a separate
// method if it is scriptless and does not have any scripting
// variable defined.
@@ -1140,48 +1521,44 @@
out.print(parent);
out.print(", ");
}
-// out.println("javax.servlet.jsp.PageContext pageContext, JspxState _jspxState)");
- out.println("javax.servlet.jsp.PageContext pageContext)");
+// out.println("PageContext pageContext, JspxState _jspxState)");
+ out.println("PageContext pageContext)");
out.printil(" throws Throwable {");
out.pushIndent();
// Initilaize local variables used in this method.
out.printil("JspWriter out = pageContext.getOut();");
- if (n.isHasUsebean()) {
- out.println("HttpSession session = pageContext.getSession();");
- out.println("ServletContext application = pageContext.getServletContext();");
- }
- if (n.isHasUsebean() || n.isHasIncludeAction() || n.isHasSetProperty()) {
- out.println("HttpServletRequest request = (HttpServletRequest)pageContext.getRequest();");
- }
- if (n.isHasIncludeAction()) {
- out.println("ServletResponse response = pageContext.getResponse();");
- }
- }
+ generateLocalVariables( out, n );
+ }
- Class tagHandlerClass = handlerInfo.getTagHandlerClass();
- boolean implementsIterationTag =
- IterationTag.class.isAssignableFrom(tagHandlerClass);
- boolean implementsBodyTag =
- BodyTag.class.isAssignableFrom(tagHandlerClass);
- boolean implementsTryCatchFinally =
- TryCatchFinally.class.isAssignableFrom(tagHandlerClass);
+ if (implementsSimpleTag) {
+ generateCustomDoTag(n, handlerInfo, tagHandlerVar,
+ implementsTryCatchFinally);
+ } else {
+ /*
+ * Classic tag handler: Generate code for start element, body,
+ * and end element
+ */
+ generateCustomStart(n, handlerInfo, tagHandlerVar, tagEvalVar,
+ implementsIterationTag, implementsBodyTag,
+ implementsTryCatchFinally);
+
+ // visit body
+ String tmpParent = parent;
+ parent = tagHandlerVar;
+ boolean tmpIsSimpleTagHandler = isSimpleTagHandler;
+ isSimpleTagHandler = false;
+ visitBody(n);
+ parent = tmpParent;
+ isSimpleTagHandler = tmpIsSimpleTagHandler;
- // Generate code for start tag, body, and end tag
- generateCustomStart(n, handlerInfo, tagHandlerVar, tagEvalVar,
- implementsIterationTag, implementsBodyTag,
- implementsTryCatchFinally);
-
- String tmpParent = parent;
- parent = tagHandlerVar;
- visitBody(n);
-
- parent = tmpParent;
- generateCustomEnd(n, tagHandlerVar, tagEvalVar,
- implementsIterationTag, implementsBodyTag,
- implementsTryCatchFinally);
+ generateCustomEnd(n, tagHandlerVar, tagEvalVar,
+ implementsIterationTag, implementsBodyTag,
+ implementsTryCatchFinally);
+ }
- if (n.isScriptless() && !n.hasScriptingVars()) {
+ if (implementsSimpleTag
+ || (ci.isScriptless() && !ci.hasScriptingVars())) {
// Generate end of method
if (methodNesting > 0) {
out.printil("return false;");
@@ -1301,6 +1678,27 @@
n.setEndJavaLine(out.getJavaLine());
}
+ public void visit(Node.JspBody n) throws JasperException {
+ if (isSimpleTagHandler) {
+ out.printin(simpleTagHandlerVar);
+ out.print(".setJspBody(");
+
+ Node.JspAttribute value = n.getValue();
+ if (value != null) {
+ out.print(attributeValue(value, false, JspFragment.class,
+ "null" ));
+ } else {
+ // Parent node must be a CustomTag since
+ // isSimpleTagHandler is set to true.
+ generateJspFragment((Node.CustomTag)n.getParent(),
+ simpleTagHandlerVar);
+ }
+ out.println(");");
+ } else {
+ visitBody(n);
+ }
+ }
+
private void generateCustomStart(Node.CustomTag n,
TagHandlerInfo handlerInfo,
String tagHandlerVar,
@@ -1347,7 +1745,7 @@
out.println("();");
}
- generateSetters(n, tagHandlerVar, handlerInfo);
+ generateSetters(n, tagHandlerVar, handlerInfo, false);
if (implementsTryCatchFinally) {
out.printil("try {");
@@ -1377,12 +1775,9 @@
out.println(" != javax.servlet.jsp.tagext.Tag.EVAL_BODY_INCLUDE) {");
// Assume EVAL_BODY_BUFFERED
out.pushIndent();
- out.printil("javax.servlet.jsp.tagext.BodyContent _bc = pageContext.pushBody();");
- out.printil("_bc.clear();");
- out.printil("out = _bc;");
-
+ out.printil("out = pageContext.pushBody();");
out.printin(tagHandlerVar);
- out.println(".setBodyContent(_bc);");
+ out.println(".setBodyContent((javax.servlet.jsp.tagext.BodyContent) out);");
out.printin(tagHandlerVar);
out.println(".doInitBody();");
@@ -1468,7 +1863,7 @@
out.pushIndent();
out.printin(tagHandlerVar);
out.println(".doFinally();");
- }
+ }
if (ctxt.getOptions().isPoolingEnabled()) {
out.printin(n.getTagHandlerPoolName());
@@ -1489,6 +1884,103 @@
n.setEndJavaLine(out.getJavaLine());
}
+ private void generateCustomDoTag(Node.CustomTag n,
+ TagHandlerInfo handlerInfo,
+ String tagHandlerVar,
+ boolean implementsTryCatchFinally)
+ throws JasperException {
+
+ Class tagHandlerClass = handlerInfo.getTagHandlerClass();
+
+ n.setBeginJavaLine(out.getJavaLine());
+ out.printin("/* ---- ");
+ out.print(n.getName());
+ out.println(" ---- */");
+ out.printil("{");
+ out.pushIndent();
+
+ /*
+ * Save current values of scripting variables, so that the
+ * scripting variables may be synchronized without affecting their
+ * original values
+ */
+ saveScriptingVariables(n);
+
+ out.printin(tagHandlerClass.getName());
+ out.print(" ");
+ out.print(tagHandlerVar);
+ out.print(" = ");
+ out.print("new ");
+ out.print(tagHandlerClass.getName());
+ out.println("();");
+
+ generateSetters(n, tagHandlerVar, handlerInfo, true);
+
+ if (implementsTryCatchFinally) {
+ out.printil("try {");
+ out.pushIndent();
+ }
+
+ // Set the body
+ if (findJspBody(n) == null) {
+ /*
+ * Encapsulate body of custom tag invocation in JspFragment
+ * and pass it to tag handler's setJspBody()
+ */
+ out.printin(tagHandlerVar);
+ out.print(".setJspBody(");
+ generateJspFragment(n, tagHandlerVar);
+ out.println(");");
+ } else {
+ /*
+ * Body of tag is the body of the <jsp:body> element.
+ * The visit method for that element is going to encapsulate
+ * that element's body in a JspFragment and pass it to
+ * the tag handler's setJspBody()
+ */
+ String tmpTagHandlerVar = simpleTagHandlerVar;
+ simpleTagHandlerVar = tagHandlerVar;
+ boolean tmpIsSimpleTagHandler = isSimpleTagHandler;
+ isSimpleTagHandler = true;
+ visitBody(n);
+ simpleTagHandlerVar = tmpTagHandlerVar;
+ isSimpleTagHandler = tmpIsSimpleTagHandler;
+ }
+
+ out.printin("if (");
+ out.print(tagHandlerVar);
+ out.println(".doTag() == javax.servlet.jsp.tagext.Tag.SKIP_PAGE)");
+ out.pushIndent();
+ out.printil((methodNesting > 0)? "return true;": "return;");
+ out.popIndent();
+
+ // Synchronize AT_BEGIN and AT_END scripting variables
+ syncScriptingVariables(n, VariableInfo.AT_BEGIN);
+ syncScriptingVariables(n, VariableInfo.AT_END);
+
+ // TryCatchFinally
+ if (implementsTryCatchFinally) {
+ out.popIndent(); // try
+ out.printil("} catch (Throwable _jspx_exception) {");
+ out.pushIndent();
+ out.printin(tagHandlerVar);
+ out.println(".doCatch(_jspx_exception);");
+ out.popIndent();
+ out.printil("} finally {");
+ out.pushIndent();
+ out.printin(tagHandlerVar);
+ out.println(".doFinally();");
+ out.popIndent();
+ out.println("}");
+ }
+
+ restoreScriptingVariables(n);
+ out.popIndent();
+ out.printil("}");
+
+ n.setEndJavaLine(out.getJavaLine());
+ }
+
/*
* Declares any NESTED scripting variables of the given custom tag,
* if the given custom tag is not nested inside itself (i.e, has a
@@ -1738,12 +2230,18 @@
}
}
- private void generateSetters(Node.CustomTag n, String tagHandlerVar,
- TagHandlerInfo handlerInfo)
+ private void generateSetters(Node.CustomTag n,
+ String tagHandlerVar,
+ TagHandlerInfo handlerInfo,
+ boolean simpleTag)
throws JasperException {
out.printin(tagHandlerVar);
- out.println(".setPageContext(pageContext);");
+ if (simpleTag) {
+ out.println(".setJspContext(pageContext);");
+ } else {
+ out.println(".setPageContext(pageContext);");
+ }
out.printin(tagHandlerVar);
out.print(".setParent(");
out.print(parent);
@@ -1753,7 +2251,24 @@
for (int i=0; i<attrs.length; i++) {
String attrValue = attrs[i].getValue();
if (attrValue == null) {
- continue;
+ if( attrs[i].isNamedAttribute() ) {
+ if( n.checkIfAttributeIsJspFragment(
+ attrs[i].getName() ) )
+ {
+ // XXX - no need to generate temporary variable
+ // here
+ attrValue = generateNamedAttributeJspFragment(
+ attrs[i].getNamedAttributeNode(),
+ tagHandlerVar );
+ }
+ else {
+ attrValue = generateNamedAttributeValue(
+ attrs[i].getNamedAttributeNode() );
+ }
+ }
+ else {
+ continue;
+ }
}
String attrName = attrs[i].getName();
Method m = handlerInfo.getSetterMethod(attrName);
@@ -1765,7 +2280,15 @@
Class c[] = m.getParameterTypes();
// XXX assert(c.length > 0)
- if (!attrs[i].isExpression()) {
+ if (attrs[i].isExpression() || attrs[i].isNamedAttribute()) {
+ // Do nothing
+ }
+ else if (attrs[i].isELInterpreterInput()) {
+ // run attrValue through the expression interpreter
+ attrValue = JspUtil.interpreterCall( attrValue,
+ c[0], "_jspx_fnmap", n.getPrefix() );
+ }
+ else {
attrValue = convertString(c[0], attrValue, attrName,
handlerInfo.getPropertyEditorClass(attrName));
}
@@ -1848,10 +2371,137 @@
+ quote(s) + ")";
}
}
- }
+ /**
+ * Generates anonymous JspFragment inner class which is passed as an
+ * argument to SimpleTag.setJspBody().
+ */
+ private void generateJspFragment(Node parent, String tagHandlerVar)
+ throws JasperException
+ {
+ // XXX - A possible optimization here would be to check to see
+ // if the old child of the parent node is TemplateText. If so,
+ // we know there won't be any parameters, etc, so we can
+ // generate a low-overhead JspFragment that just echoes its
+ // body. The implementation of this fragment can come from
+ // the org.apache.jasper.runtime package as a support class.
+ int id = helperClassBuffer.getFragmentId();
+ helperClassBuffer.openFragment( parent );
+ ServletWriter outSave = out;
+ out = helperClassBuffer.getOut();
+ visitBody( parent );
+ out = outSave;
+ helperClassBuffer.closeFragment();
+ // XXX - Need to change pageContext to jspContext if
+ // we're not in a place where pageContext is defined (e.g.
+ // in a fragment or in a tag file.
+ out.print( "new " + helperClassBuffer.getClassName() +
+ "( " + id + ", pageContext, " + tagHandlerVar + ")" );
+ }
+
+ /**
+ * Generate the code required to obtain the runtime value of the
+ * given named attribute.
+ *
+ * @return The name of the temporary variable the result is stored in.
+ */
+ public String generateNamedAttributeValue( Node.NamedAttribute n )
+ throws JasperException
+ {
+ String varName = n.getTemporaryVariableName();
+
+ // If the only body element for this named attribute node is
+ // template text, we need not generate an extra call to
+ // pushBody and popBody. Maybe we can further optimize
+ // here by getting rid of the temporary variable, but in
+ // reality it looks like javac does this for us.
+ boolean templateTextOptimization = false;
+ Node.Nodes body = n.getBody();
+ if( body.size() == 1 ) {
+ Node bodyElement = body.getNode( 0 );
+ if( bodyElement instanceof Node.TemplateText ) {
+ templateTextOptimization = true;
+ out.printil( "String " + varName + " = " +
+ quote( new String(
+ ((Node.TemplateText)bodyElement).getText() ) ) + ";" );
+ }
+ }
+
+ // XXX - Another possible optimization would be for
+ // lone EL expressions (no need to pushBody here either).
+
+ if( !templateTextOptimization ) {
+ out.printil( "out = pageContext.pushBody();" );
+ visitBody( n );
+ out.printil( "String " + varName + " = " +
+ "((javax.servlet.jsp.tagext.BodyContent)" +
+ "out).getString();" );
+ out.printil( "out = pageContext.popBody();" );
+ }
+
+ return varName;
+ }
+
+ /**
+ * Similar to generateNamedAttributeValue, but create a JspFragment
+ * instead.
+ *
+ * @param n The parent node of the named attribute
+ * @param tagHandlerVar The variable the tag handler is stored in,
+ * so the fragment knows its parent tag.
+ * @return The name of the temporary variable the fragment
+ * is stored in.
+ */
+ public String generateNamedAttributeJspFragment(
+ Node.NamedAttribute n, String tagHandlerVar )
+ throws JasperException
+ {
+ String varName = n.getTemporaryVariableName();
+
+ out.printin( "javax.servlet.jsp.tagext.JspFragment " + varName +
+ " = " );
+ generateJspFragment( n, tagHandlerVar );
+ out.println( ";" );
+
+ return varName;
+ }
+ }
+
+ private static void generateLocalVariables( ServletWriter out, Node n )
+ throws JasperException
+ {
+ Node.ChildInfo ci;
+ if( n instanceof Node.CustomTag ) {
+ ci = ((Node.CustomTag)n).getChildInfo();
+ }
+ else if( n instanceof Node.JspBody ) {
+ ci = ((Node.JspBody)n).getChildInfo();
+ }
+ else if( n instanceof Node.NamedAttribute ) {
+ ci = ((Node.NamedAttribute)n).getChildInfo();
+ }
+ else {
+ // Cannot access err since this method is static, but at
+ // least flag an error.
+ throw new JasperException( "Unexpected Node Type" );
+ //err.getString(
+ // "jsp.error.internal.unexpected_node_type" ) );
+ }
+
+ if (ci.isHasUsebean()) {
+ out.println("HttpSession session = pageContext.getSession();");
+ out.println("ServletContext application = pageContext.getServletContext();");
+ }
+ if (ci.isHasUsebean() || ci.isHasIncludeAction() || ci.isHasSetProperty()) {
+ out.println("HttpServletRequest request = (HttpServletRequest)pageContext.getRequest();");
+ }
+ if (ci.isHasIncludeAction()) {
+ out.println("ServletResponse response = pageContext.getResponse();");
+ }
+ }
+
/**
- * Generates the ending part of the static portion of the servelet.
+ * Generates the ending part of the static portion of the servlet.
*/
private void generatePostamble(Node.Nodes page) {
out.popIndent();
@@ -1881,6 +2531,12 @@
// Append any methods that were generated
out.print(methodsBuffer.toString());
+
+ // Append the helper class
+ if( helperClassBuffer.isUsed() ) {
+ helperClassBuffer.generatePostamble();
+ out.print(helperClassBuffer.toString());
+ }
// generate class definition for JspxState
if (maxTagNesting > 0) {
@@ -1900,7 +2556,9 @@
methodsBuffer = new MethodsBuffer();
err = compiler.getErrorDispatcher();
ctxt = compiler.getCompilationContext();
- pageInfo = compiler.getPageInfo();
+ helperClassBuffer = new HelperClassBuffer(
+ ctxt.getServletClassName() + "Helper" );
+ pageInfo = compiler.getPageInfo();
beanInfo = pageInfo.getBeanRepository();
breakAtLF = ctxt.getOptions().getMappedFile();
if (ctxt.getOptions().isPoolingEnabled()) {
@@ -1922,7 +2580,9 @@
gen.compileTagHandlerPoolList(page);
}
gen.generatePreamble(page);
- page.visit(gen.new GenerateVisitor(out, gen.methodsBuffer));
+ gen.helperClassBuffer.generatePreamble();
+ page.visit(gen.new GenerateVisitor(out, gen.methodsBuffer,
+ gen.helperClassBuffer));
gen.generatePostamble(page);
}
@@ -2007,7 +2667,7 @@
private static class MethodsBuffer {
private java.io.CharArrayWriter charWriter;
- private ServletWriter out;
+ protected ServletWriter out;
MethodsBuffer() {
charWriter = new java.io.CharArrayWriter();
@@ -2021,6 +2681,132 @@
public String toString() {
return charWriter.toString();
}
+ }
+
+ /**
+ * Keeps a separate buffer for helper classes.
+ */
+ private static class HelperClassBuffer
+ extends MethodsBuffer
+ {
+ // True if the helper class should be generated.
+ private boolean used = false;
+
+ private int numFragments = 0;
+
+ private String className;
+
+ public HelperClassBuffer( String className ) {
+ this.className = className;
+ }
+
+ public String getClassName() {
+ return this.className;
+ }
+
+ public boolean isUsed() {
+ return this.used;
+ }
+
+ public int getFragmentId() {
+ return this.numFragments;
+ }
+
+ public void generatePreamble() {
+ out.println();
+ out.pushIndent();
+ out.printil( "static class " + className );
+ out.printil( " extends " +
+ "org.apache.jasper.runtime.JspFragmentHelper" );
+ out.printil( "{" );
+ out.pushIndent();
+ out.printil( "public " + className +
+ "( int discriminator, JspContext jspContext, " +
+ "Object parentTag ) {" );
+ out.pushIndent();
+ out.printil( "super( discriminator, jspContext, parentTag );" );
+ out.popIndent();
+ out.printil( "}" );
+ }
+
+ public void openFragment( Node parent )
+ throws JasperException
+ {
+ this.used = true;
+ out.printil( "public void invoke" + numFragments + "( " +
+ "java.io.Writer out, java.util.Map params ) " );
+ out.pushIndent();
+ out.printil( "throws javax.servlet.jsp.JspException, " +
+ "java.io.IOException" );
+ out.popIndent();
+ out.printil( "{" );
+ out.pushIndent();
+ generateLocalVariables( out, parent );
+ }
+
+ public void closeFragment() {
+ out.popIndent();
+ out.printil( "}" );
+ this.numFragments++;
+ }
+
+ public void generatePostamble() {
+ out.printil( "public void invoke( java.io.Writer out, " +
+ "java.util.Map params )" );
+ out.pushIndent();
+ out.printil( "throws javax.servlet.jsp.JspException" );
+ out.popIndent();
+ out.printil( "{" );
+ out.pushIndent();
+ out.printil(
+ "this.jspContext.pushPageScope( this.originalPageScope );" );
+ out.printil( "java.util.Map _jspx_originalValues = null;" );
+ out.printil( "if( params != null ) {" );
+ out.pushIndent();
+ out.printil( "_jspx_originalValues = preparePageScope( params );");
+ out.popIndent();
+ out.printil( "}" );
+ out.printil( "if( out == null ) {" );
+ out.pushIndent();
+ out.printil( "out = this.jspContext.getOut();" );
+ out.popIndent();
+ out.printil( "}" );
+ out.printil( "try {" );
+ out.pushIndent();
+ out.printil( "switch( this.discriminator ) {" );
+ out.pushIndent();
+ for( int i = 0; i < numFragments; i++ ) {
+ out.printil( "case " + i + ":" );
+ out.pushIndent();
+ out.printil( "invoke" + i + "( out, params );" );
+ out.printil( "break;" );
+ out.popIndent();
+ }
+ out.popIndent();
+ out.printil( "}" ); // switch
+ out.popIndent();
+ out.printil( "}" ); // try
+ out.printil( "catch( java.io.IOException e ) {" );
+ out.pushIndent();
+ out.printil( "throw new javax.servlet.jsp.JspException( e );" );
+ out.popIndent();
+ out.printil( "}" ); // catch
+ out.printil( "finally {" );
+ out.pushIndent();
+ out.printil( "if( params != null ) {" );
+ out.pushIndent();
+ out.printil( "restorePageScope( _jspx_originalValues );");
+ out.popIndent();
+ out.printil( "}" );
+ out.printil( "this.jspContext.popPageScope();" );
+ out.popIndent();
+ out.printil( "}" ); // finally
+ out.popIndent();
+ out.printil( "}" ); // invoke method
+ out.popIndent();
+ out.printil( "}" ); // helper class
+ out.popIndent();
+ }
}
}
1.5 +24 -12 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/JspDocumentParser.java
Index: JspDocumentParser.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/JspDocumentParser.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- JspDocumentParser.java 8 Jul 2002 17:28:58 -0000 1.4
+++ JspDocumentParser.java 16 Jul 2002 19:30:51 -0000 1.5
@@ -233,6 +233,10 @@
node = new Node.PlugIn(attrsCopy, start, current);
} else if (qName.equals(JSP_TEXT_TAG)) {
node = new Node.JspText(start, current);
+ } else if (qName.equals(JSP_BODY_TAG)) {
+ node = new Node.JspBody(attrsCopy, start, current);
+ } else if (qName.equals(JSP_ATTRIBUTE_TAG)) {
+ node = new Node.NamedAttribute(attrsCopy, start, current);
} else {
node = getCustomTag(qName, attrsCopy, start, current);
if (node == null) {
@@ -285,9 +289,26 @@
public void endElement(String uri,
String localName,
String qName) throws SAXException {
+ if (current instanceof Node.NamedAttribute
+ && ((Node.NamedAttribute) current).isTrim()) {
+ // Ignore any whitespace (including spaces, carriage returns,
+ // line feeds, and tabs, that appear at the beginning and at the
+ // end of the body of the <jsp:attribute> action.
+ Node.Nodes subelems = ((Node.NamedAttribute) current).getBody();
+ Node firstNode = subelems.getNode(0);
+ if (firstNode instanceof Node.TemplateText) {
+ ((Node.TemplateText) firstNode).ltrim();
+ }
+ Node lastNode = subelems.getNode(subelems.size() - 1);
+ if (lastNode instanceof Node.TemplateText) {
+ ((Node.TemplateText) lastNode).rtrim();
+ }
+ }
+
if (current instanceof Node.ScriptingElement) {
checkScriptingBody(current.getBody());
}
+
if (current.getParent() != null) {
current = current.getParent();
}
@@ -434,15 +455,6 @@
// get the uri
String uri = attrs.getValue(i);
- /*
- * make sure that a relative path is prefixed with
- * urn:jsptld:path
- */
- if (uri.charAt(0) == '/') {
- throw new JasperException(
- err.getString("jsp.error.xml.taglib.uri.not_prefixed",
- uri));
- }
if (uri.startsWith(URN_JSPTLD)) {
// uri value is of the form "urn:jsptld:path"
uri = uri.substring(URN_JSPTLD.length());
1.6 +32 -2 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/JspReader.java
Index: JspReader.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/JspReader.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- JspReader.java 7 Jun 2002 20:04:27 -0000 1.5
+++ JspReader.java 16 Jul 2002 19:30:51 -0000 1.6
@@ -75,6 +75,9 @@
* @author Rajiv Mordani
* @author Mandar Raje
* @author Danno Ferrin
+ * @author Kin-man Chung
+ * @author Shawn Bayern
+ * @author Mark Roth
*/
public class JspReader {
@@ -254,7 +257,13 @@
while ((++current.cursor < len) &&
((ch = current.stream[current.cursor]) != '<')) {
- if (ch == '\n') {
+ if (ch == '$') {
+ // XXX Make this backward compatible with JSP1.2.
+ if ((current.cursor+1 < len) &&
+ current.stream[current.cursor+1] == '{') {
+ break;
+ }
+ } else if (ch == '\n') {
current.line++;
current.col = 0;
} else {
@@ -340,7 +349,27 @@
reset(mark);
return false;
}
-
+
+ /**
+ * Looks ahead to see if there are optional spaces followed by
+ * the given String. If so, true is returned and those spaces and
+ * characters are skipped. If not, false is returned and the
+ * position is restored to where we were before.
+ */
+ public boolean matchesOptionalSpacesFollowedBy( String s )
+ throws JasperException
+ {
+ Mark mark = mark();
+
+ skipSpaces();
+ boolean result = matches( s );
+ if( !result ) {
+ reset( mark );
+ }
+
+ return result;
+ }
+
public void advance(int n) throws JasperException {
while (--n >= 0)
nextChar();
@@ -402,6 +431,7 @@
}
final boolean isSpace() {
+ // Note: If this logic changes, also update Node.TemplateText.rtrim()
return peekChar() <= ' ';
}
1.5 +134 -7 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/JspUtil.java
Index: JspUtil.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/JspUtil.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- JspUtil.java 8 Jun 2002 00:14:35 -0000 1.4
+++ JspUtil.java 16 Jul 2002 19:30:51 -0000 1.5
@@ -86,6 +86,10 @@
import org.xml.sax.InputSource;
import org.xml.sax.helpers.AttributesImpl;
+// EL interpreter (subject to change)
+import javax.servlet.jsp.el.ExpressionEvaluator;
+import org.apache.jasper.runtime.ExpressionEvaluatorManager;
+
/**
* This class has all the utility method(s).
* Ideally should move all the bean containers here.
@@ -94,6 +98,8 @@
* @author Rajiv Mordani.
* @author Danno Ferrin
* @author Pierre Delisle
+ * @author Shawn Bayern
+ * @author Mark Roth
*/
public class JspUtil {
@@ -105,6 +111,7 @@
private static ErrorHandler errorHandler = new MyErrorHandler();
private static EntityResolver entityResolver = new MyEntityResolver();
+ private static int tempSequenceNumber = 0;
public static char[] removeQuotes(char []chars) {
CharArrayWriter caw = new CharArrayWriter();
@@ -273,15 +280,24 @@
}
}
+ /**
+ * Checks if all mandatory attributes are present and if all attributes
+ * present have valid names. Checks attributes specified as XML-style
+ * attributes as well as attributes specified using the jsp:attribute
+ * standard action. Also verifies that any attributes specified
+ * via jsp:attribute are rtexprvalue attributes.
+ */
public static void checkAttributes(String typeOfTag,
- Attributes attrs,
+ Node n,
ValidAttribute[] validAttributes,
- Mark start,
ErrorDispatcher err)
throws JasperException {
+ Attributes attrs = n.getAttributes();
+ Mark start = n.getStart();
boolean valid = true;
+
// AttributesImpl.removeAttribute is broken, so we do this...
- int tempLength = attrs.getLength();
+ int tempLength = (attrs == null) ? 0 : attrs.getLength();
Vector temp = new Vector(tempLength, 1);
for (int i = 0; i < tempLength; i++) {
String qName = attrs.getQName(i);
@@ -289,6 +305,40 @@
temp.addElement(qName);
}
+ // Add names of attributes specified using jsp:attribute and
+ // check that they are rtexprvalues while we're at it.
+ Node.Nodes tagBody = n.getBody();
+ if( tagBody != null ) {
+ int numSubElements = tagBody.size();
+ for( int i = 0; i < numSubElements; i++ ) {
+ Node node = tagBody.getNode( i );
+ if( node instanceof Node.NamedAttribute ) {
+ String attrName = node.getAttributeValue( "name" );
+ // Verify that this node is an rtexprvalue.
+ for( int j = 0; j < validAttributes.length; j++ ) {
+ if( validAttributes[j].name.equals( attrName ) &&
+ !validAttributes[j].rtexprvalue )
+ {
+ valid = false;
+ err.jspError(start,
+ "jsp.error.named.attribute.not.rt",
+ attrName );
+ break;
+ }
+ }
+ if( valid ) {
+ temp.addElement( attrName );
+ }
+ valid = true;
+ }
+ else {
+ // Nothing can come before jsp:attribute, and only
+ // jsp:body can come after it.
+ break;
+ }
+ }
+ }
+
/*
* First check to see if all the mandatory attributes are present.
* If so only then proceed to see if the other attributes are valid
@@ -337,6 +387,7 @@
err.jspError(start, "jsp.error.invalid.attribute", typeOfTag,
attribute);
}
+ // XXX *could* move EL-syntax validation here... (sb)
}
public static String escapeQueryString(String unescString) {
@@ -411,10 +462,18 @@
public static class ValidAttribute {
String name;
boolean mandatory;
+ boolean rtexprvalue;
- public ValidAttribute (String name, boolean mandatory) {
+ public ValidAttribute (String name, boolean mandatory,
+ boolean rtexprvalue )
+ {
this.name = name;
this.mandatory = mandatory;
+ this.rtexprvalue = rtexprvalue;
+ }
+
+ public ValidAttribute (String name, boolean mandatory) {
+ this( name, mandatory, false );
}
public ValidAttribute (String name) {
@@ -473,7 +532,75 @@
}
return b;
}
+
+ /**
+ * Produces a String representing a call to the EL interpreter.
+ * @param expression a String containing zero or more "${}" expressions
+ * @param expectedType the expected type of the interpreted result
+ * @param fnMap Variable name containing function map, or literal "null"
+ * @param defaultPrefix Default prefix, or literal "null"
+ * @return a String representing a call to the EL interpreter.
+ */
+ public static String interpreterCall(String expression,
+ Class expectedType,
+ String fnMap,
+ String defaultPrefix)
+ {
+ return "(" + expectedType.getName() + ") "
+ + Constants.EL_INTERPRETER_CONDUIT_CLASS + "."
+ + Constants.EL_INTERPRETER_CONDUIT_METHOD
+ + "(" + Generator.quote(expression) + ", "
+ + expectedType.getName() + ".class, "
+ + "pageContext,"
+ + fnMap + ", "
+ + Generator.quote( defaultPrefix ) + ")";
+ }
+
+ /**
+ * Validates the syntax of all ${} expressions within the given string.
+ * @param where the approximate location of the expressions in the JSP page
+ * @param expressions a string containing zero or more "${}" expressions
+ * @param err an error dispatcher to use
+ * @param extraInfo info (such as the name of the current attribute)
+ * to be included in any error messages
+ */
+ public static void validateExpressions(Mark where,
+ String expressions,
+ ErrorDispatcher err)
+ throws JasperException {
+ ExpressionEvaluator el = null;
+ try {
+ // XXX when the EL moves to Jakarta Commons, this can
+ // be replaced with a *much* cleaner interface. I apologize
+ // for it for the moment! (SB)
+ el = ExpressionEvaluatorManager.getEvaluatorByName(
+ ExpressionEvaluatorManager.EVALUATOR_CLASS);
+ } catch (javax.servlet.jsp.JspException uglyEx) {
+ err.jspError(where, "jsp.error.internal.evaluator_not_found");
+ }
+ String errMsg = el.validate(expressions);
+ if (errMsg != null)
+ err.jspError(where, "jsp.error.invalid.expression", expressions,
+ errMsg);
+ }
+
+ /**
+ * Resets the temporary variable name.
+ * (not thread-safe)
+ */
+ public static void resetTemporaryVariableName() {
+ tempSequenceNumber = 0;
+ }
+
+ /**
+ * Generates a new temporary variable name.
+ * (not thread-safe)
+ */
+ public static String nextTemporaryVariableName() {
+ return Constants.TEMP_VARIABLE_NAME_PREFIX + (tempSequenceNumber++);
+ }
}
+
class MyEntityResolver implements EntityResolver {
public InputSource resolveEntity(String publicId, String systemId)
1.16 +362 -56 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Node.java
Index: Node.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Node.java,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -r1.15 -r1.16
--- Node.java 21 Jun 2002 00:59:56 -0000 1.15
+++ Node.java 16 Jul 2002 19:30:51 -0000 1.16
@@ -72,6 +72,8 @@
*
* @author Kin-man Chung
* @author Jan Luehe
+ * @author Shawn Bayern
+ * @author Mark Roth
*/
public abstract class Node {
@@ -130,6 +132,58 @@
return (attrs == null) ? null : attrs.getValue(name);
}
+ /**
+ * Searches all subnodes of this node for jsp:attribute standard
+ * actions with the given name, and returns the NamedAttribute node
+ * of the matching named attribute, nor null if no such node is found.
+ * <p>
+ * This should always be called and only be called for nodes that
+ * accept dynamic runtime attribute expressions.
+ */
+ public NamedAttribute getNamedAttributeNode( String name ) {
+ NamedAttribute result = null;
+
+ // Look for the attribute in NamedAttribute children
+ Nodes nodes = getNamedAttributeNodes();
+ int numChildNodes = nodes.size();
+ for( int i = 0; i < numChildNodes; i++ ) {
+ NamedAttribute na = (NamedAttribute)nodes.getNode( i );
+ if( na.getName().equals( name ) ) {
+ result = na;
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Searches all subnodes of this node for jsp:attribute standard
+ * actions, and returns that set of nodes as a Node.Nodes object.
+ */
+ public Node.Nodes getNamedAttributeNodes() {
+ Node.Nodes result = new Node.Nodes();
+
+ // Look for the attribute in NamedAttribute children
+ Nodes nodes = getBody();
+ if( nodes != null ) {
+ int numChildNodes = nodes.size();
+ for( int i = 0; i < numChildNodes; i++ ) {
+ Node n = nodes.getNode( i );
+ if( n instanceof NamedAttribute ) {
+ result.add( n );
+ }
+ else {
+ // Nothing can come before jsp:attribute, and only
+ // jsp:body can come after it.
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
public Nodes getBody() {
return body;
}
@@ -440,6 +494,21 @@
}
/**
+ * Represents an EL expression. Expressions in attributes are embedded
+ * in the attribute string and not here.
+ */
+ public static class ELExpression extends Node {
+
+ public ELExpression(char[] text, Mark start, Node parent) {
+ super(text, start, parent);
+ }
+
+ public void accept(Visitor v) throws JasperException {
+ v.visit(this);
+ }
+ }
+
+ /**
* Represents a param action
*/
public static class ParamAction extends Node {
@@ -606,9 +675,9 @@
*/
public static class PlugIn extends Node {
- JspAttribute height;
- JspAttribute width;
-
+ private JspAttribute width;
+ private JspAttribute height;
+
public PlugIn(Attributes attrs, Mark start, Node parent) {
super(attrs, start, parent);
}
@@ -654,6 +723,60 @@
return tagName;
}
}
+
+ /**
+ * Collected information about child elements. Used by nodes like
+ * CustomTag, JspBody, and NamedAttribute. The information is
+ * set in the Collector.
+ */
+ public static class ChildInfo {
+ private boolean scriptless; // true if the tag and its body
+ // contians no scripting elements.
+ private boolean hasUsebean;
+ private boolean hasIncludeAction;
+ private boolean hasSetProperty;
+ private boolean hasScriptingVars;
+
+ public void setScriptless(boolean s) {
+ scriptless = s;
+ }
+
+ public boolean isScriptless() {
+ return scriptless;
+ }
+
+ public void setHasUsebean(boolean u) {
+ hasUsebean = u;
+ }
+
+ public boolean isHasUsebean() {
+ return hasUsebean;
+ }
+
+ public void setHasIncludeAction(boolean i) {
+ hasIncludeAction = i;
+ }
+
+ public boolean isHasIncludeAction() {
+ return hasIncludeAction;
+ }
+
+ public void setHasSetProperty(boolean s) {
+ hasSetProperty = s;
+ }
+
+ public boolean isHasSetProperty() {
+ return hasSetProperty;
+ }
+
+ public void setHasScriptingVars(boolean s) {
+ hasScriptingVars = s;
+ }
+
+ public boolean hasScriptingVars() {
+ return hasScriptingVars;
+ }
+ }
/**
* Represents a custom tag
@@ -664,17 +787,12 @@
private String shortName;
private JspAttribute[] jspAttrs;
private TagData tagData;
- private boolean scriptless; // true if the tag and its body
- // contians no scripting elements.
- private boolean hasUsebean;
- private boolean hasIncludeAction;
- private boolean hasSetProperty;
- private boolean hasScriptingVars;
private String tagHandlerPoolName;
private TagInfo tagInfo;
private VariableInfo[] varInfos;
private int customNestingLevel;
private boolean hasUnnestedIdAttribute;
+ private ChildInfo childInfo;
public CustomTag(Attributes attrs, Mark start, String name,
String prefix, String shortName,
@@ -686,6 +804,7 @@
this.tagInfo = tagInfo;
this.customNestingLevel = computeCustomNestingLevel();
this.hasUnnestedIdAttribute = determineHasUnnestedIdAttribute();
+ this.childInfo = new ChildInfo();
}
public void accept(Visitor v) throws JasperException {
@@ -720,6 +839,10 @@
public JspAttribute[] getJspAttributes() {
return jspAttrs;
}
+
+ public ChildInfo getChildInfo() {
+ return childInfo;
+ }
public void setTagData(TagData tagData) {
this.tagData = tagData;
@@ -730,46 +853,6 @@
return tagData;
}
- public void setScriptless(boolean s) {
- scriptless = s;
- }
-
- public boolean isScriptless() {
- return scriptless;
- }
-
- public void setHasUsebean(boolean u) {
- hasUsebean = u;
- }
-
- public boolean isHasUsebean() {
- return hasUsebean;
- }
-
- public void setHasIncludeAction(boolean i) {
- hasIncludeAction = i;
- }
-
- public boolean isHasIncludeAction() {
- return hasIncludeAction;
- }
-
- public void setHasSetProperty(boolean s) {
- hasSetProperty = s;
- }
-
- public boolean isHasSetProperty() {
- return hasSetProperty;
- }
-
- public void setHasScriptingVars(boolean s) {
- hasScriptingVars = s;
- }
-
- public boolean hasScriptingVars() {
- return hasScriptingVars;
- }
-
public void setTagHandlerPoolName(String s) {
tagHandlerPoolName = s;
}
@@ -807,6 +890,42 @@
return customNestingLevel;
}
+ /**
+ * Checks to see if the attribute or fragment attribute of the
+ * given name is of type JspFragment.
+ */
+ public boolean checkIfAttributeIsJspFragment( String name ) {
+ boolean result = false;
+
+ // The attribute is of type JspFragment if it appears in
+ // the TagInfo Fragment Attributes list, or if it appears in the
+ // Attributes list and is of the right type.
+
+ TagFragmentAttributeInfo[] fragmentAttributes =
+ tagInfo.getFragmentAttributes();
+ for( int i = 0; i < fragmentAttributes.length; i++ ) {
+ if( fragmentAttributes[i].getName().equals( name ) ) {
+ result = true;
+ break;
+ }
+ }
+
+ if( !result ) {
+ TagAttributeInfo[] attributes = tagInfo.getAttributes();
+ for( int i = 0; i < attributes.length; i++ ) {
+ if( attributes[i].getName().equals( name ) &&
+ "javax.servlet.jsp.tagext.JspFragment".equals(
+ attributes[i].getTypeName() ) )
+ {
+ result = true;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
/*
* Computes this custom tag's custom nesting level, which corresponds
* to the number of times this custom tag is nested inside itself.
@@ -877,7 +996,7 @@
}
/**
- * Represents the body of a <jsp:text> element
+ * Represents the body of a <jsp:text> element
*/
public static class JspText extends Node {
@@ -891,6 +1010,84 @@
}
/**
+ * Represents a Named Attribute (<jsp:attribute>)
+ */
+ public static class NamedAttribute extends Node {
+
+ // A unique temporary variable name suitable for code generation
+ private String temporaryVariableName;
+
+ // True if this node is to be trimmed, or false otherwise
+ private boolean trim = true;
+
+ private ChildInfo childInfo;
+
+ public NamedAttribute( Attributes attrs, Mark start, Node parent) {
+ super( attrs, start, parent );
+ this.temporaryVariableName = JspUtil.nextTemporaryVariableName();
+ if( "false".equals( this.getAttributeValue( "trim" ) ) ) {
+ // (if null or true, leave default of true)
+ trim = false;
+ }
+ this.childInfo = new ChildInfo();
+ }
+
+ public void accept(Visitor v) throws JasperException {
+ v.visit(this);
+ }
+
+ public String getName() {
+ return this.getAttributeValue( "name" );
+ }
+
+ public ChildInfo getChildInfo() {
+ return this.childInfo;
+ }
+
+ public boolean isTrim() {
+ return trim;
+ }
+
+ /**
+ * @return A unique temporary variable name to store the result in.
+ * (this probably could go elsewhere, but it's convenient here)
+ */
+ public String getTemporaryVariableName() {
+ return temporaryVariableName;
+ }
+ }
+
+ /**
+ * Represents a JspBody node (<jsp:body>)
+ */
+ public static class JspBody extends Node {
+
+ private JspAttribute value;
+ private ChildInfo childInfo;
+
+ public JspBody( Attributes attrs, Mark start, Node parent) {
+ super( attrs, start, parent );
+ this.childInfo = new ChildInfo();
+ }
+
+ public void accept(Visitor v) throws JasperException {
+ v.visit(this);
+ }
+
+ public void setValue(JspAttribute value) {
+ this.value = value;
+ }
+
+ public JspAttribute getValue() {
+ return value;
+ }
+
+ public ChildInfo getChildInfo() {
+ return childInfo;
+ }
+ }
+
+ /**
* Represents a template text string
*/
public static class TemplateText extends Node {
@@ -902,6 +1099,35 @@
public void accept(Visitor v) throws JasperException {
v.visit(this);
}
+
+ /**
+ * Trim all whitespace from the left of the template text
+ */
+ public void ltrim() {
+ // Whitespace logic borrowed from JspReader.isSpace
+ int index = 0;
+ while ((index < text.length) && (text[index] <= ' ')) {
+ index++;
+ }
+ int size = text.length - index;
+ char[] newText = new char[size];
+ System.arraycopy(text, index, newText, 0, size);
+ text = newText;
+ }
+
+ /**
+ * Trim all whitespace from the right of the template text
+ */
+ public void rtrim() {
+ // Whitespace logic borrowed from JspReader.isSpace
+ int size = text.length;
+ while( (size > 0) && (text[size-1] <= ' ') ) {
+ size--;
+ }
+ char[] newText = new char[size];
+ System.arraycopy( text, 0, newText, 0, size );
+ text = newText;
+ }
}
/*********************************************************************
@@ -910,6 +1136,10 @@
/**
* Represents attributes that can be request time expressions.
+ *
+ * Can either be a plain attribute, an attribute that represents a
+ * request time expression value, or a named attribute (specified using
+ * the jsp:attribute standard action).
*/
public static class JspAttribute {
@@ -917,13 +1147,36 @@
private String name;
private String value;
private boolean expression;
+ private boolean el;
- JspAttribute(String name, String value, boolean expr) {
+ // If true, this JapAttribute represents a <jsp:attribute>
+ private boolean namedAttribute;
+ // The node in the parse tree for the NamedAttribute
+ private NamedAttribute namedAttributeNode;
+
+ JspAttribute(String name, String value, boolean expr, boolean el ) {
this.name = name;
this.value = value;
+ this.namedAttributeNode = null;
this.expression = expr;
+ this.el = el;
+ this.namedAttribute = false;
}
+ /**
+ * Use this constructor if the JspAttribute represents a
+ * named attribute. In this case, we have to store the nodes of
+ * the body of the attribute.
+ */
+ JspAttribute( String name, NamedAttribute namedAttributeNode ) {
+ this.name = name;
+ this.value = null;
+ this.namedAttributeNode = namedAttributeNode;
+ this.expression = false;
+ this.el = false;
+ this.namedAttribute = true;
+ }
+
/**
* @return The name of the attribute
*/
@@ -932,19 +1185,56 @@
}
/**
- * @return the value for the attribute, or the expression string
- * (stripped of "<%=", "%>", "%=", or "%")
+ * Only makes sense if namedAttribute is false.
+ *
+ * @return the value for the attribute, or the expression string
+ * (stripped of "<%=", "%>", "%=", or "%"
+ * but containing "${" and "}" for EL expressions)
*/
public String getValue() {
return value;
}
+ /**
+ * Only makes sense if namedAttribute is true.
+ *
+ * @return the nodes that evaluate to the body of this attribute.
+ */
+ public NamedAttribute getNamedAttributeNode() {
+ return namedAttributeNode;
+ }
+
/**
- * @return true is the value represents an expression
+ * @return true if the value represents a traditional rtexprvalue
*/
public boolean isExpression() {
return expression;
}
+
+ /**
+ * @return true if the value represents a NamedAttribute value.
+ */
+ public boolean isNamedAttribute() {
+ return namedAttribute;
+ }
+
+ /**
+ * @return true if the value represents an expression that should
+ * be fed to the expression interpreter
+ * @return false for string literals or rtexprvalues that should
+ * not be interpreter or reevaluated
+ */
+ public boolean isELInterpreterInput() {
+ return el;
+ }
+
+ /**
+ * @return true if the value is a string literal know at translation
+ * time.
+ */
+ public boolean isLiteral() {
+ return !expression && !el && !namedAttribute;
+ }
}
/**
@@ -1068,6 +1358,10 @@
doVisit(n);
}
+ public void visit(ELExpression n) throws JasperException {
+ doVisit(n);
+ }
+
public void visit(IncludeAction n) throws JasperException {
doVisit(n);
visitBody(n);
@@ -1084,10 +1378,12 @@
public void visit(SetProperty n) throws JasperException {
doVisit(n);
+ visitBody(n);
}
public void visit(ParamAction n) throws JasperException {
doVisit(n);
+ visitBody(n);
}
public void visit(ParamsAction n) throws JasperException {
@@ -1124,6 +1420,16 @@
doVisit(n);
visitBody(n);
}
+
+ public void visit(NamedAttribute n) throws JasperException {
+ doVisit(n);
+ visitBody(n);
+ }
+
+ public void visit(JspBody n) throws JasperException {
+ doVisit(n);
+ visitBody(n);
+ }
public void visit(TemplateText n) throws JasperException {
doVisit(n);
1.4 +12 -4 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/PageDataImpl.java
Index: PageDataImpl.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/PageDataImpl.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- PageDataImpl.java 17 May 2002 23:44:00 -0000 1.3
+++ PageDataImpl.java 16 Jul 2002 19:30:51 -0000 1.4
@@ -325,6 +325,14 @@
appendTag(JSP_PLUGIN_TAG, n.getAttributes(), n.getBody());
}
+ public void visit(Node.NamedAttribute n) throws JasperException {
+ appendTag(JSP_ATTRIBUTE_TAG, n.getAttributes(), n.getBody());
+ }
+
+ public void visit(Node.JspBody n) throws JasperException {
+ appendTag(JSP_BODY_TAG, n.getAttributes(), n.getBody());
+ }
+
public void visit(Node.CustomTag n) throws JasperException {
appendTag(n.getName(), n.getAttributes(), n.getBody());
}
@@ -337,7 +345,7 @@
// jsp:text has no attributes, except for jsp:id
appendTag(JSP_TEXT_TAG, n.getAttributes(), n.getBody());
}
-
+
public void visit(Node.TemplateText n) throws JasperException {
/*
* If the template text came from a JSP page written in JSP syntax,
1.6 +10 -2 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/PageInfo.java
Index: PageInfo.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/PageInfo.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- PageInfo.java 5 Jun 2002 22:01:33 -0000 1.5
+++ PageInfo.java 16 Jul 2002 19:30:51 -0000 1.6
@@ -90,6 +90,7 @@
private String pageEncoding = null;
private int maxTagNesting = 0;
private boolean scriptless = false;
+ private boolean scriptingEnabled = true;
PageInfo(BeanRepository beanRepository) {
this.beanRepository = beanRepository;
@@ -222,4 +223,11 @@
return scriptless;
}
+ public void setScriptingEnabled(boolean s) {
+ scriptingEnabled = s;
+ }
+
+ public boolean isScriptingEnabled() {
+ return scriptingEnabled;
+ }
}
1.8 +553 -189 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Parser.java
Index: Parser.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Parser.java,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- Parser.java 8 Jul 2002 23:47:55 -0000 1.7
+++ Parser.java 16 Jul 2002 19:30:51 -0000 1.8
@@ -67,6 +67,7 @@
import javax.servlet.jsp.tagext.TagInfo;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.AttributesImpl;
+import org.apache.jasper.Constants;
import org.apache.jasper.JspCompilationContext;
import org.apache.jasper.JasperException;
@@ -77,6 +78,8 @@
* location in the production.
*
* @author Kin-man Chung
+ * @author Shawn Bayern
+ * @author Mark Roth
*/
public class Parser {
@@ -88,6 +91,14 @@
private Mark start;
private Hashtable taglibs;
private ErrorDispatcher err;
+ private int scriptlessCount;
+
+ // Virtual body content types, to make parsing a little easier.
+ // These are not accessible from outside the parser.
+ private static final String JAVAX_BODY_CONTENT_PARAM =
+ "JAVAX_BODY_CONTENT_PARAM";
+ private static final String JAVAX_BODY_CONTENT_PLUGIN =
+ "JAVAX_BODY_CONTENT_PLUGIN";
/**
* The constructor
@@ -99,6 +110,7 @@
this.err = pc.getCompiler().getErrorDispatcher();
this.reader = reader;
this.currentFile = reader.mark().getFile();
+ this.scriptlessCount = 0;
start = reader.mark();
}
@@ -151,9 +163,9 @@
/**
* Attribute ::= Name S? Eq S?
- * ( '"<%= RTAttributeValueDouble
+ * ( '"<%=' RTAttributeValueDouble
* | '"' AttributeValueDouble
- * | "'<%= RTAttributeValueSingle
+ * | "'<%=" RTAttributeValueSingle
* | "'" AttributeValueSingle
* }
* Note: JSP and XML spec does not allow while spaces around Eq. It is
@@ -352,7 +364,7 @@
// Errors to be checked in Validator
String[] location = ctxt.getTldLocation(uri);
TagLibraryInfo tl = new TagLibraryInfoImpl(ctxt, prefix, uri,
- location, err);
+ location, err);
taglibs.put(prefix, tl);
}
@@ -429,7 +441,38 @@
new Node.Expression(parseScriptText(reader.getText(start, stop)),
start, parent);
}
-
+
+ /*
+ * ELExpressionBody
+ * (following "${" to first unquoted "}")
+ * // XXX add formal production and confirm implementation against it,
+ * // once it's decided
+ */
+ private void parseELExpression(Node parent) throws JasperException {
+ start = reader.mark();
+ Mark last = null;
+ boolean singleQuoted = false, doubleQuoted = false;
+ int currentChar;
+ do {
+ // XXX could move this logic to JspReader
+ last = reader.mark(); // XXX somewhat wasteful
+ currentChar = reader.nextChar();
+ if (currentChar == '\\' && (singleQuoted || doubleQuoted)) {
+ // skip character following '\' within quotes
+ reader.nextChar();
+ currentChar = reader.nextChar();
+ }
+ if (currentChar == -1)
+ err.jspError(start, "jsp.error.unterminated", "${");
+ if (currentChar == '"')
+ doubleQuoted = !doubleQuoted;
+ if (currentChar == '\'')
+ singleQuoted = !singleQuoted;
+ } while (currentChar != '}' || (singleQuoted || doubleQuoted));
+
+ new Node.ELExpression(reader.getText(start, last), start, parent);
+ }
+
/*
* Scriptlet ::= (Char* - (char* '%>')) '%>'
*/
@@ -445,217 +488,338 @@
}
/**
- * Param ::= '<jsp:param' Attributes* '/>'
+ * Param ::= '<jsp:param' S Attributes S? EmptyBody S?
*/
private void parseParam(Node parent) throws JasperException {
-
if (!reader.matches("<jsp:param")) {
err.jspError(reader.mark(), "jsp.error.paramexpectedonly");
}
Attributes attrs = parseAttributes();
reader.skipSpaces();
-
- if (!reader.matches("/>")) {
- err.jspError(reader.mark(), "jsp.error.unterminated",
- "<jsp:param");
- }
-
- new Node.ParamAction(attrs, start, parent);
- }
-
- /**
- * Params ::= (Param S?)*
- */
- private void parseParams(Node parent, String tag) throws JasperException {
-
- Mark start = reader.mark();
-
- while (reader.hasMoreInput()) {
- if (reader.matchesETag(tag)) {
- break;
- }
-
- parseParam(parent);
- reader.skipSpaces();
- }
+
+ Node paramActionNode = new Node.ParamAction( attrs, start, parent );
+
+ parseEmptyBody( paramActionNode, "jsp:param" );
+
+ reader.skipSpaces();
}
/*
- * IncludeAction ::= Attributes
- * ( '/>'
- * | '>' S? Params '</jsp:include' S? '>'
- * )
+ * For Include:
+ * StdActionContent ::= Attributes ParamBody
+ *
+ * ParamBody ::= EmptyBody
+ * | ( '>' S? ( '<jsp:attribute' NamedAttributes )?
+ * '<jsp:body'
+ * (JspBodyParam | <TRANSLATION_ERROR> )
+ * S? ETag
+ * )
+ * | ( '>' S? Param* ETag )
+ *
+ * EmptyBody ::= '/>'
+ * | ( '>' ETag )
+ * | ( '>' S? '<jsp:attribute' NamedAttributes ETag )
+ *
+ * JspBodyParam ::= S? '>' Param* '</jsp:body>'
*/
private void parseInclude(Node parent) throws JasperException {
Attributes attrs = parseAttributes();
reader.skipSpaces();
- if (reader.matches("/>")) {
- // No body
- new Node.IncludeAction(attrs, start, parent);
- return;
- }
-
- if (!reader.matches(">")) {
- err.jspError(reader.mark(), "jsp.error.unterminated",
- "<jsp:include");
- }
-
- reader.skipSpaces();
- Node includeNode = new Node.IncludeAction(attrs, start, parent);
- parseParams(includeNode, "jsp:include");
+ Node includeNode = new Node.IncludeAction( attrs, start, parent );
+
+ parseOptionalBody( includeNode, "jsp:include",
+ JAVAX_BODY_CONTENT_PARAM );
}
-
+
/*
- * ForwardAction ::= Attributes
- * ( '/>'
- * | '>' S? Params '<jsp:forward' S? '>'
- * )
+ * For Forward:
+ * StdActionContent ::= Attributes ParamBody
*/
private void parseForward(Node parent) throws JasperException {
-
Attributes attrs = parseAttributes();
reader.skipSpaces();
- if (reader.matches("/>")) {
- // No body
- new Node.ForwardAction(attrs, start, parent);
- return;
- }
-
- if (!reader.matches(">")) {
- err.jspError(reader.mark(), "jsp.error.unterminated",
- "<jsp:forward");
- }
-
- reader.skipSpaces();
- Node forwardNode = new Node.ForwardAction(attrs, start, parent);
- parseParams(forwardNode, "jsp:forward");
+ Node forwardNode = new Node.ForwardAction( attrs, start, parent );
+
+ parseOptionalBody( forwardNode, "jsp:forward",
+ JAVAX_BODY_CONTENT_PARAM );
}
/*
- * GetProperty ::= (S? Attribute)* S? '/>/
+ * For GetProperty:
+ * StdActionContent ::= Attributes EmptyBody
*/
private void parseGetProperty(Node parent) throws JasperException {
Attributes attrs = parseAttributes();
reader.skipSpaces();
- if (!reader.matches("/>")) {
- err.jspError(reader.mark(), "jsp.error.unterminated",
- "<jsp:getProperty");
- }
-
- new Node.GetProperty(attrs, start, parent);
+ Node getPropertyNode = new Node.GetProperty( attrs, start, parent );
+
+ parseEmptyBody( getPropertyNode, "jsp:getProperty" );
}
/*
- * SetProperty ::= (S Attribute)* S? '/>'
+ * For SetProperty:
+ * StdActionContent ::= Attributes EmptyBody
*/
private void parseSetProperty(Node parent) throws JasperException {
Attributes attrs = parseAttributes();
reader.skipSpaces();
- if (!reader.matches("/>")) {
- err.jspError(reader.mark(), "jsp.error.unterminated",
- "<jsp:setProperty");
- }
+ Node setPropertyNode = new Node.SetProperty( attrs, start, parent );
+
+ parseEmptyBody( setPropertyNode, "jsp:setProperty" );
+ }
- new Node.SetProperty(attrs, start, parent);
+ /*
+ * EmptyBody ::= '/>'
+ * | ( '>' ETag )
+ * | ( '>' S? '<jsp:attribute' NamedAttributes ETag )
+ */
+ private void parseEmptyBody( Node parent, String tag )
+ throws JasperException
+ {
+ if( reader.matches("/>") ) {
+ // Done
+ }
+ else if( reader.matches( ">" ) ) {
+ if( reader.matchesETag( tag ) ) {
+ // Done
+ }
+ else if( reader.matchesOptionalSpacesFollowedBy(
+ "<jsp:attribute" ) )
+ {
+ // Parse the one or more named attribute nodes
+ parseNamedAttributes( parent );
+ if( !reader.matchesETag( tag ) ) {
+ // Body not allowed
+ err.jspError(reader.mark(),
+ "jsp.error.jspbody.emptybody.only",
+ "<" + tag );
+ }
+ }
+ else {
+ err.jspError(reader.mark(), "jsp.error.jspbody.emptybody.only",
+ "<" + tag );
+ }
+ }
+ else {
+ err.jspError(reader.mark(), "jsp.error.unterminated",
+ "<" + tag );
+ }
}
/*
- * UseBean ::= (S Attribute)* S?
- * ('/>' | ( '>' Body '</jsp:useBean' S? '>' ))
+ * For UseBean:
+ * StdActionContent ::= Attributes OptionalBody
*/
private void parseUseBean(Node parent) throws JasperException {
Attributes attrs = parseAttributes();
reader.skipSpaces();
+
+ Node useBeanNode = new Node.UseBean( attrs, start, parent );
+
+ parseOptionalBody( useBeanNode, "jsp:useBean",
+ TagInfo.BODY_CONTENT_JSP );
+ }
+ /*
+ * Parses OptionalBody, but also reused to parse bodies for plugin
+ * and param since the syntax is identical (the only thing that
+ * differs substantially is how to process the body, and thus
+ * we accept the body type as a parameter).
+ *
+ * OptionalBody ::= EmptyBody | ActionBody
+ *
+ * ScriptlessOptionalBody ::= EmptyBody | ScriptlessActionBody
+ *
+ * TagDependentOptionalBody ::= EmptyBody | TagDependentActionBody
+ *
+ * EmptyBody ::= '/>'
+ * | ( '>' ETag )
+ * | ( '>' S? '<jsp:attribute' NamedAttributes ETag )
+ *
+ * ActionBody ::= JspAttributeAndBody
+ * | ( '>' Body ETag )
+ *
+ * ScriptlessActionBody ::= JspAttributeAndBody
+ * | ( '>' ScriptlessBody ETag )
+ *
+ * TagDependentActionBody ::= JspAttributeAndBody
+ * | ( '>' TagDependentBody ETag )
+ *
+ */
+ private void parseOptionalBody( Node parent, String tag, String bodyType )
+ throws JasperException
+ {
if (reader.matches("/>")) {
- new Node.UseBean(attrs, start, parent);
+ // EmptyBody
return;
}
if (!reader.matches(">")) {
err.jspError(reader.mark(), "jsp.error.unterminated",
- "<jsp:useBean");
+ "<" + tag );
}
-
- Node beanNode = new Node.UseBean(attrs, start, parent);
- parseBody(beanNode, "jsp:useBean");
+
+ if( reader.matchesETag( tag ) ) {
+ // EmptyBody
+ return;
+ }
+
+ if( !parseJspAttributeAndBody( parent, tag, bodyType ) ) {
+ // Must be ( '>' # Body ETag )
+ parseBody(parent, tag, bodyType );
+ }
+ }
+
+ /**
+ * Attempts to parse 'JspAttributeAndBody' production. Returns true if
+ * it matched, or false if not. Assumes EmptyBody is okay as well.
+ *
+ * JspAttributeAndBody ::=
+ * ( '>' # S? ( '<jsp:attribute' NamedAttributes )?
+ * '<jsp:body'
+ * ( JspBodyBody | <TRANSLATION_ERROR> )
+ * S? ETag
+ * )
+ */
+ private boolean parseJspAttributeAndBody( Node parent, String tag,
+ String bodyType )
+ throws JasperException
+ {
+ boolean result = false;
+
+ if( reader.matchesOptionalSpacesFollowedBy( "<jsp:attribute" ) ) {
+ // May be an EmptyBody, depending on whether
+ // There's a "<jsp:body" before the ETag
+
+ // First, parse <jsp:attribute> elements:
+ parseNamedAttributes( parent );
+
+ result = true;
+ }
+
+ if( reader.matchesOptionalSpacesFollowedBy( "<jsp:body" ) ) {
+ // ActionBody
+ parseJspBody( parent, bodyType );
+ reader.skipSpaces();
+ if( !reader.matchesETag( tag ) ) {
+ err.jspError(reader.mark(), "jsp.error.unterminated",
+ "<" + tag );
+ }
+
+ result = true;
+ }
+ else if( result && !reader.matchesETag( tag ) ) {
+ // If we have <jsp:attribute> but something other than
+ // <jsp:body> or the end tag, translation error.
+ err.jspError(reader.mark(), "jsp.error.jspbody.required",
+ "<" + tag );
+ }
+
+ return result;
}
/*
- * JspParams ::= S? '>' S? Params+ </jsp:params' S? '>'
+ * Params ::= '/>'
+ * | ( '>' S? Param* '</jsp:params>' )
*/
private void parseJspParams(Node parent) throws JasperException {
-
- reader.skipSpaces();
- if (!reader.matches(">")) {
- err.jspError(reader.mark(), "jsp.error.params.notclosed");
- }
-
- reader.skipSpaces();
- Node jspParamsNode = new Node.ParamsAction(start, parent);
- parseParams(jspParamsNode, "jsp:params");
+ if( reader.matches( "/>" ) ) {
+ // No elements, don't create node.
+ }
+ else if( reader.matches( ">" ) ) {
+ reader.skipSpaces();
+ Node jspParamsNode = new Node.ParamsAction(start, parent);
+ parseBody(jspParamsNode, "jsp:params", JAVAX_BODY_CONTENT_PARAM );
+ }
+ else {
+ err.jspError(reader.mark(), "jsp.error.unterminated",
+ "<jsp:params" );
+ }
}
/*
- * FallBack ::= S? '>' Char* '</jsp:fallback' S? '>'
+ * Fallback ::= '/>'
+ * | ( '>'
+ * ( Char* - ( Char* '</jsp:fallback>' ) )
+ * '</jsp:fallback>'
+ * )
*/
private void parseFallBack(Node parent) throws JasperException {
-
- reader.skipSpaces();
- if (!reader.matches(">")) {
- err.jspError(reader.mark(), "jsp.error.fallback.notclosed");
- }
-
- Mark bodyStart = reader.mark();
- Mark bodyEnd = reader.skipUntilETag("jsp:fallback");
- if (bodyEnd == null) {
- err.jspError(start, "jsp.error.unterminated", "<jsp:fallback>");
- }
- char[] text = reader.getText(bodyStart, bodyEnd);
- new Node.FallBackAction(start, text, parent);
+ if( reader.matches( "/>" ) ) {
+ // No elements, don't create node.
+ }
+ else if( reader.matches( ">" ) ) {
+ Mark bodyStart = reader.mark();
+ Mark bodyEnd = reader.skipUntilETag("jsp:fallback");
+ if (bodyEnd == null) {
+ err.jspError(start, "jsp.error.unterminated",
+ "<jsp:fallback");
+ }
+ char[] text = reader.getText(bodyStart, bodyEnd);
+ new Node.FallBackAction(start, text, parent);
+ }
+ else {
+ err.jspError( reader.mark(), "jsp.error.unterminated",
+ "<jsp:fallback" );
+ }
}
/*
- * PlugIn ::= Attributes '>' PlugInBody '</jsp:plugin' S? '>'
- * PlugBody ::= S? ('<jsp:params' JspParams S?)?
- * ('<jsp:fallback' JspFallack S?)?
+ * For Plugin:
+ * StdActionContent ::= Attributes PluginBody
+ *
+ * PluginBody ::= EmptyBody
+ * | ( '>' S? ( '<jsp:attribute' NamedAttributes )?
+ * '<jsp:body'
+ * ( JspBodyPluginTags | <TRANSLATION_ERROR> )
+ * S? ETag
+ * )
+ * | ( '>' S? PluginTags ETag )
+ *
+ * EmptyBody ::= '/>'
+ * | ( '>' ETag )
+ * | ( '>' S? '<jsp:attribute' NamedAttributes ETag )
+ *
*/
private void parsePlugin(Node parent) throws JasperException {
Attributes attrs = parseAttributes();
reader.skipSpaces();
-
- if (!reader.matches(">")) {
- err.jspError(reader.mark(), "jsp.error.plugin.notclosed");
- }
-
- reader.skipSpaces();
+
Node pluginNode = new Node.PlugIn(attrs, start, parent);
- if (reader.matches("<jsp:params")) {
- parseJspParams(pluginNode);
- reader.skipSpaces();
- }
-
- if (reader.matches("<jsp:fallback")) {
- parseFallBack(pluginNode);
- reader.skipSpaces();
- }
-
- if (!reader.matchesETag("jsp:plugin")) {
- err.jspError(reader.mark(), "jsp.error.plugin.notclosed");
- }
+
+ parseOptionalBody( pluginNode, "jsp:plugin",
+ JAVAX_BODY_CONTENT_PLUGIN );
}
/*
- * StandardAction ::= 'include' IncludeAction
- * | 'forward' ForwardAction
- * | 'getProperty' GetPropertyAction
- * | 'setProperty' SetPropertyAction
- * | 'useBean' UseBeanAction
- * | 'plugin' PlugInAction
+ * PluginTags ::= ( '<jsp:params' Params S? )?
+ * ( '<jsp:fallback' Fallback? S? )?
+ */
+ private void parsePluginTags( Node parent ) throws JasperException {
+ reader.skipSpaces();
+
+ if( reader.matches( "<jsp:params" ) ) {
+ parseJspParams( parent );
+ reader.skipSpaces();
+ }
+
+ if( reader.matches( "<jsp:fallback" ) ) {
+ parseFallBack( parent );
+ reader.skipSpaces();
+ }
+ }
+
+ /*
+ * StandardAction ::= 'include' StdActionContent
+ * | 'forward' StdActionContent
+ * | 'getProperty' StdActionContent
+ * | 'setProperty' StdActionContent
+ * | 'useBean' StdActionContent
+ * | 'plugin' StdActionContent
*/
private void parseAction(Node parent) throws JasperException {
Mark start = reader.mark();
@@ -678,16 +842,35 @@
}
/*
- * ActionElement ::= EmptyElemTag | Stag Body Etag
- * EmptyElemTag ::= '<' Name ( S Attribute )* S? '/>'
- * Stag ::= '<' Name ( S Attribute)* S? '>'
- * Etag ::= '</' Name S? '>'
+ * # '<' CustomAction CustomActionBody
+ *
+ * CustomAction ::= TagPrefix ':' CustomActionName
+ *
+ * TagPrefix ::= Name
+ *
+ * CustomActionName ::= Name
+ *
+ * CustomActionBody ::= ( Attributes CustomActionEnd )
+ * | <TRANSLATION_ERROR>
+ *
+ * Attributes ::= ( S Attribute )* S?
+ *
+ * CustomActionEnd ::= CustomActionTagDependent
+ * | CustomActionJSPContent
+ * | CustomActionScriptlessContent
+ *
+ * CustomActionTagDependent ::= TagDependentOptionalBody
+ *
+ * CustomActionJSPContent ::= OptionalBody
+ *
+ * CustomActionScriptlessContent ::= ScriptlessOptionalBody
*/
private boolean parseCustomTag(Node parent) throws JasperException {
if (reader.peekChar() != '<') {
return false;
}
+ // Parse 'CustomAction' production (tag prefix and custom action name)
reader.nextChar(); // skip '<'
String tagName = reader.parseToken(false);
int i = tagName.indexOf(':');
@@ -709,46 +892,35 @@
if (tagInfo == null) {
err.jspError(start, "jsp.error.bad_tag", shortTagName, prefix);
}
+
+ // Parse 'CustomActionBody' production:
+ // At this point we are committed - if anything fails, we produce
+ // a translation error.
- // EmptyElemTag ::= '<' Name ( #S Attribute )* S? '/>'
- // or Stag ::= '<' Name ( #S Attribute)* S? '>'
+ // Parse 'Attributes' production:
Attributes attrs = parseAttributes();
reader.skipSpaces();
+ // Parse 'CustomActionEnd' production:
if (reader.matches("/>")) {
- // EmptyElemTag ::= '<' Name ( S Attribute )* S? '/>'#
new Node.CustomTag(attrs, start, tagName, prefix, shortTagName,
tagInfo, parent);
return true;
}
- if (!reader.matches(">")) {
- err.jspError(start, "jsp.error.unterminated.tag");
- }
+ // Now we parse one of 'CustomActionTagDependent',
+ // 'CustomActionJSPContent', or 'CustomActionScriptlessContent'.
+ // depending on body-content in TLD.
- // ActionElement ::= Stag #Body Etag
-
// Looking for a body, it still can be empty; but if there is a
// a tag body, its syntax would be dependent on the type of
// body content declared in TLD.
- String bc = tagInfo.getBodyContent();
+ String bc = ((TagLibraryInfo)taglibs.get(prefix)).getTag(
+ shortTagName).getBodyContent();
Node tagNode = new Node.CustomTag(attrs, start, tagName, prefix,
shortTagName, tagInfo, parent);
- // There are 3 body content types: empty, jsp, or tag-dependent.
- if (bc.equalsIgnoreCase(TagInfo.BODY_CONTENT_EMPTY)) {
- if (!reader.matchesETag(tagName)) {
- err.jspError(start, "jasper.error.emptybodycontent.nonempty");
- }
- } else if (bc.equalsIgnoreCase(TagInfo.BODY_CONTENT_TAG_DEPENDENT)) {
- // parse the body as text
- parseBodyText(tagNode, tagName);
- } else if (bc.equalsIgnoreCase(TagInfo.BODY_CONTENT_JSP)) {
- // parse body as JSP page
- parseBody(tagNode, tagName);
- } else {
- err.jspError(start, "jasper.error.bad.bodycontent.type");
- }
+ parseOptionalBody( tagNode, tagName, bc );
return true;
}
@@ -771,18 +943,29 @@
new Node.TemplateText(reader.nextContent(), start, parent);
}
}
-
+
/*
- * BodyElement ::= '<%--' JSPCommentBody
- * | '<%@' DirectiveBody
- * | '<%!' DeclarationBody
- * | '<%=' ExpressionBody
- * | '<%' ScriptletBody
- * | '<jsp:' StandardAction
- * | '<' CustomAction
+ * AllBody ::= ( '<%--' JSPCommentBody )
+ * | ( '<%@' DirectiveBody )
+ * | ( '<%!' DeclarationBody )
+ * | ( '<%=' ExpressionBody )
+ * | ( '${' ELExpressionBody )
+ * | ( '<%' ScriptletBody )
+ * | ( '<jsp:' StandardAction )
+ * | ( '<' CustomAction CustomActionBody )
* | TemplateText
*/
- private void parseElements(Node parent) throws JasperException {
+ private void parseElements(Node parent)
+ throws JasperException
+ {
+ if( scriptlessCount > 0 ) {
+ // vc: ScriptlessBody
+ // We must follow the ScriptlessBody production if one of
+ // our parents is ScriptlessBody.
+ parseElementsScriptless( parent );
+ return;
+ }
+
start = reader.mark();
if (reader.matches("<%--")) {
parseComment(parent);
@@ -794,6 +977,8 @@
parseExpression(parent);
} else if (reader.matches("<%")) {
parseScriptlet(parent);
+ } else if (reader.matches("${")) {
+ parseELExpression(parent);
} else if (reader.matches("<jsp:")) {
parseAction(parent);
} else if (!parseCustomTag(parent)) {
@@ -802,33 +987,212 @@
}
/*
+ * ScriptlessBody ::= ( '<%--' JSPCommentBody )
+ * | ( '<%@' <TRANSLATION_ERROR> )
+ * | ( '<%!' <TRANSLATION_ERROR> )
+ * | ( '<%=' <TRANSLATION_ERROR> )
+ * | ( '<%' <TRANSLATION_ERROR> )
+ * | ( '${' ELExpressionBody )
+ * | ( '<jsp:' StandardAction )
+ * | ( '<' CustomAction CustomActionBody )
+ * | TemplateText
+ */
+ private void parseElementsScriptless(Node parent)
+ throws JasperException
+ {
+ // Keep track of how many scriptless nodes we've encountered
+ // so we know whether our child nodes are forced scriptless
+ scriptlessCount++;
+
+ start = reader.mark();
+ if (reader.matches("<%--")) {
+ parseComment(parent);
+ } else if (reader.matches("<%@")) {
+ parseDirective(parent);
+ } else if (reader.matches("<%!")) {
+ err.jspError( reader.mark(), "jsp.error.no.scriptlets" );
+ } else if (reader.matches("<%=")) {
+ err.jspError( reader.mark(), "jsp.error.no.scriptlets" );
+ } else if (reader.matches("<%")) {
+ err.jspError( reader.mark(), "jsp.error.no.scriptlets" );
+ } else if (reader.matches("${")) {
+ parseELExpression(parent);
+ } else if (reader.matches("<jsp:")) {
+ parseAction(parent);
+ } else if (!parseCustomTag(parent)) {
+ parseTemplateText(parent);
+ }
+
+ scriptlessCount--;
+ }
+
+ /*
*
*/
private void parseBodyText(Node parent, String tag) throws JasperException{
Mark bodyStart = reader.mark();
Mark bodyEnd = reader.skipUntilETag(tag);
if (bodyEnd == null) {
- err.jspError(start, "jsp.error.unterminated", "<"+tag+">");
+ err.jspError(start, "jsp.error.unterminated", "<"+tag );
}
new Node.TemplateText(reader.getText(bodyStart, bodyEnd), bodyStart,
parent);
}
/*
+ * JspBodyBody ::= ( S? '>'
+ * ( ( ScriptlessBody
+ * | Body
+ * | TagDependentBody
+ * ) - ''
+ * ) '</jsp:body>'
+ * )
+ * | ( ATTR[ !value ] S? JspBodyEmptyBody )
+ *
+ * JspBodyEmptyBody ::= '/>'
+ * | '></jsp:body>'
+ * | <TRANSLATION_ERROR>
+ *
+ */
+ private void parseJspBody(Node parent, String bodyType)
+ throws JasperException
+ {
+ Mark start = reader.mark();
+ reader.skipSpaces();
+
+ if( reader.matches( ">" ) ) {
+ Node bodyNode = new Node.JspBody( null, start, parent );
+ if( reader.matches( "</jsp:body>" ) ) {
+ // Body was empty. This is illegal, according to the grammar.
+ err.jspError(reader.mark(),
+ "jsp.error.empty.body.not.allowed",
+ "<jsp:body>" );
+ }
+ else {
+ parseBody( bodyNode, "jsp:body", bodyType );
+ }
+ }
+ else {
+ Attributes attrs = parseAttributes();
+ reader.skipSpaces();
+ new Node.JspBody( attrs, start, parent );
+
+ if( !reader.matches( "/>" ) &&
+ !reader.matches( "></jsp:body>" ) )
+ {
+ err.jspError(reader.mark(),
+ "jsp.error.jspbody.body.not.allowed.with.value",
+ "<jsp:body>" );
+ }
+ }
+ }
+
+ /*
* Parse the body as JSP content.
* @param tag The name of the tag whose end tag would terminate the body
+ * @param bodyType One of the TagInfo body types
*/
- private void parseBody(Node parent, String tag) throws JasperException {
+ private void parseBody(Node parent, String tag, String bodyType)
+ throws JasperException
+ {
+ if( bodyType.equalsIgnoreCase( TagInfo.BODY_CONTENT_TAG_DEPENDENT ) ) {
+ parseBodyText( parent, tag );
+ }
+ else if( bodyType.equalsIgnoreCase( TagInfo.BODY_CONTENT_EMPTY ) ) {
+ if( !reader.matchesETag( tag ) ) {
+ err.jspError(start, "jasper.error.emptybodycontent.nonempty");
+ }
+ }
+ else if( bodyType == JAVAX_BODY_CONTENT_PLUGIN ) {
+ // (note the == since we won't recognize JAVAX_*
+ // from outside this module).
+ parsePluginTags(parent);
+ if( !reader.matchesETag( tag ) ) {
+ err.jspError( reader.mark(), "jsp.error.unterminated",
+ "<" + tag );
+ }
+ }
+ else if( bodyType.equalsIgnoreCase( TagInfo.BODY_CONTENT_JSP ) ||
+ bodyType.equalsIgnoreCase( TagInfo.BODY_CONTENT_SCRIPTLESS ) ||
+ (bodyType == JAVAX_BODY_CONTENT_PARAM) )
+ {
+ while (reader.hasMoreInput()) {
+ if (reader.matchesETag(tag)) {
+ return;
+ }
+
+ if( bodyType.equalsIgnoreCase( TagInfo.BODY_CONTENT_JSP ) ) {
+ parseElements( parent );
+ }
+ else if( bodyType.equalsIgnoreCase(
+ TagInfo.BODY_CONTENT_SCRIPTLESS ) )
+ {
+ parseElementsScriptless( parent );
+ }
+ else if( bodyType == JAVAX_BODY_CONTENT_PARAM ) {
+ // (note the == since we won't recognize JAVAX_*
+ // from outside this module).
+ reader.skipSpaces();
+ parseParam( parent );
+ }
+ }
+ err.jspError(start, "jsp.error.unterminated", "<"+tag );
+ }
+ else {
+ err.jspError(start, "jasper.error.bad.bodycontent.type");
+ }
+ }
- while (reader.hasMoreInput()) {
- if (reader.matchesETag(tag)) {
- return;
- }
- parseElements(parent);
- }
- err.jspError(start, "jsp.error.unterminated", "<"+tag+">");
+ /*
+ * NamedAttributes ::= AttributeBody S?
+ * ( '<jsp:attribute' AttributeBody S? )*
+ *
+ * AttributeBody ::= ATTR[ !name, trim ] S? AttributeBodyRequired
+ *
+ * AttributeBodyRequired ::= '>' ( ScriptlessBody - '' ) '</jsp:attribute>'
+ */
+ private void parseNamedAttributes(Node parent) throws JasperException {
+ do {
+ Mark start = reader.mark();
+ Attributes attrs = parseAttributes();
+ reader.skipSpaces();
+
+ Node.NamedAttribute namedAttributeNode =
+ new Node.NamedAttribute( attrs, start, parent );
+
+ if( reader.matches( "/>" ) ) {
+ // Body was empty. This is illegal, according to the grammar.
+ err.jspError(reader.mark(), "jsp.error.empty.body.not.allowed",
+ "<jsp:attribute" );
+ }
+ else if( !reader.matches( ">" ) ) {
+ err.jspError(reader.mark(), "jsp.error.unterminated",
+ "<jsp:attribute");
+ }
+
+ if( reader.matches( "</jsp:attribute" ) ) {
+ // Body was empty. This is illegal, according to the grammar.
+ err.jspError(reader.mark(), "jsp.error.empty.body.not.allowed",
+ "<jsp:attribute" );
+ }
+ else {
+ if( namedAttributeNode.isTrim() ) {
+ reader.skipSpaces();
+ }
+ parseBody( namedAttributeNode, "jsp:attribute",
+ TagInfo.BODY_CONTENT_SCRIPTLESS );
+ if( namedAttributeNode.isTrim() ) {
+ Node.Nodes subelements = namedAttributeNode.getBody();
+ Node lastNode = subelements.getNode(
+ subelements.size() - 1 );
+ if( lastNode instanceof Node.TemplateText ) {
+ ((Node.TemplateText)lastNode).rtrim();
+ }
+ }
+ }
+ reader.skipSpaces();
+ } while( reader.matches( "<jsp:attribute" ) );
}
-
}
1.2 +5 -3 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/TagConstants.java
Index: TagConstants.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/TagConstants.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- TagConstants.java 28 Mar 2002 18:46:16 -0000 1.1
+++ TagConstants.java 16 Jul 2002 19:30:51 -0000 1.2
@@ -87,4 +87,6 @@
public static final String JSP_TEXT_TAG = "jsp:text";
public static final String JSP_TEXT_TAG_START = "<jsp:text>";
public static final String JSP_TEXT_TAG_END = "</jsp:text>";
+ public static final String JSP_ATTRIBUTE_TAG = "jsp:attribute";
+ public static final String JSP_BODY_TAG = "jsp:body";
}
1.3 +142 -23 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/TagLibraryInfoImpl.java
Index: TagLibraryInfoImpl.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/TagLibraryInfoImpl.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- TagLibraryInfoImpl.java 16 May 2002 20:59:21 -0000 1.2
+++ TagLibraryInfoImpl.java 16 Jul 2002 19:30:51 -0000 1.3
@@ -78,6 +78,7 @@
* @author Anil K. Vijendran
* @author Mandar Raje
* @author Pierre Delisle
+ * @author Kin-man Chung
*/
public class TagLibraryInfoImpl extends TagLibraryInfo {
static private final String TAGLIB_TLD = "META-INF/taglib.tld";
@@ -110,6 +111,9 @@
for(int i = 0; i < tags.length; i++)
out.println(tags[i].toString());
+ for(int i = 0; i < functions.length; i++)
+ out.println(functions[i].toString());
+
return sw.toString();
}
@@ -240,6 +244,7 @@
throws JasperException
{
Vector tagVector = new Vector();
+ Vector functionVector = new Vector();
// Create an iterator over the child elements of our <taglib> element
ClassLoader cl = ctxt.getClassLoader();
@@ -271,6 +276,8 @@
this.tagLibraryValidator = createValidator(element);
else if ("tag".equals(tname))
tagVector.addElement(createTagInfo(element));
+ else if ("function".equals(tname)) // JSP2.0
+ functionVector.addElement(createFunctionInfo(element));
else if ("display-name".equals(tname) || // Ignored elements
"small-icon".equals(tname) ||
"large-icon".equals(tname) ||
@@ -287,6 +294,9 @@
this.tags = new TagInfo[tagVector.size()];
tagVector.copyInto (this.tags);
+
+ this.functions = new FunctionInfo[functionVector.size()];
+ functionVector.copyInto (this.functions);
}
private TagInfo createTagInfo(TreeNode elem) throws JasperException {
@@ -298,42 +308,48 @@
String displayName = null;
String smallIcon = null;
String largeIcon = null;
+ boolean dynamicAttributes = false;
Vector attributeVector = new Vector();
Vector variableVector = new Vector();
+ Vector fragmentAttributeVector = new Vector();
Iterator list = elem.findChildren();
while (list.hasNext()) {
TreeNode element = (TreeNode) list.next();
String tname = element.getName();
- if ("name".equals(tname))
+ if ("name".equals(tname)) {
name = element.getBody();
- else if ("tagclass".equals(tname) ||
- "tag-class".equals(tname))
+ } else if ("tagclass".equals(tname) ||
+ "tag-class".equals(tname)) {
tagclass = element.getBody();
- else if ("teiclass".equals(tname) ||
- "tei-class".equals(tname))
+ } else if ("teiclass".equals(tname) ||
+ "tei-class".equals(tname)) {
teiclass = element.getBody();
- else if ("bodycontent".equals(tname) ||
- "body-content".equals(tname))
+ } else if ("bodycontent".equals(tname) ||
+ "body-content".equals(tname)) {
bodycontent = element.getBody();
- else if ("display-name".equals(tname))
+ } else if ("display-name".equals(tname)) {
displayName = element.getBody();
- else if ("small-icon".equals(tname))
+ } else if ("small-icon".equals(tname)) {
smallIcon = element.getBody();
- else if ("large-icon".equals(tname))
+ } else if ("large-icon".equals(tname)) {
largeIcon = element.getBody();
- else if ("info".equals(tname) ||
- "description".equals(tname))
+ } else if ("info".equals(tname) ||
+ "description".equals(tname)) {
info = element.getBody();
- else if ("variable".equals(tname)) {
+ } else if ("variable".equals(tname)) {
variableVector.addElement(createVariable(element));
- } else if ("attribute".equals(tname))
+ } else if ("attribute".equals(tname)) {
attributeVector.addElement(createAttribute(element));
- else if ("example".equals(tname) || // Ignored elements
- false)
- ;
- else {
+ } else if ("fragment-attribute".equals(tname)) {
+ fragmentAttributeVector.addElement(
+ createFragmentAttribute(element));
+ } else if ("dynamic-attributes".equals(tname)) {
+ dynamicAttributes = JspUtil.booleanValue(element.getBody());
+ } else if ("example".equals(tname)) {
+ // Ignored elements
+ } else {
Constants.message("jsp.warning.unknown.element.in.tag",
new Object[] {tname},
Logger.WARNING
@@ -342,12 +358,15 @@
}
TagAttributeInfo[] tagAttributeInfo
= new TagAttributeInfo[attributeVector.size()];
- attributeVector.copyInto (tagAttributeInfo);
+ attributeVector.copyInto(tagAttributeInfo);
TagVariableInfo[] tagVariableInfos
= new TagVariableInfo[variableVector.size()];
variableVector.copyInto(tagVariableInfos);
+ TagFragmentAttributeInfo [] fragmentAttributes
+ = new TagFragmentAttributeInfo[fragmentAttributeVector.size()];
+ fragmentAttributeVector.copyInto(fragmentAttributes);
TagExtraInfo tei = null;
@@ -393,7 +412,9 @@
displayName,
smallIcon,
largeIcon,
- tagVariableInfos);
+ tagVariableInfos,
+ fragmentAttributes,
+ dynamicAttributes);
return taginfo;
}
@@ -436,6 +457,70 @@
return new TagAttributeInfo(name, required, type, rtexprvalue);
}
+ TagFragmentAttributeInfo createFragmentAttribute(TreeNode elem) {
+
+ Vector fragmentInputVector = new Vector();
+ String name = null;
+ String description = null;
+ boolean required = false;
+
+ Iterator list = elem.findChildren();
+ while (list.hasNext()) {
+ TreeNode element = (TreeNode) list.next();
+ String tname = element.getName();
+
+ if ("name".equals(tname)) {
+ name = element.getBody();
+ } else if ("required".equals(tname)) {
+ required = JspUtil.booleanValue(element.getBody());
+ } else if ("fragment-input".equals(tname)) {
+ fragmentInputVector.addElement(createFragmentInput(element));
+ } else if ("description".equals(tname)) {
+ description = element.getBody();
+ } else {
+ Constants.message("jsp.warning.unknown.element.in.attribute",
+ new Object[] {tname},
+ Logger.WARNING
+ );
+ }
+ }
+
+ TagFragmentAttributeInfo.FragmentInput[] fragmentInputs =
+ new TagFragmentAttributeInfo.FragmentInput[fragmentInputVector.size()];
+ fragmentInputVector.copyInto(fragmentInputs);
+
+ return new TagFragmentAttributeInfo(name, required, description,
+ fragmentInputs);
+ }
+
+ TagFragmentAttributeInfo.FragmentInput createFragmentInput(TreeNode elem) {
+
+ String name = null;
+ String type = null;
+ String description = null;
+
+ Iterator list = elem.findChildren();
+ while (list.hasNext()) {
+ TreeNode element = (TreeNode) list.next();
+ String tname = element.getName();
+
+ if ("name".equals(tname)) {
+ name = element.getBody();
+ } else if ("type".equals(tname)) {
+ type = element.getBody();
+ } else if ("description".equals(tname)) {
+ description = element.getBody();
+ } else {
+ Constants.message("jsp.warning.unknown.element.in.attribute",
+ new Object[] {tname},
+ Logger.WARNING
+ );
+ }
+ }
+ return new TagFragmentAttributeInfo.FragmentInput(name, type,
+ description);
+ }
+
TagVariableInfo createVariable(TreeNode elem) {
String nameGiven = null;
String nameFromAttribute = null;
@@ -547,6 +632,39 @@
return initParam;
}
+ FunctionInfo createFunctionInfo(TreeNode elem) {
+
+ String name = null;
+ String klass = null;
+ String signature = null;
+
+ Iterator list = elem.findChildren();
+ while (list.hasNext()) {
+ TreeNode element = (TreeNode) list.next();
+ String tname = element.getName();
+
+ if ("name".equals(tname)) {
+ name = element.getBody();
+ } else if ("function-class".equals(tname)) {
+ klass = element.getBody();
+ } else if ("function-signature".equals(tname)) {
+ signature = element.getBody();
+ } else if ("display-name".equals(tname) || // Ignored elements
+ "small-icon".equals(tname) ||
+ "large-icon".equals(tname) ||
+ "description".equals(tname) ||
+ "example".equals(tname)) {
+ } else {
+ Constants.message("jsp.warning.unknown.element.in.function",
+ new Object[] {tname},
+ Logger.WARNING);
+ }
+ }
+
+ return new FunctionInfo(name, klass, signature);
+ }
+
+/*
static void copy(InputStream in, String fileName)
throws IOException, FileNotFoundException
{
@@ -557,6 +675,7 @@
while ((nRead = in.read(buf, 0, buf.length)) != -1)
out.write(buf, 0, nRead);
}
+*/
//*********************************************************************
// Until javax.servlet.jsp.tagext.TagLibraryInfo is fixed
1.12 +229 -69 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Validator.java
Index: Validator.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Validator.java,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- Validator.java 8 Jul 2002 17:28:58 -0000 1.11
+++ Validator.java 16 Jul 2002 19:30:51 -0000 1.12
@@ -62,14 +62,19 @@
import java.util.Hashtable;
import java.util.Enumeration;
+
import javax.servlet.jsp.tagext.PageData;
import javax.servlet.jsp.tagext.TagData;
import javax.servlet.jsp.tagext.TagInfo;
import javax.servlet.jsp.tagext.TagAttributeInfo;
+import javax.servlet.jsp.tagext.TagFragmentAttributeInfo;
import javax.servlet.jsp.tagext.TagLibraryInfo;
import javax.servlet.jsp.tagext.TagLibraryInfo;
import javax.servlet.jsp.tagext.ValidationMessage;
+
+import org.apache.jasper.Constants;
import org.apache.jasper.JasperException;
+
import org.xml.sax.Attributes;
/**
@@ -80,6 +85,8 @@
*
* @author Kin-man Chung
* @author Jan Luehe
+ * @author Shawn Bayern
+ * @author Mark Roth
*/
public class Validator {
@@ -103,7 +110,8 @@
new JspUtil.ValidAttribute("errorPage"),
new JspUtil.ValidAttribute("isErrorPage"),
new JspUtil.ValidAttribute("contentType"),
- new JspUtil.ValidAttribute("pageEncoding") };
+ new JspUtil.ValidAttribute("pageEncoding"),
+ new JspUtil.ValidAttribute("isScriptingEnabled") };
private boolean languageSeen = false;
private boolean extendsSeen = false;
@@ -127,8 +135,8 @@
public void visit(Node.PageDirective n) throws JasperException {
- JspUtil.checkAttributes("Page directive", n.getAttributes(),
- pageDirectiveAttrs, n.getStart(), err);
+ JspUtil.checkAttributes("Page directive", n,
+ pageDirectiveAttrs, err);
// JSP.2.10.1
Attributes attrs = n.getAttributes();
@@ -210,6 +218,14 @@
pageInfo.setThreadSafe(false);
else
err.jspError(n, "jsp.error.isThreadSafe.invalid");
+ } else if ("isScriptingEnabled".equals(attr)) {
+ // XXX Test for multiple occurrence?
+ if ("true".equalsIgnoreCase(value))
+ pageInfo.setScriptingEnabled(true);
+ else if ("false".equalsIgnoreCase(value))
+ pageInfo.setScriptingEnabled(false);
+ else
+ err.jspError(n, "jsp.error.isScriptingEnabled.invalid");
} else if ("isErrorPage".equals(attr)) {
if (isErrorPageSeen)
err.jspError(n, "jsp.error.page.multiple.iserrorpage");
@@ -266,15 +282,15 @@
new JspUtil.ValidAttribute("prefix", true) };
private static final JspUtil.ValidAttribute[] includeActionAttrs = {
- new JspUtil.ValidAttribute("page", true),
+ new JspUtil.ValidAttribute("page", true, true),
new JspUtil.ValidAttribute("flush") };
private static final JspUtil.ValidAttribute[] paramActionAttrs = {
new JspUtil.ValidAttribute("name", true),
- new JspUtil.ValidAttribute("value", true) };
+ new JspUtil.ValidAttribute("value", true, true) };
private static final JspUtil.ValidAttribute[] forwardActionAttrs = {
- new JspUtil.ValidAttribute("page", true) };
+ new JspUtil.ValidAttribute("page", true, true) };
private static final JspUtil.ValidAttribute[] getPropertyAttrs = {
new JspUtil.ValidAttribute("name", true),
@@ -283,7 +299,7 @@
private static final JspUtil.ValidAttribute[] setPropertyAttrs = {
new JspUtil.ValidAttribute("name", true),
new JspUtil.ValidAttribute("property", true),
- new JspUtil.ValidAttribute("value"),
+ new JspUtil.ValidAttribute("value", false, true),
new JspUtil.ValidAttribute("param") };
private static final JspUtil.ValidAttribute[] useBeanAttrs = {
@@ -291,7 +307,7 @@
new JspUtil.ValidAttribute("scope"),
new JspUtil.ValidAttribute("class"),
new JspUtil.ValidAttribute("type"),
- new JspUtil.ValidAttribute("beanName") };
+ new JspUtil.ValidAttribute("beanName", false, true) };
private static final JspUtil.ValidAttribute[] plugInAttrs = {
new JspUtil.ValidAttribute("type",true),
@@ -299,14 +315,21 @@
new JspUtil.ValidAttribute("codebase"),
new JspUtil.ValidAttribute("align"),
new JspUtil.ValidAttribute("archive"),
- new JspUtil.ValidAttribute("height"),
+ new JspUtil.ValidAttribute("height", false, true),
new JspUtil.ValidAttribute("hspace"),
new JspUtil.ValidAttribute("jreversion"),
new JspUtil.ValidAttribute("name"),
new JspUtil.ValidAttribute("vspace"),
- new JspUtil.ValidAttribute("width"),
+ new JspUtil.ValidAttribute("width", false, true),
new JspUtil.ValidAttribute("nspluginurl"),
new JspUtil.ValidAttribute("iepluginurl") };
+
+ private static final JspUtil.ValidAttribute[] attributeAttrs = {
+ new JspUtil.ValidAttribute("name", true),
+ new JspUtil.ValidAttribute("trim") };
+
+ private static final JspUtil.ValidAttribute[] bodyAttrs = {
+ new JspUtil.ValidAttribute("value") };
/*
* Constructor
@@ -317,71 +340,75 @@
}
public void visit(Node.JspRoot n) throws JasperException {
- JspUtil.checkAttributes("Jsp:root", n.getAttributes(),
- jspRootAttrs, n.getStart(), err);
+ JspUtil.checkAttributes("Jsp:root", n,
+ jspRootAttrs, err);
visitBody(n);
}
public void visit(Node.IncludeDirective n) throws JasperException {
- JspUtil.checkAttributes("Include directive", n.getAttributes(),
- includeDirectiveAttrs, n.getStart(), err);
+ JspUtil.checkAttributes("Include directive", n,
+ includeDirectiveAttrs, err);
visitBody(n);
}
public void visit(Node.TaglibDirective n) throws JasperException {
- JspUtil.checkAttributes("Taglib directive", n.getAttributes(),
- taglibDirectiveAttrs, n.getStart(), err);
+ JspUtil.checkAttributes("Taglib directive", n,
+ taglibDirectiveAttrs, err);
}
public void visit(Node.ParamAction n) throws JasperException {
- JspUtil.checkAttributes("Param action", n.getAttributes(),
- paramActionAttrs, n.getStart(), err);
+ JspUtil.checkAttributes("Param action", n,
+ paramActionAttrs, err);
n.setValue(getJspAttribute("value", n.getAttributeValue("value"),
- n.isXmlSyntax()));
+ n));
+ visitBody(n);
}
public void visit(Node.IncludeAction n) throws JasperException {
- JspUtil.checkAttributes("Include action", n.getAttributes(),
- includeActionAttrs, n.getStart(), err);
- n.setPage(getJspAttribute("page", n.getAttributeValue("page"),
- n.isXmlSyntax()));
+ JspUtil.checkAttributes("Include action", n,
+ includeActionAttrs, err);
+ n.setPage(getJspAttribute("page", n.getAttributeValue("page"), n));
visitBody(n);
};
public void visit(Node.ForwardAction n) throws JasperException {
- JspUtil.checkAttributes("Forward", n.getAttributes(),
- forwardActionAttrs, n.getStart(), err);
- n.setPage(getJspAttribute("page", n.getAttributeValue("page"),
- n.isXmlSyntax()));
+ JspUtil.checkAttributes("Forward", n,
+ forwardActionAttrs, err);
+ n.setPage(getJspAttribute("page", n.getAttributeValue("page"), n));
visitBody(n);
}
public void visit(Node.GetProperty n) throws JasperException {
- JspUtil.checkAttributes("GetProperty", n.getAttributes(),
- getPropertyAttrs, n.getStart(), err);
+ JspUtil.checkAttributes("GetProperty", n,
+ getPropertyAttrs, err);
}
public void visit(Node.SetProperty n) throws JasperException {
- JspUtil.checkAttributes("SetProperty", n.getAttributes(),
- setPropertyAttrs, n.getStart(), err);
+ JspUtil.checkAttributes("SetProperty", n,
+ setPropertyAttrs, err);
String name = n.getAttributeValue("name");
String property = n.getAttributeValue("property");
String param = n.getAttributeValue("param");
String value = n.getAttributeValue("value");
+ n.setValue(getJspAttribute("value", value, n));
+
+ boolean valueSpecified = n.getValue() != null;
+
if ("*".equals(property)) {
- if (param != null || value != null)
+ if (param != null || valueSpecified)
err.jspError(n, "jsp.error.setProperty.invalid");
- } else if (param != null && value != null) {
+ } else if (param != null && valueSpecified) {
err.jspError(n, "jsp.error.setProperty.invalid");
}
- n.setValue(getJspAttribute("value", value, n.isXmlSyntax()));
+
+ visitBody(n);
}
public void visit(Node.UseBean n) throws JasperException {
- JspUtil.checkAttributes("UseBean", n.getAttributes(),
- useBeanAttrs, n.getStart(), err);
+ JspUtil.checkAttributes("UseBean", n,
+ useBeanAttrs, err);
String name = n.getAttributeValue ("id");
String scope = n.getAttributeValue ("scope");
@@ -400,7 +427,7 @@
Node.JspAttribute jattr
= getJspAttribute("beanName", n.getAttributeValue("beanName"),
- n.isXmlSyntax());
+ n);
n.setBeanName(jattr);
if (className != null && jattr != null)
err.jspError(n, "jsp.error.useBean.notBoth");
@@ -423,8 +450,7 @@
}
public void visit(Node.PlugIn n) throws JasperException {
- JspUtil.checkAttributes("Plugin", n.getAttributes(),
- plugInAttrs, n.getStart(), err);
+ JspUtil.checkAttributes("Plugin", n, plugInAttrs, err);
String type = n.getAttributeValue("type");
if (type == null)
@@ -433,14 +459,61 @@
err.jspError(n, "jsp.error.plugin.badtype");
if (n.getAttributeValue("code") == null)
err.jspError(n, "jsp.error.plugin.nocode");
+
+ Node.JspAttribute width = getJspAttribute("width",
+ n.getAttributeValue("width"), n);
+ n.setWidth( width );
+
+ Node.JspAttribute height = getJspAttribute("height",
+ n.getAttributeValue("height"), n);
+ n.setHeight( height );
n.setHeight(getJspAttribute("height", n.getAttributeValue("height"),
- n.isXmlSyntax()));
+ n));
n.setWidth(getJspAttribute("width", n.getAttributeValue("width"),
- n.isXmlSyntax()));
+ n));
visitBody(n);
}
+ public void visit(Node.NamedAttribute n) throws JasperException {
+ JspUtil.checkAttributes("Attribute", n,
+ attributeAttrs, err);
+ visitBody(n);
+ }
+
+ public void visit(Node.JspBody n) throws JasperException {
+ JspUtil.checkAttributes("Body", n,
+ bodyAttrs, err);
+ n.setValue(getJspAttribute("value", n.getAttributeValue("value"),
+ n));
+ visitBody(n);
+ }
+
+ public void visit(Node.Declaration n) throws JasperException {
+ if (! pageInfo.isScriptingEnabled()) {
+ err.jspError(n.getStart(), "jsp.error.no.scriptlets");
+ }
+ }
+
+ public void visit(Node.Expression n) throws JasperException {
+ if (! pageInfo.isScriptingEnabled()) {
+ err.jspError(n.getStart(), "jsp.error.no.scriptlets");
+ }
+ }
+
+ public void visit(Node.Scriptlet n) throws JasperException {
+ if (! pageInfo.isScriptingEnabled()) {
+ err.jspError(n.getStart(), "jsp.error.no.scriptlets");
+ }
+ }
+
+ public void visit(Node.ELExpression n) throws JasperException {
+ if ( true /*isELEnabled*/ ) {
+ JspUtil.validateExpressions(n.getStart(),
+ "${" + new String(n.getText()) + "}", err);
+ }
+ }
+
public void visit(Node.CustomTag n) throws JasperException {
TagLibraryInfo tagLibInfo = (TagLibraryInfo)
pageInfo.getTagLibraries().get(n.getPrefix());
@@ -450,13 +523,16 @@
}
/*
- * Make sure all required attributes are present
+ * Make sure all required attributes are present, either as
+ * attributes or named attributes (<jsp:attribute>).
*/
TagAttributeInfo[] tldAttrs = tagInfo.getAttributes();
Attributes attrs = n.getAttributes();
for (int i=0; i<tldAttrs.length; i++) {
- if (tldAttrs[i].isRequired()
- && attrs.getValue(tldAttrs[i].getName()) == null) {
+ if (tldAttrs[i].isRequired() &&
+ (attrs.getValue(tldAttrs[i].getName()) == null) &&
+ (n.getNamedAttributeNode(tldAttrs[i].getName()) == null) )
+ {
err.jspError(n, "jsp.error.missing_attribute",
tldAttrs[i].getName(), n.getShortName());
}
@@ -465,9 +541,11 @@
/*
* Make sure there are no invalid attributes
*/
+ Node.Nodes namedAttributeNodes = n.getNamedAttributeNodes();
Hashtable tagDataAttrs = new Hashtable(attrs.getLength());
Node.JspAttribute[] jspAttrs
- = new Node.JspAttribute[attrs.getLength()];
+ = new Node.JspAttribute[attrs.getLength() +
+ namedAttributeNodes.size()];
for (int i=0; i<attrs.getLength(); i++) {
boolean found = false;
for (int j=0; j<tldAttrs.length; j++) {
@@ -476,12 +554,12 @@
jspAttrs[i]
= getJspAttribute(attrs.getQName(i),
attrs.getValue(i),
- n.isXmlSyntax());
+ n);
} else {
jspAttrs[i]
= new Node.JspAttribute(attrs.getQName(i),
attrs.getValue(i),
- false);
+ false, false);
}
if (jspAttrs[i].isExpression()) {
tagDataAttrs.put(attrs.getQName(i),
@@ -499,6 +577,50 @@
attrs.getQName(i));
}
}
+
+ /*
+ * Make sure there are no invalid named attributes
+ */
+ TagFragmentAttributeInfo[] tfais = tagInfo.getFragmentAttributes();
+ for (int i=0; i<namedAttributeNodes.size(); i++) {
+ Node.NamedAttribute na =
+ (Node.NamedAttribute)namedAttributeNodes.getNode( i );
+ boolean found = false;
+ for (int j=0; j<tldAttrs.length; j++) {
+ if (na.getName().equals(tldAttrs[j].getName())) {
+ if (tldAttrs[j].canBeRequestTime()) {
+ jspAttrs[attrs.getLength() + i]
+ = getJspAttribute(na.getName(), null, n);
+ } else {
+ err.jspError( n,
+ "jsp.error.named.attribute.not.rt",
+ na.getName() );
+ }
+ tagDataAttrs.put(na.getName(),
+ TagData.REQUEST_TIME_VALUE);
+ found = true;
+ break;
+ }
+ }
+ if (!found && (tfais != null)) {
+ // check given named attribute against attributes of type
+ // JspFragment
+ for (int j=0; j<tfais.length; j++) {
+ if (na.getName().equals(tfais[j].getName())) {
+ jspAttrs[attrs.getLength() + i]
+ = getJspAttribute(na.getName(), null, n);
+ tagDataAttrs.put(na.getName(),
+ TagData.REQUEST_TIME_VALUE);
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ err.jspError(n, "jsp.error.bad_attribute",
+ na.getName());
+ }
+ }
TagData tagData = new TagData(tagDataAttrs);
n.setTagData(tagData);
@@ -510,33 +632,71 @@
/**
* Preprocess attributes that can be expressions. Expression
* delimiters are stripped.
+ * <p>
+ * If value is null, checks if there are any
+ * NamedAttribute subelements in the tree node, and if so,
+ * constructs a JspAttribute out of a child NamedAttribute node.
*/
private Node.JspAttribute getJspAttribute(String name,
String value,
- boolean isXml) {
+ Node n)
+ throws JasperException {
+
+ Node.JspAttribute result = null;
+
// XXX Is it an error to see "%=foo%" in non-Xml page?
// (We won't see "<%=foo%> in xml page because '<' is not a
// valid attribute value in xml).
- if (value == null)
- return null;
+ if (value != null) {
+ if (n.isXmlSyntax() && value.startsWith("%=")) {
+ result = new Node.JspAttribute(
+ name,
+ value.substring(2,
+ value.length()-1),
+ true, false);
+ }
+ else if(!n.isXmlSyntax() && value.startsWith("<%=")) {
+ result = new Node.JspAttribute(
+ name,
+ value.substring(3,
+ value.length()-2),
+ true, false);
+ }
+ else {
+ // The attribute can contain expressions but is not an
+ // rtexprvalue; thus, we want to run it through the
+ // expression interpreter (final argument "true" in
+ // Node.JspAttribute constructor).
+ // XXX Optimize by directing generator to pass expressions
+ // through interpreter only if they contain at least
+ // one "${"? (But ensure consistent type conversions
+ // in JSP 1.3!)
+
+ // validate expression syntax if string contains
+ // expression(s)
+ if (value.indexOf("${") != -1 /* && isELEnabled */) {
+ JspUtil.validateExpressions(n.getStart(), value, err);
+ result = new Node.JspAttribute(name, value, false, true);
+ } else {
+ result = new Node.JspAttribute(name, value, false, false);
+ }
+ }
+ }
+ else {
+ // Value is null. Check for any NamedAttribute subnodes
+ // that might contain the value for this attribute.
+ // Otherwise, the attribute wasn't found so we return null.
+
+ Node.NamedAttribute namedAttributeNode =
+ n.getNamedAttributeNode( name );
+ if( namedAttributeNode != null ) {
+ result = new Node.JspAttribute(name, namedAttributeNode);
+ }
+ }
- if (isXml && value.startsWith("%=")) {
- return new Node.JspAttribute(name,
- value.substring(2,
- value.length()-1),
- true);
- }
-
- if (!isXml && value.startsWith("<%=")) {
- return new Node.JspAttribute(name,
- value.substring(3,
- value.length()-2),
- true);
- }
-
- return new Node.JspAttribute(name, value, false);
- }
+ return result;
+ }
}
/**
1.1 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/SmapGenerator.java
Index: SmapGenerator.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.jasper.compiler;
import java.util.List;
import java.util.ArrayList;
/**
* Represents a source map (SMAP), which serves to associate lines
* of the input JSP file(s) to lines in the generated servlet in the
* final .class file, according to the JSR-045 spec.
*
* @author Shawn Bayern
*/
public class SmapGenerator {
//*********************************************************************
// Overview
/*
* The SMAP syntax is reasonably straightforward. The purpose of this
* class is currently twofold:
* - to provide a simple but low-level Java interface to build
* a logical SMAP
* - to serialize this logical SMAP for eventual inclusion directly
* into a .class file.
*/
//*********************************************************************
// Private state
private String outputFileName;
private String defaultStratum = "Java";
private List strata = new ArrayList();
private List embedded = new ArrayList();
private boolean doEmbedded = true;
//*********************************************************************
// Methods for adding mapping data
/**
* Sets the filename (without path information) for the generated
* source file. E.g., "foo$jsp.java".
*/
public synchronized void setOutputFileName(String x) {
outputFileName = x;
}
/**
* Adds the given SmapStratum object, representing a Stratum with
* logically associated FileSection and LineSection blocks, to
* the current SmapGenerator. If <tt>default</tt> is true, this
* stratum is made the default stratum, overriding any previously
* set default.
*
* @param stratum the SmapStratum object to add
* @param defaultStratum if <tt>true</tt>, this SmapStratum is considered
* to represent the default SMAP stratum unless
* overwritten
*/
public synchronized void addStratum(SmapStratum stratum,
boolean defaultStratum) {
strata.add(stratum);
if (defaultStratum)
this.defaultStratum = stratum.getStratumName();
}
/**
* Adds the given string as an embedded SMAP with the given stratum name.
*
* @param smap the SMAP to embed
* @param stratumName the name of the stratum output by the compilation
* that produced the <tt>smap</tt> to be embedded
*/
public synchronized void addSmap(String smap, String stratumName) {
embedded.add("*O " + stratumName + "\n"
+ smap
+ "*C " + stratumName + "\n");
}
/**
* Instructs the SmapGenerator whether to actually print any embedded
* SMAPs or not. Intended for situations without an SMAP resolver.
*
* @param status If <tt>false</tt>, ignore any embedded SMAPs.
*/
public void setDoEmbedded(boolean status) {
doEmbedded = status;
}
//*********************************************************************
// Methods for serializing the logical SMAP
public synchronized String getString() {
// check state and initialize buffer
if (outputFileName == null)
throw new IllegalStateException();
StringBuffer out = new StringBuffer();
// start the SMAP
out.append("SMAP\n");
out.append(outputFileName + '\n');
out.append(defaultStratum + '\n');
// include embedded SMAPs
if (doEmbedded) {
int nEmbedded = embedded.size();
for (int i = 0; i < nEmbedded; i++) {
out.append(embedded.get(i));
}
}
// print our StratumSections, FileSections, and LineSections
int nStrata = strata.size();
for (int i = 0; i < nStrata; i++) {
SmapStratum s = (SmapStratum) strata.get(i);
out.append(s.getString());
}
// end the SMAP
out.append("*E\n");
return out.toString();
}
public String toString() { return getString(); }
//*********************************************************************
// For testing (and as an example of use)...
public static void main(String args[]) {
SmapGenerator g = new SmapGenerator();
g.setOutputFileName("foo.java");
SmapStratum s = new SmapStratum("JSP");
s.addFile("foo.jsp");
s.addFile("bar.jsp", "/foo/foo/bar.jsp");
s.addLineData(1, "foo.jsp", 1, 1, 1);
s.addLineData(2, "foo.jsp", 1, 6, 1);
s.addLineData(3, "foo.jsp", 2, 10, 5);
s.addLineData(20, "bar.jsp", 1, 30, 1);
g.addStratum(s, true);
System.out.print(g);
System.out.println("---");
SmapGenerator embedded = new SmapGenerator();
embedded.setOutputFileName("blargh.tier2");
s = new SmapStratum("Tier2");
s.addFile("1.tier2");
s.addLineData(1, "1.tier2", 1, 1, 1);
embedded.addStratum(s, true);
g.addSmap(embedded.toString(), "JSP");
System.out.println(g);
}
}
1.1 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/SmapStratum.java
Index: SmapStratum.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.jasper.compiler;
import java.util.List;
import java.util.ArrayList;
/**
* Represents the line and file mappings associated with a JSR-045
* "stratum".
*
* @author Jayson Falkner
* @author Shawn Bayern
*/
public class SmapStratum {
//*********************************************************************
// Class for storing LineInfo data
/**
* Represents a single LineSection in an SMAP, associated with
* a particular stratum.
*/
public static class LineInfo {
private int inputStartLine = -1;
private int outputStartLine = -1;
private int lineFileID = 0;
private int inputLineCount = 1;
private int outputLineIncrement = 1;
private boolean lineFileIDSet = false;
/** Sets InputStartLine. */
public void setInputStartLine(int inputStartLine) {
if (inputStartLine < 0)
throw new IllegalArgumentException("" + inputStartLine);
this.inputStartLine = inputStartLine;
}
/** Sets OutputStartLine. */
public void setOutputStartLine(int outputStartLine) {
if (outputStartLine < 0)
throw new IllegalArgumentException("" + outputStartLine);
this.outputStartLine = outputStartLine;
}
/**
* Sets lineFileID. Should be called only when different from
* that of prior LineInfo object (in any given context) or 0
* if the current LineInfo has no (logical) predecessor.
* <tt>LineInfo</tt> will print this file number no matter what.
*/
public void setLineFileID(int lineFileID) {
if (lineFileID < 0)
throw new IllegalArgumentException("" + lineFileID);
this.lineFileID = lineFileID;
this.lineFileIDSet = true;
}
/** Sets InputLineCount. */
public void setInputLineCount(int inputLineCount) {
if (inputLineCount < 0)
throw new IllegalArgumentException("" + inputLineCount);
this.inputLineCount = inputLineCount;
}
/** Sets OutputLineIncrement. */
public void setOutputLineIncrement(int outputLineIncrement) {
if (outputLineIncrement < 0)
throw new IllegalArgumentException("" + outputLineIncrement);
this.outputLineIncrement = outputLineIncrement;
}
/**
* Retrieves the current LineInfo as a String, print all values
* only when appropriate (but LineInfoID if and only if it's been
* specified, as its necessity is sensitive to context).
*/
public String getString() {
if (inputStartLine == -1 || outputStartLine == -1)
throw new IllegalStateException();
StringBuffer out = new StringBuffer();
out.append(inputStartLine);
if (lineFileIDSet)
out.append("#" + lineFileID);
if (inputLineCount != 1)
out.append("," + inputLineCount);
out.append(":" + outputStartLine);
if (outputLineIncrement != 1)
out.append("," + outputLineIncrement);
out.append('\n');
return out.toString();
}
public String toString() { return getString(); }
}
//*********************************************************************
// Private state
private String stratumName;
private List fileNameList;
private List filePathList;
private List lineData;
private int lastFileID;
//*********************************************************************
// Constructor
/**
* Constructs a new SmapStratum object for the given stratum name
* (e.g., JSP).
*
* @param stratumName the name of the stratum (e.g., JSP)
*/
public SmapStratum(String stratumName) {
this.stratumName = stratumName;
fileNameList = new ArrayList();
filePathList = new ArrayList();
lineData = new ArrayList();
lastFileID = 0;
}
//*********************************************************************
// Methods to add mapping information
/**
* Adds record of a new file, by filename.
*
* @param fileName the filename to add, unqualified by path.
*/
public void addFile(String filename) {
addFile(filename, null);
}
/**
* Adds record of a new file, by filename and path. The path
* may be relative to a source compilation path.
*
* @param fileName the filename to add, unqualified by path
* @param filePath the path for the filename, potentially relative
* to a source compilation path
*/
public synchronized void addFile(String filename, String filePath) {
// fix this to check if duplicate name exists.
int fileIndex = fileNameList.indexOf(filename);
if (fileIndex == -1) {
fileNameList.add(filename);
filePathList.add(filePath);
}
}
/**
* Adds complete information about a simple line mapping. Specify
* all the fields in this method; the back-end machinery takes care
* of printing only those that are necessary in the final SMAP.
* (My view is that fields are optional primarily for spatial efficiency,
* not for programmer convenience. Could always add utility methods
* later.)
*
* @param inputStartLine starting line in the source file
* (SMAP <tt>InputStartLine</tt>)
* @param inputFileName the filepath (or name) from which the input comes
* (yields SMAP <tt>LineFileID</tt>) Use unqualified names
* carefully, and only when they uniquely identify a file.
* @param inputLineCount the number of lines in the input to map
* (SMAP <tt>LineFileCount</tt>)
* @param outputStartLine starting line in the output file
* (SMAP <tt>OutputStartLine</tt>)
* @param outputLineIncrement number of output lines to map to each
* input line (SMAP <tt>OutputLineIncrement</tt>). <i>Given the
* fact that the name starts with "output", I continuously have
* the subconscious urge to call this field
* <tt>OutputLineExcrement</tt>.</i>
*/
public synchronized void addLineData(int inputStartLine,
String inputFileName,
int inputLineCount,
int outputStartLine,
int outputLineIncrement) {
// check the input - what are you doing here??
// int fileIndex = filePathList.indexOf(inputFileName);
// if (fileIndex == -1)
// fileNameList.indexOf(inputFileName);
int fileIndex = fileNameList.indexOf(inputFileName);
if (fileIndex == -1) // still
throw new IllegalArgumentException(
"inputFileName: " + inputFileName);
// build the LineInfo
LineInfo li = new LineInfo();
li.setInputStartLine(inputStartLine);
li.setInputLineCount(inputLineCount);
li.setOutputStartLine(outputStartLine);
li.setOutputLineIncrement(outputLineIncrement);
if (fileIndex != lastFileID)
li.setLineFileID(fileIndex);
lastFileID = fileIndex;
// save it
lineData.add(li);
}
//*********************************************************************
// Methods to retrieve information
/**
* Returns the name of the stratum.
*/
public String getStratumName() {
return stratumName;
}
/**
* Returns the given stratum as a String: a StratumSection,
* followed by at least one FileSection and at least one LineSection.
*/
public synchronized String getString() {
// check state and initialize buffer
if (fileNameList.size() == 0 || lineData.size() == 0)
throw new IllegalStateException();
StringBuffer out = new StringBuffer();
// print StratumSection
out.append("*S " + stratumName + "\n");
// print FileSection
out.append("*F\n");
int bound = fileNameList.size();
for (int i = 0; i < bound; i++) {
if (filePathList.get(i) != null) {
out.append("+ " + i + " " + fileNameList.get(i) + "\n");
out.append(filePathList.get(i) + "\n");
} else {
out.append(i + " " + fileNameList.get(i) + "\n");
}
}
// print LineSection
out.append("*L\n");
bound = lineData.size();
for (int i = 0; i < bound; i++) {
LineInfo li = (LineInfo) lineData.get(i);
out.append(li.getString());
}
return out.toString();
}
public String toString() { return getString(); }
}
1.1 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/SmapUtil.java
Index: SmapUtil.java
===================================================================
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.jasper.compiler;
import java.io.*;
import java.util.*;
import org.apache.jasper.JspCompilationContext;
import org.apache.jasper.compiler.Node;
/**
* Contains static utilities for generating SMAP data based on the
* current version of Jasper.
*
* @author Jayson Falkner
* @author Shawn Bayern
* @author Robert Field (inner SDEInstaller class)
* @author Mark Roth
*/
public class SmapUtil {
//*********************************************************************
// Constants
public static final String SMAP_ENCODING = "UTF-8";
//*********************************************************************
// Public entry points
/**
* Generates an appropriate SMAP representing the current compilation
* context and optionally installs in the target .class file. (JSR-045.)
* If the keepGenerated flag is set in the compilation context, the
* generated .smap file will remain. Otherwise, it will be deleted.
*
* @param install True if the SourceDebugExtension is to be installed
* in the generated .class file, or false if not.
*/
public static void generateSmap(JspCompilationContext ctxt,
Node.Nodes pageNodes,
boolean install )
throws IOException
{
// set up our SMAP generator
SmapGenerator g = new SmapGenerator();
// determine if we have an input SMAP
String smapPath = inputSmapPath(ctxt.getRealPath(ctxt.getJspFile()));
File inputSmap = new File(smapPath);
if (inputSmap.exists()) {
byte[] embeddedSmap = null;
byte[] subSmap = SDEInstaller.readWhole(inputSmap);
String subSmapString = new String(subSmap, SMAP_ENCODING);
g.addSmap(subSmapString, "JSP");
}
// now, assemble info about our own stratum (JSP) using JspLineMap
SmapStratum s = new SmapStratum("JSP");
g.setOutputFileName(unqualify(ctxt.getServletJavaFileName()));
// recursively map out Node.Nodes, it seems ugly..but its the only way??
evaluateNodes(pageNodes, s);
g.addStratum(s, true);
/*
* Save the output to a temporary file (just so as to use
* Robert's interface -- so that I don't have to keep changing
* this code if he updates it). TODO: We could do this more
* gracefully later. But writing out the SMAP to a known
* filename (servlet.class.smap) will also make troubleshooting
* easier.
*/
File outSmap = new File(ctxt.getClassFileName() + ".smap");
PrintWriter so = new PrintWriter(
new OutputStreamWriter(new FileOutputStream(outSmap),
SMAP_ENCODING));
so.print(g.getString());
so.close();
}
public static void installSmap(JspCompilationContext ctxt) throws IOException {
File outSmap = new File(ctxt.getClassFileName() + ".smap");
File outServlet = new File(ctxt.getClassFileName());
SDEInstaller.install(outServlet, outSmap);
if( !ctxt.keepGenerated() ) {
outSmap.delete();
}
}
//*********************************************************************
// Private utilities
/**
* Returns an unqualified version of the given file path.
*/
private static String unqualify(String path) {
return path.substring(path.lastIndexOf("/") + 1);
}
/**
* Returns a file path corresponding to a potential SMAP input
* for the given compilation input (JSP file).
*/
private static String inputSmapPath(String path) {
return path.substring(0, path.lastIndexOf('.') + 1) + "smap";
}
//*********************************************************************
// Installation logic (from Robert Field, JSR-045 spec lead)
private static class SDEInstaller {
static final boolean verbose = true;
static final String nameSDE = "SourceDebugExtension";
byte[] orig;
byte[] sdeAttr;
byte[] gen;
int origPos = 0;
int genPos = 0;
int sdeIndex;
public static void main(String[] args) throws IOException {
if (args.length == 2) {
install(new File(args[0]), new File(args[1]));
} else if (args.length == 3) {
install(new File(args[0]), new File(args[1]), new File(args[2]));
} else {
abort("Usage: <command> <input class file> " +
"<attribute file> <output class file name>\n" +
"<command> <input/output class file> <attribute file>");
}
}
static void install(File inClassFile, File attrFile, File outClassFile)
throws IOException {
new SDEInstaller(inClassFile, attrFile, outClassFile);
}
static void install(File inOutClassFile, File attrFile) throws IOException {
File tmpFile = new File(inOutClassFile.getPath() + "tmp");
new SDEInstaller(inOutClassFile, attrFile, tmpFile);
if (!inOutClassFile.delete()) {
throw new IOException("inOutClassFile.delete() failed");
}
if (!tmpFile.renameTo(inOutClassFile)) {
throw new IOException("tmpFile.renameTo(inOutClassFile) failed");
}
}
static void abort(String msg) {
System.err.println(msg);
System.exit(1);
}
SDEInstaller(File inClassFile, File attrFile, File outClassFile) throws IOException {
if (!inClassFile.exists()) {
abort("no such file: " + inClassFile);
}
if (!attrFile.exists()) {
abort("no such file: " + attrFile);
}
// get the bytes
orig = readWhole(inClassFile);
sdeAttr = readWhole(attrFile);
gen = new byte[orig.length + sdeAttr.length + 100];
// do it
addSDE();
// write result
FileOutputStream outStream = new FileOutputStream(outClassFile);
outStream.write(gen, 0, genPos);
outStream.close();
}
static byte[] readWhole(File input) throws IOException {
FileInputStream inStream = new FileInputStream(input);
int len = (int)input.length();
byte[] bytes = new byte[len];
if (inStream.read(bytes, 0, len) != len) {
abort("expected size: " + len);
}
inStream.close();
return bytes;
}
void addSDE() throws UnsupportedEncodingException {
int i;
copy(4 + 2 + 2); // magic min/maj version
int constantPoolCountPos = genPos;
int constantPoolCount = readU2();
writeU2(constantPoolCount);
// copy old constant pool return index of SDE symbol, if found
sdeIndex = copyConstantPool(constantPoolCount);
if (sdeIndex < 0) {
// if "SourceDebugExtension" symbol not there add it
writeUtf8ForSDE();
// increment the countantPoolCount
sdeIndex = constantPoolCount;
++constantPoolCount;
randomAccessWriteU2(constantPoolCountPos, constantPoolCount);
if (verbose) {
System.out.println("SourceDebugExtension not found, installed at: " +
sdeIndex);
}
} else {
if (verbose) {
System.out.println("SourceDebugExtension found at: " +
sdeIndex);
}
}
copy(2 + 2 + 2); // access, this, super
int interfaceCount = readU2();
writeU2(interfaceCount);
if (verbose) {
System.out.println("interfaceCount: " + interfaceCount);
}
copy(interfaceCount * 2);
copyMembers(); // fields
copyMembers(); // methods
int attrCountPos = genPos;
int attrCount = readU2();
writeU2(attrCount);
if (verbose) {
System.out.println("class attrCount: " + attrCount);
}
// copy the class attributes, return true if SDE attr found (not copied)
if (!copyAttrs(attrCount)) {
// we will be adding SDE and it isn't already counted
++attrCount;
randomAccessWriteU2(attrCountPos, attrCount);
if (verbose) {
System.out.println("class attrCount incremented");
}
}
writeAttrForSDE(sdeIndex);
}
void copyMembers() {
int count = readU2();
writeU2(count);
if (verbose) {
System.out.println("members count: " + count);
}
for (int i = 0; i < count; ++i) {
copy(6); // access, name, descriptor
int attrCount = readU2();
writeU2(attrCount);
if (verbose) {
System.out.println("member attr count: " + attrCount);
}
copyAttrs(attrCount);
}
}
boolean copyAttrs(int attrCount) {
boolean sdeFound = false;
for (int i = 0; i < attrCount; ++i) {
int nameIndex = readU2();
// don't write old SDE
if (nameIndex == sdeIndex) {
sdeFound = true;
if (verbose) {
System.out.println("SDE attr found");
}
} else {
writeU2(nameIndex); // name
int len = readU4();
writeU4(len);
copy(len);
if (verbose) {
System.out.println("attr len: " + len);
}
}
}
return sdeFound;
}
void writeAttrForSDE(int index) {
writeU2(index);
writeU4(sdeAttr.length);
for (int i = 0; i < sdeAttr.length; ++i) {
writeU1(sdeAttr[i]);
}
}
void randomAccessWriteU2(int pos, int val) {
int savePos = genPos;
genPos = pos;
writeU2(val);
genPos = savePos;
}
int readU1() {
return ((int)orig[origPos++]) & 0xFF;
}
int readU2() {
int res = readU1();
return (res << 8) + readU1();
}
int readU4() {
int res = readU2();
return (res << 16) + readU2();
}
void writeU1(int val) {
gen[genPos++] = (byte)val;
}
void writeU2(int val) {
writeU1(val >> 8);
writeU1(val & 0xFF);
}
void writeU4(int val) {
writeU2(val >> 16);
writeU2(val & 0xFFFF);
}
void copy(int count) {
for (int i = 0; i < count; ++i) {
gen[genPos++] = orig[origPos++];
}
}
byte[] readBytes(int count) {
byte[] bytes = new byte[count];
for (int i = 0; i < count; ++i) {
bytes[i] = orig[origPos++];
}
return bytes;
}
void writeBytes(byte[] bytes) {
for (int i = 0; i < bytes.length; ++i) {
gen[genPos++] = bytes[i];
}
}
int copyConstantPool(int constantPoolCount) throws UnsupportedEncodingException {
int sdeIndex = -1;
// copy const pool index zero not in class file
for (int i = 1; i < constantPoolCount; ++i) {
int tag = readU1();
writeU1(tag);
switch (tag) {
case 7: // Class
case 8: // String
copy(2);
break;
case 9: // Field
case 10: // Method
case 11: // InterfaceMethod
case 3: // Integer
case 4: // Float
case 12: // NameAndType
copy(4);
break;
case 5: // Long
case 6: // Double
copy(8);
break;
case 1: // Utf8
int len = readU2();
writeU2(len);
byte[] utf8 = readBytes(len);
String str = new String(utf8, "UTF-8");
if (verbose) {
System.out.println(i + " read class attr -- '" + str + "'");
}
if (str.equals(nameSDE)) {
sdeIndex = i;
}
writeBytes(utf8);
break;
default:
abort("unexpected tag: " + tag);
break;
}
}
return sdeIndex;
}
void writeUtf8ForSDE() {
int len = nameSDE.length();
writeU1(1); // Utf8 tag
writeU2(len);
for (int i = 0; i < len; ++i) {
writeU1(nameSDE.charAt(i));
}
}
}
public static void evaluateNodes(Node.Nodes nodes, SmapStratum s) {
if( nodes != null && nodes.size()>0) {
int numChildNodes = nodes.size();
for( int i = 0; i < numChildNodes; i++ ) {
Node n = nodes.getNode( i );
Mark mark = n.getStart();
System.out.println("Mark(start): line="+ mark.getLineNumber() + " col="+mark.getColumnNumber() +"Node: begLine="+n.getBeginJavaLine() +" endLine="+n.getEndJavaLine());
String unqualifiedName = unqualify(mark.getFile());
s.addFile(unqualifiedName);
s.addLineData(mark.getLineNumber(),
unqualifiedName,
1,
n.getBeginJavaLine(),
n.getEndJavaLine() - n.getBeginJavaLine());
evaluateNodes(nodes.getNode(i).getBody(), s);
/*
int inputStartLine,
String inputFileName,
int inputLineCount,
int outputStartLine,
int outputLineIncrement
*/
}
}
}
}
1.10 +19 -2 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/resources/messages.properties
Index: messages.properties
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/resources/messages.properties,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -r1.9 -r1.10
--- messages.properties 8 Jul 2002 17:28:58 -0000 1.9
+++ messages.properties 16 Jul 2002 19:30:52 -0000 1.10
@@ -73,6 +73,7 @@
jsp.error.unable.rename=Unable to rename class file {0} to {1}
jsp.error.mandatory.attribute={0}: Mandatory attribute {1} missing
jsp.engine.info=Jasper JSP 1.1 Engine
+jsp.error.invalid.expression="{0}" contains invalid expression(s): {1}
jsp.error.invalid.attribute={0}: Invalid attribute, {1}
jsp.error.usebean.class.notfound=Class: {0} not found
jsp.error.file.cannot.read=Cannot read file: {0}
@@ -125,7 +126,6 @@
jsp.warning.unknown.element.in.TLD=Warning: Unknown element {0} in TLD
jsp.warning.unknown.element.in.tag=Warning: Unknown element {0} in tag
jsp.warning.unknown.element.in.attribute=Warning: Unknown element {0} in attribute
-jsp.error.xml.taglib.uri.not_prefixed=The relative URI \"{0}\" must be prefixed by \"urn:jsptld:\"
jsp.error.more.than.one.taglib=More than one taglib in the TLD: {0}
jsp.warning.teiclass.is.null=Could not load TagExtraInfo class {0}: {1}
jsp.warning.teiclass.is.nonnull=variable subelement defined in tld when TagExtraInfo class {0} is non-null
@@ -144,6 +144,7 @@
jsp.message.accepted=Accepted {0} at {1}
jsp.message.adding_jar=Adding jar {0} to my classpath
jsp.message.compiling_with=Compiling with: {0}
+jsp.message.template_text=template text
jsp.error.missing_attribute=According to the TLD attribute {0} is mandatory for tag {1}
jsp.error.bad_attribute=Attribute {0} invalid according to the specified TLD
jsp.error.tld_not_found=Could not locate TLD {0}
@@ -230,6 +231,7 @@
jsp.error.parse.xml.line=XML parsing error on file {0}: (line {1}, col {2}): {3}
jsp.error.internal.tldinit=Exception initializing TldLocationsCache: {0}
jsp.error.internal.filenotfound=Internal Error: File {0} not found
+jsp.error.internal.evaluator_not_found=Internal error: unable to load expression evaluator
jsp.error.parse.xml.invalidPublicId=Invalid PUBLIC ID: {0}
jsp.error.include.flush.invalid.value=Invalid value for the flush attribute: {0}
jsp.error.page.invalid.pageencoding=Page directive: invalid value for pageEncoding
@@ -250,3 +252,18 @@
jsp.error.single.line.number=\n\nAn error occurred at line: {0} in the jsp file: {1}\n\n
jsp.error.multiple.line.number=\n\nAn error occurred between lines: {0} and {1} in the jsp file: {2}\n\n
jsp.error.corresponding.servlet=Generated servlet error:\n
+
+jsp.error.empty.body.not.allowed=Empty body not allowed for {0}
+jsp.error.jspbody.body.not.allowed.with.value=Body not allowed for jsp:body if any attributes (such as value) are specified.
+
+jsp.error.named.attribute.not.rt=Cannot use jsp:attribute to specify value for non-request-time-attribute {0}.
+
+jsp.error.jspbody.required=Must use jsp:body to specify tag body for {0} if jsp:attribute is used.
+
+jsp.error.jspbody.emptybody.only=The {0} tag can only have jsp:attribute in its body.
+
+jsp.error.no.scriptlets=Scripting elements ( <%@, <%!, <%=, <% ) are disallowed here.
+
+jsp.error.internal.unexpected_node_type=Internal Error: Unexpected node type encountered
+
+jsp.error.tld.fn.invalid.signature=Invalid syntax for function signature in TLD. Tag Library: {0}, Function: {1}
1.2 +29 -3 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/runtime/JspRuntimeLibrary.java
Index: JspRuntimeLibrary.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/runtime/JspRuntimeLibrary.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- JspRuntimeLibrary.java 28 Mar 2002 18:46:19 -0000 1.1
+++ JspRuntimeLibrary.java 16 Jul 2002 19:30:52 -0000 1.2
@@ -94,6 +94,8 @@
import org.apache.jasper.JasperException;
import org.apache.jasper.Constants;
+// for JSTL expression interpreter
+import javax.servlet.jsp.PageContext;
/**
* Bunch of util methods that are used by code generated for useBean,
@@ -105,6 +107,7 @@
* works so well right now. It got forgotten at some point. -akv
*
* @author Mandar Raje
+ * @author Shawn Bayern
*/
public class JspRuntimeLibrary {
@@ -522,6 +525,29 @@
return value;
}
// __end lookupReadMethodMethod
+
+ // handles <jsp:setProperty> with EL expression for 'value' attribute
+ public static void handleSetPropertyExpression(Object bean,
+ String prop,
+ String expression,
+ PageContext pageContext,
+ java.util.Map fnMap )
+ throws JasperException
+ {
+ try {
+ Method method = getWriteMethod(bean.getClass(), prop);
+ method.invoke(bean, new Object[] {
+ ExpressionEvaluatorManager.evaluate(
+ expression,
+ method.getParameterTypes()[0],
+ pageContext,
+ fnMap,
+ "null" )
+ });
+ } catch (Exception ex) {
+ throw new JasperException(ex);
+ }
+ }
public static void handleSetProperty(Object bean, String prop,
Object value)
1.12 +87 -3 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/runtime/PageContextImpl.java
Index: PageContextImpl.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/runtime/PageContextImpl.java,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- PageContextImpl.java 20 Jun 2002 09:36:10 -0000 1.11
+++ PageContextImpl.java 16 Jul 2002 19:30:52 -0000 1.12
@@ -64,9 +64,11 @@
import java.io.IOException;
import java.io.InputStreamReader;
+import java.util.EmptyStackException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.NoSuchElementException;
+import java.util.Stack;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
@@ -85,6 +87,7 @@
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.el.ExpressionEvaluator;
import org.apache.jasper.Constants;
import org.apache.jasper.logging.Logger;
@@ -96,6 +99,7 @@
* @author Larry Cable
* @author Hans Bergsten
* @author Pierre Delisle
+ * @author Mark Roth
*/
public class PageContextImpl extends PageContext {
@@ -444,6 +448,7 @@
outs = newOuts;
}
out = outs[depth];
+ outs[depth].clearBody();
return outs[depth];
}
@@ -457,6 +462,82 @@
return out;
}
+ /**
+ * Pops the page scope from the stack. After calling this method, the
+ * PageScope will appear the same as it was before the last call to
+ * pushPageScope.
+ *
+ * @return A Map representing the state of the page scope just before
+ * it was popped. This object can be passed to pushPageScope to
+ * restore this state. The keys of the returned Map are Strings
+ * representing attribute names. The values are the values of
+ * those attributes.
+ */
+ public java.util.Map popPageScope()
+ throws EmptyStackException
+ {
+ if( this.attributesStack == null ) {
+ throw new EmptyStackException();
+ }
+ // Remember pop() may throw EmptyStackException.
+ this.attributes = (Hashtable)this.attributesStack.pop();
+ return this.attributes;
+ }
+
+ /**
+ * Pushes a new page scope on the stack.
+ *
+ * @param scopeState If null, a new, empty, page scope is pushed.
+ * Otherwise, the state of the page scope is restored to the
+ * contents of the provided Map.
+ */
+ public void pushPageScope( java.util.Map scopeState ) {
+ // Lazily create page scope stack
+ if( this.attributesStack == null ) {
+ this.attributesStack = new Stack();
+ }
+
+ // Push the old page scope on the stack:
+ this.attributesStack.push( this.attributes );
+
+ // Set the new page scope, depending on the input.
+ if( scopeState == null ) {
+ // Create a fresh page scope:
+ this.attributes = new Hashtable( 16 );
+ }
+ else if( scopeState instanceof Hashtable ) {
+ // Compatible Map.
+ this.attributes = (Hashtable)scopeState;
+ }
+ else {
+ // Incompatible Map. Only Maps returned by popPageScope()
+ // or peekPageScope() can be passed in to this method.
+ // Therefore, the scopeState MUST be an instance of Hashtable.
+ throw new IllegalArgumentException(
+ "Attempt to pass PageContext.pushPageScope() a Map " +
+ "that was not created by this container." );
+ }
+ }
+
+ public java.util.Map peekPageScope() {
+ return this.attributes;
+ }
+
+ /**
+ * Provides programmatic access to the ExpressionEvaluator.
+ * The JSP Container must return a valid instance of an
+ * ExpressionEvaluator that can parse EL expressions.
+ */
+ public ExpressionEvaluator getExpressionEvaluator() {
+ try {
+ return ExpressionEvaluatorManager.getEvaluatorByName(
+ ExpressionEvaluatorManager.EVALUATOR_CLASS );
+ }
+ catch( JspException e ) {
+ throw new RuntimeException( e );
+ }
+ }
+
public void handlePageException(Exception ex)
throws IOException, ServletException
{
@@ -529,6 +610,9 @@
protected transient Hashtable attributes = new Hashtable(16);
+ // Page scope attribute stack, to implement {push|pop|peek}PageScope:
+ // Lazily initialized.
+ protected transient Stack attributesStack = null;
// per request state
protected transient ServletRequest request;
1.1 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/runtime/ExpressionEvaluatorImpl.java
Index: ExpressionEvaluatorImpl.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.jasper.runtime;
import java.util.Map;
import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.el.ExpressionEvaluator;
import org.apache.taglibs.standard.lang.jstl.Evaluator;
/**
* <p>An adapter for the JSTL Expression Evaluator.</p>
*
* <p>Encapsulates and delegates to the JSTL evaluator, until the
* JSTL evaluator APIs are up to date with JSP 2.0.</p>
*
* @author Mark Roth
*/
public class ExpressionEvaluatorImpl
implements ExpressionEvaluator
{
private Evaluator delegate;
/**
* Create a new expression evaluator that delegates to the
* given evaluator.
*/
public ExpressionEvaluatorImpl() {
this.delegate = new Evaluator();
}
/**
* @see javax.servlet.jsp.el.ExpressionEvaluator#validate
*/
public String validate( String expression ) {
return delegate.validate( "", expression );
}
/**
* @see javax.servlet.jsp.el.ExpressionEvaluator#evaluate
*/
public Object evaluate( String expression,
Class expectedType,
JspContext jspContext,
Map elFunctions,
String defaultPrefix )
throws JspException
{
// XXX - Assume PageContext for now, until JSTL APIs are updated.
// change back to JspContext later.
return delegate.evaluate( "", expression, expectedType, null,
(PageContext)jspContext, elFunctions, defaultPrefix );
}
}
1.1 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/runtime/ExpressionEvaluatorManager.java
Index: ExpressionEvaluatorManager.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.jasper.runtime;
import java.util.*;
import javax.servlet.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.el.*;
import javax.servlet.jsp.tagext.*;
/**
* <p>A conduit to the JSTL EL.</p>
*
* <p>This is a stripped-down version of the expression evaluator manager
* found in the JSTL implementation.</p>
*
* @author Shawn Bayern
* @author Mark Roth
*/
public class ExpressionEvaluatorManager {
//*********************************************************************
// Constants
public static final String EVALUATOR_CLASS =
"org.apache.jasper.runtime.ExpressionEvaluatorImpl";
//*********************************************************************
// Internal, static state
private static HashMap nameMap = new HashMap();
//*********************************************************************
// Public static methods
/**
* Invokes the evaluate() method on the "active" ExpressionEvaluator
* for the given pageContext.
*/
public static Object evaluate( String expression,
Class expectedType,
JspContext jspContext,
Map elFunctions,
String defaultPrefix )
throws JspException
{
// the evaluator we'll use
ExpressionEvaluator target = getEvaluatorByName(EVALUATOR_CLASS);
// delegate the call
return (target.evaluate(
expression, expectedType, jspContext, elFunctions, defaultPrefix));
}
/**
* Gets an ExpressionEvaluator from the cache, or seeds the cache
* if we haven't seen a particular ExpressionEvaluator before.
*/
public static synchronized
ExpressionEvaluator getEvaluatorByName(String name)
throws JspException {
try {
Object oEvaluator = nameMap.get(name);
if (oEvaluator == null) {
ExpressionEvaluator e = (ExpressionEvaluator)
Class.forName(name).newInstance();
nameMap.put(name, e);
return (e);
} else
return ((ExpressionEvaluator) oEvaluator);
} catch (ClassCastException ex) {
// just to display a better error message
throw new JspException("invalid ExpressionEvaluator: " +
ex.getMessage(), ex);
} catch (ClassNotFoundException ex) {
throw new JspException("couldn't find ExpressionEvaluator: " +
ex.getMessage(), ex);
} catch (IllegalAccessException ex) {
throw new JspException("couldn't access ExpressionEvaluator: " +
ex.getMessage(), ex);
} catch (InstantiationException ex) {
throw new JspException(
"couldn't instantiate ExpressionEvaluator: " +
ex.getMessage(), ex);
}
}
}
1.1 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/runtime/JspFragmentHelper.java
Index: JspFragmentHelper.java
===================================================================
/*
* $Header: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/runtime/JspFragmentHelper.java,v 1.1 2002/07/16 19:30:52 kinman Exp $
* $Revision: 1.1 $
* $Date: 2002/07/16 19:30:52 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.jasper.runtime;
import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.JspFragment;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* Helper class from which all Jsp Fragment helper classes extend.
* This class allows for the emulation of numerous fragments within
* a single class, which in turn reduces the load on the class loader
* since there are potentially many JspFragments in a single page.
* <p>
* The class also provides various utility methods for JspFragment
* implementations.
*
* @author Mark Roth
*/
public abstract class JspFragmentHelper
implements JspFragment
{
protected int discriminator;
protected JspContext jspContext;
protected PageContext pageContext;
protected Object parentTag;
protected Map originalPageScope;
public JspFragmentHelper( int discriminator, JspContext jspContext,
Object parentTag )
{
this.discriminator = discriminator;
this.jspContext = jspContext;
this.pageContext = null;
if( jspContext instanceof PageContext ) {
pageContext = (PageContext)jspContext;
}
this.parentTag = parentTag;
this.originalPageScope = jspContext.peekPageScope();
}
public JspContext getJspContext() {
return this.jspContext;
}
public Object getParentTag() {
return this.parentTag;
}
/**
* Takes a snapshot of the current JspContext and stores
* the results in a Map for later restoration. Also sets the
* new values in the page context, given the provided parameters.
*
* @param params the parameters to set in the page scope
* @return A map that contains a snapshot of the old page scope.
*/
protected Map preparePageScope( Map params ) {
Map originalValues = new HashMap();
Iterator keys = params.keySet().iterator();
while( keys.hasNext() ) {
String key = (String)keys.next();
// Remember original values to restore later
originalValues.put( key, originalPageScope.get( key ) );
// Set new values, based on params
jspContext.setAttribute( key, params.get( key ) );
}
return originalValues;
}
/**
* Restores the state of the page scope in the current page context,
* from the given map.
*
* @param originalValues the values to restore in the page context.
*/
protected void restorePageScope( Map originalValues ) {
Iterator keys = originalValues.keySet().iterator();
while( keys.hasNext() ) {
String key = (String)keys.next();
Object value = originalValues.get( key );
if( value == null ) {
// Value to be cleared:
jspContext.removeAttribute( key );
}
else {
// Value to be restored:
jspContext.setAttribute( key, value );
}
}
}
}
1.4 +3 -2 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/xmlparser/ParserUtils.java
Index: ParserUtils.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/xmlparser/ParserUtils.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- ParserUtils.java 20 Jun 2002 22:31:23 -0000 1.3
+++ ParserUtils.java 16 Jul 2002 19:30:53 -0000 1.4
@@ -119,7 +119,8 @@
*/
static EntityResolver entityResolver = new MyEntityResolver();
- public static boolean validating=true;
+ // Turn off for JSP 2.0 until switch over to using xschema.
+ public static boolean validating=false;
// --------------------------------------------------------- Public Methods
--
To unsubscribe, e-mail: <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>