/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jasper.interpreter; import java.io.StringReader; import java.util.ArrayList; import java.util.List; import org.apache.el.parser.AstDeferredExpression; import org.apache.el.parser.AstDynamicExpression; import org.apache.el.parser.AstLiteralExpression; import org.apache.el.parser.ELParser; import org.apache.el.parser.Node; import org.apache.el.parser.ParseException; import org.apache.jasper.JspCompilationContext; import org.apache.jasper.compiler.ELInterpreter; import org.apache.jasper.compiler.JspUtil; /** * Simple ELInterpreter. It can transfer simple ELs to java code directly. * EL is a big bottleneck of JSP. EL resolving takes much CPU expense. * * The performance is better when simple ELs were transfered to java code. * * However the code will by pass all ELResolvers. This interpreter is not a standard of JSP specification. * When activate this ELInterpreter. User must know how it works. * * Here are the cases of Simple ELs, * * 1.Simple EL, only contains one part of expression, for example ${elemId}. This kind of EL will be generated as * * this.getJspContext().findAttribute("elemId") * * 2.EL with two parts of expression and type of first part is specified by attribute in Tag File. * For example, ${model.location} . "model" is specified on the top of tag file, * * <%@ attribute name="model" required="true" type="org.apache.jasper.model.results.ItemModel" %>. * * It is generated as (getModel() != null ? getModel().getLocation() : null) * * 3.EL with logic or arithmetic and the value part can be generated. * For example: ${(intlExpansion eq 'true' && not empty model.location) || sortType==7} * * It is generated as * (org.apache.jasper.runtime.ELRuntimeUtil.equals(getIntlExpansion(), "true")&&(!org.apache.jasper.runtime.ELRuntimeUtil.isEmpty((getModel() != null ? getModel().getLocation() : null)))) * * How to apply this ELInterpreter? * * It is based on BUG 54239. * * @author Sheldon Shao */ public class JasperELInterpreter implements ELInterpreter { public static enum ELFormat { SINGLE, COUPLE, MULTIPLE, COMPLEX }; public String interpreterCall(JspCompilationContext context, boolean isTagFile, String expression, Class expectedType, String fnmapvar, boolean xmlEscape) { if (fnmapvar == null) { List nodes = new ArrayList(2); ELFormat format = split(expression, nodes); if (format == ELFormat.SINGLE) { return ELCodegen.genSingleNode(context, isTagFile, nodes.get(0), expectedType, xmlEscape, true); } else if (format == ELFormat.COUPLE && isTagFile) { String firstNode = nodes.get(0); String secondNode = nodes.get(1); return ELCodegen.genCoupleNodes(context, isTagFile, firstNode, secondNode, expression, expectedType, xmlEscape, true); } String extension = forExtend(format, context, isTagFile, expression, expectedType, fnmapvar, xmlEscape, nodes); if (extension != null) { return extension; } else { Node node = parseAsNode(expression); if (node != null) { try { return generate(context, isTagFile, expectedType, node, xmlEscape); } catch(IllegalStateException ise) { } } } } return JspUtil.interpreterCall(isTagFile, expression, expectedType, fnmapvar, xmlEscape); } protected String forExtend(ELFormat format, JspCompilationContext context, boolean isTagFile, String expression, Class expectedType, String fnmapvar, boolean xmlEscape, List nodes) { return null; } private String generate(JspCompilationContext context, boolean isTagFile, Class expectedType, Node node, boolean xmlEscape) { String code = ELCodegen.generate(context, isTagFile, expectedType, node, true); if (xmlEscape && String.class == expectedType) { return "org.apache.jasper.runtime.ELRuntimeUtil.xmlEscape(" + code + ")"; } return code; } private Node parseAsNode(String expression) { ELParser parser = new ELParser(new StringReader(expression)); Node n; try { n = parser.CompositeExpression(); } catch (ParseException e) { return null; } // validate composite expression int numChildren = n.jjtGetNumChildren(); if (numChildren == 1) { n = n.jjtGetChild(0); } else { Class type = null; Node child = null; for (int i = 0; i < numChildren; i++) { child = n.jjtGetChild(i); if (child instanceof AstLiteralExpression) continue; if (type == null) type = child.getClass(); else { if (!type.equals(child.getClass())) { return null; } } } } if (n instanceof AstDeferredExpression || n instanceof AstDynamicExpression) { n = n.jjtGetChild(0); } return n; } private static int[] trim(String text, int start, int end) { char ch; int i = start; for (; i < end; i++) { ch = text.charAt(i); if (ch == ' ' || ch == '\t') { continue; } else { break; } } start = i; for (i = end -1; i > start; i--) { ch = text.charAt(i); if (ch == ' ' || ch == '\t') { continue; } else { break; } } end = i + 1; return new int[] { start, end }; } private ELFormat split(String text, List nodes) { char ch = text.charAt(0); int len = text.length(); if (ch == '$' || ch == '#') { int end = len - 1; if (text.charAt(1) == '{' && text.charAt(end) == '}') { int[] trimed = trim(text, 2, end); ch = text.charAt(trimed[0]); if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) { int nodeCount = 1; int pos = trimed[0]; end = trimed[1]; for (int i = pos+1; i < end; i++) { ch = text.charAt(i); if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_') { continue; } else if (ch == '.') { String el = text.substring(pos, i); nodes.add(el); pos = i + 1; nodeCount++; } else { return ELFormat.COMPLEX; } } if (pos < end) { String el = text.substring(pos, end); nodes.add(el); } return nodeCount == 1 ? ELFormat.SINGLE :(nodeCount == 2 ? ELFormat.COUPLE : ELFormat.MULTIPLE); } } } return ELFormat.COMPLEX; } }