kinman 2003/03/19 12:51:35 Modified: jasper2/src/share/org/apache/jasper/compiler Compiler.java Generator.java JspUtil.java Node.java PageInfo.java Validator.java jasper2/src/share/org/apache/jasper/resources messages.properties Added: jasper2/src/share/org/apache/jasper/compiler ELFunctionMapper.java ELNode.java ELParser.java Removed: jasper2/src/share/org/apache/jasper/compiler FunctionMapperImpl.java jasper2/src/share/org/apache/jasper/resources jsp12.dtd Log: - Remove FunctionMapperImpl.java since parseExpression does not validate functions. - Implement a EL expression parser. It only parses functions now, but can be expanded to be a full parser, in the future. - Modify Generator to output a function mapper for each invokation of EL expression evaluation. The namespace for functions can now be reused, if the page is in XML syntax. Revision Changes Path 1.59 +4 -1 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.58 retrieving revision 1.59 diff -u -r1.58 -r1.59 --- Compiler.java 27 Feb 2003 22:51:38 -0000 1.58 +++ Compiler.java 19 Mar 2003 20:51:34 -0000 1.59 @@ -253,7 +253,7 @@ // Generate FunctionMapper (used for validation of EL expressions and // code generation) - pageInfo.setFunctionMapper(new FunctionMapperImpl(this)); + // pageInfo.setFunctionMapper(new FunctionMapperImpl(this)); // Validate and process attributes Validator.validate(this, pageNodes); @@ -278,6 +278,9 @@ // Optimizations by Tag Plugins TagPluginManager tagPluginManager = options.getTagPluginManager(); tagPluginManager.apply(pageNodes, errDispatcher, pageInfo); + + // Generate static funciton mapper codes. + ELFunctionMapper.map(this, pageNodes); // generate servlet .java file Generator.generate(writer, this, pageNodes); 1.174 +7 -74 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.173 retrieving revision 1.174 diff -u -r1.173 -r1.174 --- Generator.java 8 Mar 2003 00:58:09 -0000 1.173 +++ Generator.java 19 Mar 2003 20:51:34 -0000 1.174 @@ -435,10 +435,6 @@ out.printil("}"); out.println(); } - - // Static data for EL function and prefix maps: - generateELFunctionMap(); - generateFunctionMapper(); } /** @@ -613,70 +609,6 @@ } } - /** - * Generates EL Function map section - */ - private void generateELFunctionMap() - throws JasperException - { - FunctionMapperImpl fnMap = pageInfo.getFunctionMapper(); - - out.printil("private static org.apache.jasper.runtime.ProtectedFunctionMapper _jspx_fnmap;"); - if (!fnMap.isEmpty()) { - Iterator iter = fnMap.keySet().iterator(); - out.println(); - out.printil("static {"); - out.pushIndent(); - out.printil("_jspx_fnmap = org.apache.jasper.runtime.ProtectedFunctionMapper.getInstance();"); - while (iter.hasNext()) { - String key = (String) iter.next(); - out.printin("_jspx_fnmap.mapFunction("); - out.print(quote(key)); - out.print(", "); - out.print(fnMap.getFunctionClass(key) + ".class, "); - out.print(quote(fnMap.getMethodName(key))); - out.print(", "); - Class[] args = fnMap.getParameterTypes(key); - if (args != null) { - out.print("new Class[] {" ); - for( int j = 0; j < args.length; j++ ) { - out.print( args[j].getName() + ".class" ); - if( j < (args.length - 1) ) { - out.print( ", " ); - } - } - out.print("} "); - } else { - out.print("null"); - } - out.println(");"); - } - out.popIndent(); - out.printil("}"); - out.println(); - } - } - - /** - * Generates the method needed to implement FunctionMapper - */ - private void generateFunctionMapper() - throws JasperException - { -/* XX suppress until EL moves out of JSTL - out.printil( "public java.lang.reflect.Method resolveFunction(" ); - out.printil( " String prefix, String localName )" ); - out.printil( "{" ); - out.pushIndent(); - out.printil( "return (java.lang.reflect.Method)_jspx_fnmap.get( " ); - out.printil( " prefix + \":\" + localName );" ); - out.popIndent(); - out.printil( "}" ); - out.println(); -*/ - } - - /* * Generates the constructor. * (shared by servlet and tag handler preamble generation) @@ -784,7 +716,7 @@ boolean replaceESC = v.indexOf(Constants.ESC) > 0; v = JspUtil.interpreterCall(this.isTagFile, v, expectedType, defaultPrefix, - "_jspx_fnmap", false ); + attr.getEL().getMapName(), false ); // XXX ESC replacement hack if (replaceESC) { v = "(" + v + ").replace(" + Constants.ESCStr + ", '$')"; @@ -867,7 +799,7 @@ "out.write(" + JspUtil.interpreterCall(this.isTagFile, "${" + new String(n.getText()) + "}", String.class, - null, "_jspx_fnmap", false ) + null, n.getEL().getMapName(), false ) + ");"); } else { out.printil("out.write(" + @@ -2480,7 +2412,8 @@ // run attrValue through the expression interpreter boolean replaceESC = attrValue.indexOf(Constants.ESC) > 0; attrValue = JspUtil.interpreterCall(this.isTagFile, - attrValue, c[0], n.getPrefix(), "_jspx_fnmap", false ); + attrValue, c[0], n.getPrefix(), + attr.getEL().getMapName(), false ); // XXX hack: Replace ESC with '$' if (replaceESC) { attrValue = "(" + attrValue + ").replace(" + 1.34 +5 -10 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.33 retrieving revision 1.34 diff -u -r1.33 -r1.34 --- JspUtil.java 26 Feb 2003 00:11:38 -0000 1.33 +++ JspUtil.java 19 Mar 2003 20:51:34 -0000 1.34 @@ -60,8 +60,6 @@ */ package org.apache.jasper.compiler; -import java.net.URL; - import java.io.*; import java.util.Enumeration; import java.util.Hashtable; @@ -597,10 +595,10 @@ String defaultPrefix, ErrorDispatcher err) throws JasperException { - // Just parse and check if any exceptions are thrown. + try { JspUtil.expressionEvaluator.parseExpression( expressions, - expectedType, functionMapper, defaultPrefix ); + expectedType, null, defaultPrefix ); } catch( ELParseException e ) { err.jspError(where, "jsp.error.invalid.expression", expressions, @@ -918,7 +916,4 @@ return reader; } } - - - 1.66 +22 -8 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.65 retrieving revision 1.66 diff -u -r1.65 -r1.66 --- Node.java 1 Mar 2003 02:07:52 -0000 1.65 +++ Node.java 19 Mar 2003 20:51:34 -0000 1.66 @@ -784,6 +784,8 @@ */ public static class ELExpression extends Node { + private ELNode.Nodes el; + public ELExpression(String text, Mark start, Node parent) { super(null, null, text, start, parent); } @@ -791,6 +793,14 @@ public void accept(Visitor v) throws JasperException { v.visit(this); } + + public void setEL(ELNode.Nodes el) { + this.el = el; + } + + public ELNode.Nodes getEL() { + return el; + } } /** @@ -1766,8 +1776,8 @@ private String localName; private String value; private boolean expression; - private boolean el; private boolean dynamic; + private ELNode.Nodes el; // If true, this JspAttribute represents a <jsp:attribute> private boolean namedAttribute; @@ -1775,7 +1785,7 @@ private NamedAttribute namedAttributeNode; JspAttribute(String qName, String uri, String localName, String value, - boolean expr, boolean el, boolean dyn ) { + boolean expr, ELNode.Nodes el, boolean dyn ) { this.qName = qName; this.uri = uri; this.localName = localName; @@ -1798,7 +1808,7 @@ this.value = null; this.namedAttributeNode = na; this.expression = false; - this.el = false; + this.el = null; this.dynamic = dyn; this.namedAttribute = true; } @@ -1866,7 +1876,7 @@ * not be interpreted or reevaluated */ public boolean isELInterpreterInput() { - return el; + return el != null; } /** @@ -1874,7 +1884,7 @@ * time. */ public boolean isLiteral() { - return !expression && !el && !namedAttribute; + return !expression && (el != null) && !namedAttribute; } /** @@ -1882,6 +1892,10 @@ */ public boolean isDynamic() { return dynamic; + } + + public ELNode.Nodes getEL() { + return el; } } 1.25 +3 -12 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.24 retrieving revision 1.25 diff -u -r1.24 -r1.25 --- PageInfo.java 1 Mar 2003 02:07:52 -0000 1.24 +++ PageInfo.java 19 Mar 2003 20:51:35 -0000 1.25 @@ -78,7 +78,6 @@ private BeanRepository beanRepository; private Hashtable tagLibraries; private Hashtable prefixMapper; - private FunctionMapperImpl funcMap; private String language = "java"; private String xtends = Constants.JSP_SERVLET_BASE; @@ -385,13 +384,5 @@ public void setOmitXmlDecl(String omit) { omitXmlDecl = omit; - } - - public void setFunctionMapper(FunctionMapperImpl map) { - this.funcMap = map; - } - - public FunctionMapperImpl getFunctionMapper() { - return this.funcMap; } } 1.91 +89 -17 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.90 retrieving revision 1.91 diff -u -r1.90 -r1.91 --- Validator.java 4 Mar 2003 22:41:12 -0000 1.90 +++ Validator.java 19 Mar 2003 20:51:35 -0000 1.91 @@ -364,9 +364,6 @@ private ClassLoader loader; private Hashtable taglibs; - // A FunctionMapper, used to validate EL expressions. - private FunctionMapper functionMapper; - private static final JspUtil.ValidAttribute[] jspRootAttrs = { new JspUtil.ValidAttribute("version", true) }; @@ -448,7 +445,6 @@ this.err = compiler.getErrorDispatcher(); this.tagInfo = compiler.getCompilationContext().getTagInfo(); this.loader = compiler.getCompilationContext().getClassLoader(); - this.functionMapper = pageInfo.getFunctionMapper(); } public void visit(Node.JspRoot n) throws JasperException { @@ -653,15 +649,21 @@ } public void visit(Node.ELExpression n) throws JasperException { + // Currently parseExpression does not validate functions, so + // a null FunctionMapper is passed. if ( !pageInfo.isELIgnored() ) { + String expressions = "${" + new String(n.getText()) + "}"; JspUtil.validateExpressions( n.getStart(), - "${" + new String(n.getText()) + "}", + expressions, java.lang.String.class, // XXX - Should template text // always evaluate to String? - this.functionMapper, + null, null, err); + ELNode.Nodes el = ELParser.parse(expressions); + validateFunctions(el, n); + n.setEL(el); } } @@ -932,7 +934,7 @@ attrs.getLocalName(i), attrs.getValue(i), false, - false, + null, false); } if (jspAttrs[i].isExpression()) { @@ -1058,7 +1060,7 @@ localName, value.substring(2, value.length()-1), true, - false, + null, dynamic); } else if(!n.isXmlSyntax() && value.startsWith("<%=")) { @@ -1068,7 +1070,7 @@ localName, value.substring(3, value.length()-2), true, - false, + null, dynamic); } else { @@ -1079,22 +1081,25 @@ // validate expression syntax if string contains // expression(s) - if (value.indexOf("${") != -1 && !pageInfo.isELIgnored()) { + ELNode.Nodes el = ELParser.parse(value); + if (el.containsEL() && !pageInfo.isELIgnored()) { JspUtil.validateExpressions( n.getStart(), value, expectedType, - this.functionMapper, + null, defaultPrefix, this.err); + + validateFunctions(el, n); result = new Node.JspAttribute(qName, uri, localName, - value, false, true, + value, false, el, dynamic); } else { value = value.replace(Constants.ESC, '$'); result = new Node.JspAttribute(qName, uri, localName, - value, false, false, + value, false, null, dynamic); } } @@ -1160,7 +1165,74 @@ return hasDynamicContent; } } - } + + private String findUri(String prefix, Node n) { + + Node p = n; + while (p != null) { + if (p instanceof Node.CustomTag) { + Node.CustomTag ct = (Node.CustomTag) p; + if (prefix.equals(ct.getPrefix())) { + return (ct.getURI()); + } + } else if (p instanceof Node.JspRoot) { + // XXX find Uri from the root node + } + p = p.getParent(); + } + return null; + } + + /** + * Validate functions in EL expressions + */ + private void validateFunctions(ELNode.Nodes el, Node n) + throws JasperException { + + class FVVisitor extends ELNode.Visitor { + + Node n; + + FVVisitor(Node n) { + this.n = n; + } + + public void visit(ELNode.Function func) throws JasperException { + String defaultNS = null; // for now + String prefix = func.getPrefix(); + String function = func.getName(); + String uri = null; + if (prefix == null) { + // In XML syntax, use the default namespace + if (defaultNS == null) { + err.jspError(n, "jsp.error.noFuncionPrefix", + function); + } + uri = defaultNS; + } else if (n.isXmlSyntax()) { + uri = findUri(prefix, n); + } else { + Hashtable prefixMapper = pageInfo.getPrefixMapper(); + uri = (String) prefixMapper.get(prefix); + } + + TagLibraryInfo taglib = + (TagLibraryInfo) taglibs.get(uri); + FunctionInfo funcInfo = null; + if (taglib != null) { + funcInfo = taglib.getFunction(function); + } + if (funcInfo == null) { + err.jspError(n, "jsp.error.noFunction", function); + } + // Skip TLD function uniqueness check. Done by Schema ? + func.setFunctionInfo(funcInfo); + } + } + + el.visit(new FVVisitor(n)); + } + } // End of ValidateVisitor /** * A visitor for validating TagExtraInfo classes of all tags 1.1 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/ELFunctionMapper.java Index: ELFunctionMapper.java =================================================================== /* ========================================================================= * * * * The Apache Software License, Version 1.1 * * * * Copyright (c) 1999, 2000, 2001 The Apache Software Foundation. * * All rights reserved. * * * * ========================================================================= * * * * Redistribution and use in source and binary forms, with or without modi- * * fication, are permitted provided that the following conditions are met: * * * * 1. Redistributions of source code must retain the above copyright notice * * 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 <[EMAIL PROTECTED]>. * * * * 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 Software Foundation. * * * * 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 indivi- * * duals 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.*; import javax.servlet.jsp.tagext.FunctionInfo; import org.apache.jasper.JasperException; /** * This class generates a mapper for an EL expression * Instead of a global mapper, a mapper is used for ecah call to EL * evaluator, thus avoiding the prefix overlapping and redefinition * issues. */ public class ELFunctionMapper { static private int currFunc = 0; private ErrorDispatcher err; StringBuffer ds; StringBuffer ss; public static void map(Compiler compiler, Node.Nodes page) throws JasperException { currFunc = 0; ELFunctionMapper map = new ELFunctionMapper(); map.err = compiler.getErrorDispatcher(); map.ds = new StringBuffer(); map.ss = new StringBuffer(); map.ds.append("static {\n"); page.visit(map.new ELFunctionVisitor()); map.ds.append("}\n"); // Append the declarations to the root node Node root = page.getRoot(); new Node.Declaration(map.ss.toString(), root.getStart(), root); new Node.Declaration(map.ds.toString(), root.getStart(), root); } class ELFunctionVisitor extends Node.Visitor { public void visit(Node.ParamAction n) throws JasperException { doMap(n.getValue()); } public void visit(Node.IncludeAction n) throws JasperException { doMap(n.getPage()); } public void visit(Node.ForwardAction n) throws JasperException { doMap(n.getPage()); } public void visit(Node.SetProperty n) throws JasperException { doMap(n.getValue()); } public void visit(Node.UseBean n) throws JasperException { doMap(n.getBeanName()); } public void visit(Node.PlugIn n) throws JasperException { doMap(n.getHeight()); doMap(n.getWidth()); } public void visit(Node.JspElement n) throws JasperException { Node.JspAttribute[] attrs = n.getJspAttributes(); for (int i = 0; i < attrs.length; i++) { doMap(attrs[i]); } doMap(n.getNameAttribute()); } public void visit(Node.CustomTag n) throws JasperException { Node.JspAttribute[] attrs = n.getJspAttributes(); for (int i = 0; i < attrs.length; i++) { doMap(attrs[i]); } } public void visit(Node.ELExpression n) throws JasperException { doMap(n.getEL()); } private void doMap(Node.JspAttribute attr) throws JasperException { if (attr != null) { doMap(attr.getEL()); } } private void doMap(ELNode.Nodes el) throws JasperException { class Fvisitor extends ELNode.Visitor { ArrayList funcs = new ArrayList(); public void visit(ELNode.Function n) throws JasperException { funcs.add(n); } } if (el == null) { return; } // First locate all functions in this expression Fvisitor fv = new Fvisitor(); el.visit(fv); ArrayList functions = fv.funcs; // TODO Some optimization here: if the fmap has only one entry, // if it was generated before, use it. if (functions.size() == 0) { return; } // Generate declaration for the map statically String decName = getMapName(); ss.append("static private org.apache.jasper.runtime.ProtectedFunctionMapper " + decName + ";\n"); ds.append(" " + decName + " = org.apache.jasper.runtime.ProtectedFunctionMapper.getInstance();\n"); for (int i = 0; i < functions.size(); i++) { ELNode.Function f = (ELNode.Function)functions.get(i); FunctionInfo funcInfo = f.getFunctionInfo(); String key = f.getPrefix()+ ":" + f.getName(); ds.append(" " + decName + ".mapFunction(\"" + key + "\", " + funcInfo.getFunctionClass() + ".class, " + '\"' + getMethod(f) + "\", " + "new Class[] {" + getParameters(f) + "}" + ");\n"); } el.setMapName(decName); } private String getMapName() { return "_jspx_fnmap_" + currFunc++; } private String getMethod(ELNode.Function func) throws JasperException { FunctionInfo funcInfo = func.getFunctionInfo(); String signature = funcInfo.getFunctionSignature(); int start = signature.indexOf(' '); if (start < 0) { err.jspError("jsp.error.tld.fn.invalid.signature", func.getPrefix(), func.getName()); } int end = signature.indexOf('('); if (end < 0) { err.jspError("jsp.error.tld.fn.invalid.signature.parenexpected", func.getPrefix(), func.getName()); } return signature.substring(start+1, end).trim(); } private String getParameters(ELNode.Function func) throws JasperException { FunctionInfo funcInfo = func.getFunctionInfo(); StringBuffer buf = new StringBuffer(); String signature = funcInfo.getFunctionSignature(); // Signature is of the form // <return-type> S <method-name S? '(' // < <arg-type> ( ',' <arg-type> )* )? ')' int start = signature.indexOf('(') + 1; boolean lastArg = false; while (true) { int p = signature.indexOf(',', start); if (p < 0) { p = signature.indexOf(')', start); if (p < 0) { err.jspError("jsp.error.tld.fn.invalid.signature", func.getPrefix(), func.getName()); } lastArg = true; } String arg = signature.substring(start, p).trim(); buf.append(arg + ".class"); if (lastArg) { break; } buf.append(','); start = p+1; } return buf.toString(); } } } 1.1 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/ELNode.java Index: ELNode.java =================================================================== /* ========================================================================= * * * * The Apache Software License, Version 1.1 * * * * Copyright (c) 1999, 2000, 2001 The Apache Software Foundation. * * All rights reserved. * * * * ========================================================================= * * * * Redistribution and use in source and binary forms, with or without modi- * * fication, are permitted provided that the following conditions are met: * * * * 1. Redistributions of source code must retain the above copyright notice * * 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 <[EMAIL PROTECTED]>. * * * * 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 Software Foundation. * * * * 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 indivi- * * duals 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.*; import javax.servlet.jsp.tagext.FunctionInfo; import org.apache.jasper.JasperException; /** * This class defines internal representation for an EL Expression * * It currently only defines functions. It can be expanded to define * all the components of an EL expression, if need to. */ abstract class ELNode { abstract public void accept(Visitor v) throws JasperException; /** * Child classes */ /** * Represents an EL expression: anything in ${ and }. */ public static class Root extends ELNode { private ELNode.Nodes expr; Root(ELNode.Nodes expr) { this.expr = expr; } public void accept(Visitor v) throws JasperException { v.visit(this); } public ELNode.Nodes getExpression() { return expr; } } /** * Represents text outside of EL expression. */ public static class Text extends ELNode { private String text; Text(String text) { this.text = text; } public void accept(Visitor v) throws JasperException { v.visit(this); } public String getText() { return text; } } /** * Represents anything else EL expression, including function arguments etc */ public static class ELText extends ELNode { private String text; ELText(String text) { this.text = text; } public void accept(Visitor v) throws JasperException { v.visit(this); } public String getText() { return text; } } /** * Represents a function * Currently only the prefix and function name, but not its arguments. */ public static class Function extends ELNode { String prefix; String name; FunctionInfo functionInfo; Function(String prefix, String name) { this.prefix = prefix; this.name = name; } public void accept(Visitor v) throws JasperException { v.visit(this); } public String getPrefix() { return prefix; } public String getName() { return name; } public void setFunctionInfo(FunctionInfo f) { this.functionInfo = f; } public FunctionInfo getFunctionInfo() { return functionInfo; } } /** * An ordered list of ELNode. */ public static class Nodes { /* Name used for creating a map for the functions in this EL expression, for communication to Generator. */ String mapName = null; private List list; public Nodes() { list = new ArrayList(); } public void add(ELNode en) { list.add(en); } /** * Visit the nodes in the list with the supplied visitor * @param v The visitor used */ public void visit(Visitor v) throws JasperException { Iterator iter = list.iterator(); while (iter.hasNext()) { ELNode n = (ELNode) iter.next(); n.accept(v); } } public Iterator iterator() { return list.iterator(); } public boolean isEmpty() { return list.size() == 0; } /** * @return true if the expression contains a ${...} */ public boolean containsEL() { Iterator iter = list.iterator(); while (iter.hasNext()) { ELNode n = (ELNode) iter.next(); if (n instanceof Root) { return true; } } return false; } public void setMapName(String name) { this.mapName = name; } public String getMapName() { return mapName; } } public static class Visitor { public void visit(Root n) throws JasperException { n.getExpression().visit(this); } public void visit(Function n) throws JasperException { } public void visit(Text n) throws JasperException { } public void visit(ELText n) throws JasperException { } } } 1.1 jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/ELParser.java Index: ELParser.java =================================================================== /* ========================================================================= * * * * The Apache Software License, Version 1.1 * * * * Copyright (c) 1999, 2000, 2001 The Apache Software Foundation. * * All rights reserved. * * * * ========================================================================= * * * * Redistribution and use in source and binary forms, with or without modi- * * fication, are permitted provided that the following conditions are met: * * * * 1. Redistributions of source code must retain the above copyright notice * * 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 <[EMAIL PROTECTED]>. * * * * 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 Software Foundation. * * * * 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 indivi- * * duals 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; /** * This class implements a parser for EL expressions. * * It takes strings of the form xxx${..}yyy${..}zzz etc, and turn it into * a ELNode.Nodes. * * Currently, it only handles text outside ${..} and functions in ${ ..}. */ public class ELParser { private Token curToken; // current token private ELNode.Nodes expr; private ELNode.Nodes ELexpr; private int index; // Current index of the expression private String expression; // The EL expression private boolean escapeBS; // is '\' an escape char in text outside EL? public ELParser(String expression) { index = 0; this.expression = expression; expr = new ELNode.Nodes(); } public static ELNode.Nodes parse(String expression) { ELParser parser = new ELParser(expression); while (parser.hasNextChar()) { String text = parser.skipUntilEL(); if (text.length() > 0) { parser.expr.add(new ELNode.Text(text)); } ELNode.Nodes elexpr = parser.parseEL(); if (! elexpr.isEmpty()) { parser.expr.add(new ELNode.Root(elexpr)); } } return parser.expr; } /** * Parse EL into functions and else. [EMAIL PROTECTED] An ELLNode.Nodes representing the EL expression * TODO: this should be rewritten for a full parser. */ private ELNode.Nodes parseEL() { StringBuffer buf = new StringBuffer(); ELexpr = new ELNode.Nodes(); while (hasNext()) { curToken = nextToken(); if (curToken instanceof Char) { if (curToken.toChar() == '}') { break; } buf.append(curToken.toChar()); } else { // Output whatever is in buffer if (buf.length() > 0) { ELexpr.add(new ELNode.ELText(buf.toString())); } if (!parseFunction()) { ELexpr.add(new ELNode.ELText(curToken.toString())); } } } if (buf.length() > 0) { ELexpr.add(new ELNode.ELText(buf.toString())); } return ELexpr; } /** * Parse for a function * FunctionInvokation ::= (identifier ':')? identifier '(' * (Expression (,Expression)*)? ')' * Note: currently we don't parse arguments */ private boolean parseFunction() { if (! (curToken instanceof Id)) { return false; } String s1 = null; String s2 = curToken.toString(); int mark = getIndex(); if (hasNext()) { Token t = nextToken(); if (t.toChar() == ':') { if (hasNext()) { Token t2 = nextToken(); if (t2 instanceof Id) { s1 = s2; s2 = t2.toString(); if (hasNext()) { t = nextToken(); } } } } if (t.toChar() == '(') { ELexpr.add(new ELNode.Function(s1, s2)); return true; } } setIndex(mark); return false; } /** * Skip until an EL expression is reached. * @return The text string up to the EL expression */ private String skipUntilEL() { char prev = 0; StringBuffer buf = new StringBuffer(); while (hasNextChar()) { char ch = nextChar(); if (prev == '\\') { prev = 0; if (ch == '\\') { buf.append('\\'); if (!escapeBS) prev = '\\'; } else if (ch == '$') { buf.append('$'); } // else error! } else if (prev == '$') { if (ch == '{') { prev = 0; break; } buf.append('$'); buf.append(ch); } else if (ch == '\\' || ch == '$') { prev = ch; } else { buf.append(ch); } } if (prev != 0) { buf.append(prev); } return buf.toString(); } private boolean hasNext() { skipSpaces(); return hasNextChar(); } private Token nextToken() { skipSpaces(); if (hasNextChar()) { char ch = nextChar(); if (Character.isJavaIdentifierStart(ch)) { StringBuffer buf = new StringBuffer(); buf.append(ch); while ((ch = peekChar()) != -1 && Character.isJavaIdentifierPart(ch)) { buf.append(ch); nextChar(); } return new Id(buf.toString()); } if (ch == '\'' || ch == '"') { return parseQuotedChars(ch); } else { // For now... return new Char(ch); } } return null; } private Token parseQuotedChars(char quote) { StringBuffer buf = new StringBuffer(); buf.append(quote); while (hasNextChar()) { char ch = nextChar(); if (ch == '\\') { ch = nextChar(); if (ch == '\\' || ch == quote) { buf.append(ch); } // else error! } else if (ch == quote) { buf.append(ch); break; } else { buf.append(ch); } } return new QuotedString(buf.toString()); } private void skipSpaces() { while (hasNextChar()) { if (expression.charAt(index) > ' ') break; index++; } } private boolean hasNextChar() { return index < expression.length(); } private char nextChar() { if (index >= expression.length()) { return (char)-1; } return expression.charAt(index++); } private char peekChar() { if (index >= expression.length()) { return (char)-1; } return expression.charAt(index); } private int getIndex() { return index; } private void setIndex(int i) { index = i; } private static class Token { char toChar() { return 0; } public String toString() { return ""; } } private static class Id extends Token { String id; Id(String id) { this.id = id; } public String toString() { return id; } } private static class Char extends Token { private char ch; Char(char ch) { this.ch = ch; } char toChar() { return ch; } public String toString() { return (new Character(ch)).toString(); } } private static class QuotedString extends Token { private String value; QuotedString(String v) { this.value = v; } public String toString() { return value; } } } 1.105 +3 -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.104 retrieving revision 1.105 diff -u -r1.104 -r1.105 --- messages.properties 6 Mar 2003 20:21:46 -0000 1.104 +++ messages.properties 19 Mar 2003 20:51:35 -0000 1.105 @@ -374,4 +374,5 @@ jsp.error.attribute.null_name=Null attribute name jsp.error.jsptext.badcontent=\'<\', when appears in the body of <jsp:text>, must be encapsulated within a CDATA jsp.error.jsproot.version.invalid=Invalid version number: \"{0}\", must be \"1.2\" or \"2.0\" - +jsp.error.noFunctionPrefix=The function {0} must be used with a prefix when a default namespace is not specified +jsp.error.noFunction=The function {0} is cannot be located with the specified prefix
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]