View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.struts2.jasper.compiler;
19  
20  import org.apache.struts2.jasper.JasperException;
21  
22  import javax.servlet.jsp.tagext.FunctionInfo;
23  import java.util.ArrayList;
24  import java.util.HashMap;
25  
26  /***
27   * This class generates functions mappers for the EL expressions in the page.
28   * Instead of a global mapper, a mapper is used for each call to EL
29   * evaluator, thus avoiding the prefix overlapping and redefinition
30   * issues.
31   *
32   * @author Kin-man Chung
33   */
34  
35  public class ELFunctionMapper {
36      private int currFunc = 0;
37      StringBuffer ds;  // Contains codes to initialize the functions mappers.
38      StringBuffer ss;  // Contains declarations of the functions mappers.
39  
40      /***
41       * Creates the functions mappers for all EL expressions in the JSP page.
42       *
43       * @param compiler Current compiler, mainly for accessing error dispatcher.
44       * @param page     The current compilation unit.
45       */
46      public static void map(Compiler compiler, Node.Nodes page)
47              throws JasperException {
48  
49          ELFunctionMapper map = new ELFunctionMapper();
50          map.ds = new StringBuffer();
51          map.ss = new StringBuffer();
52  
53          page.visit(map.new ELFunctionVisitor());
54  
55          // Append the declarations to the root node
56          String ds = map.ds.toString();
57          if (ds.length() > 0) {
58              Node root = page.getRoot();
59              new Node.Declaration(map.ss.toString(), null, root);
60              new Node.Declaration("static {\n" + ds + "}\n", null, root);
61          }
62      }
63  
64      /***
65       * A visitor for the page.  The places where EL is allowed are scanned
66       * for functions, and if found functions mappers are created.
67       */
68      class ELFunctionVisitor extends Node.Visitor {
69  
70          /***
71           * Use a global name map to facilitate reuse of function maps.
72           * The key used is prefix:function:uri.
73           */
74          private HashMap gMap = new HashMap();
75  
76          public void visit(Node.ParamAction n) throws JasperException {
77              doMap(n.getValue());
78              visitBody(n);
79          }
80  
81          public void visit(Node.IncludeAction n) throws JasperException {
82              doMap(n.getPage());
83              visitBody(n);
84          }
85  
86          public void visit(Node.ForwardAction n) throws JasperException {
87              doMap(n.getPage());
88              visitBody(n);
89          }
90  
91          public void visit(Node.SetProperty n) throws JasperException {
92              doMap(n.getValue());
93              visitBody(n);
94          }
95  
96          public void visit(Node.UseBean n) throws JasperException {
97              doMap(n.getBeanName());
98              visitBody(n);
99          }
100 
101         public void visit(Node.PlugIn n) throws JasperException {
102             doMap(n.getHeight());
103             doMap(n.getWidth());
104             visitBody(n);
105         }
106 
107         public void visit(Node.JspElement n) throws JasperException {
108 
109             Node.JspAttribute[] attrs = n.getJspAttributes();
110             for (int i = 0; attrs != null && i < attrs.length; i++) {
111                 doMap(attrs[i]);
112             }
113             doMap(n.getNameAttribute());
114             visitBody(n);
115         }
116 
117         public void visit(Node.UninterpretedTag n) throws JasperException {
118 
119             Node.JspAttribute[] attrs = n.getJspAttributes();
120             for (int i = 0; attrs != null && i < attrs.length; i++) {
121                 doMap(attrs[i]);
122             }
123             visitBody(n);
124         }
125 
126         public void visit(Node.CustomTag n) throws JasperException {
127             Node.JspAttribute[] attrs = n.getJspAttributes();
128             for (int i = 0; attrs != null && i < attrs.length; i++) {
129                 doMap(attrs[i]);
130             }
131             visitBody(n);
132         }
133 
134         public void visit(Node.ELExpression n) throws JasperException {
135             doMap(n.getEL());
136         }
137 
138         private void doMap(Node.JspAttribute attr)
139                 throws JasperException {
140             if (attr != null) {
141                 doMap(attr.getEL());
142             }
143         }
144 
145         /***
146          * Creates function mappers, if needed, from ELNodes
147          */
148         private void doMap(ELNode.Nodes el)
149                 throws JasperException {
150 
151             // Only care about functions in ELNode's
152             class Fvisitor extends ELNode.Visitor {
153                 ArrayList funcs = new ArrayList();
154                 HashMap keyMap = new HashMap();
155 
156                 public void visit(ELNode.Function n) throws JasperException {
157                     String key = n.getPrefix() + ":" + n.getName();
158                     if (!keyMap.containsKey(key)) {
159                         keyMap.put(key, "");
160                         funcs.add(n);
161                     }
162                 }
163             }
164 
165             if (el == null) {
166                 return;
167             }
168 
169             // First locate all unique functions in this EL
170             Fvisitor fv = new Fvisitor();
171             el.visit(fv);
172             ArrayList functions = fv.funcs;
173 
174             if (functions.size() == 0) {
175                 return;
176             }
177 
178             // Reuse a previous map if possible
179             String decName = matchMap(functions);
180             if (decName != null) {
181                 el.setMapName(decName);
182                 return;
183             }
184 
185             // Generate declaration for the map statically
186             decName = getMapName();
187             ss.append("static private org.apache.struts2.jasper.runtime.ProtectedFunctionMapper " + decName + ";\n");
188 
189             ds.append("  " + decName + "= ");
190             ds.append("org.apache.struts2.jasper.runtime.ProtectedFunctionMapper");
191 
192             // Special case if there is only one function in the map
193             String funcMethod = null;
194             if (functions.size() == 1) {
195                 funcMethod = ".getMapForFunction";
196             } else {
197                 ds.append(".getInstance();\n");
198                 funcMethod = "  " + decName + ".mapFunction";
199             }
200 
201             // Setup arguments for either getMapForFunction or mapFunction
202             for (int i = 0; i < functions.size(); i++) {
203                 ELNode.Function f = (ELNode.Function) functions.get(i);
204                 FunctionInfo funcInfo = f.getFunctionInfo();
205                 String key = f.getPrefix() + ":" + f.getName();
206                 ds.append(funcMethod + "(\"" + key + "\", " +
207                         funcInfo.getFunctionClass() + ".class, " +
208                         '\"' + f.getMethodName() + "\", " +
209                         "new Class[] {");
210                 String params[] = f.getParameters();
211                 for (int k = 0; k < params.length; k++) {
212                     if (k != 0) {
213                         ds.append(", ");
214                     }
215                     int iArray = params[k].indexOf('[');
216                     if (iArray < 0) {
217                         ds.append(params[k] + ".class");
218                     } else {
219                         String baseType = params[k].substring(0, iArray);
220                         ds.append("java.lang.reflect.Array.newInstance(");
221                         ds.append(baseType);
222                         ds.append(".class,");
223 
224                         // Count the number of array dimension
225                         int aCount = 0;
226                         for (int jj = iArray; jj < params[k].length(); jj++) {
227                             if (params[k].charAt(jj) == '[') {
228                                 aCount++;
229                             }
230                         }
231                         if (aCount == 1) {
232                             ds.append("0).getClass()");
233                         } else {
234                             ds.append("new int[" + aCount + "]).getClass()");
235                         }
236                     }
237                 }
238                 ds.append("});\n");
239                 // Put the current name in the global function map
240                 gMap.put(f.getPrefix() + ':' + f.getName() + ':' + f.getUri(),
241                         decName);
242             }
243             el.setMapName(decName);
244         }
245 
246         /***
247          * Find the name of the function mapper for an EL.  Reuse a
248          * previously generated one if possible.
249          *
250          * @param functions An ArrayList of ELNode.Function instances that
251          *                  represents the functions in an EL
252          * @return A previous generated function mapper name that can be used
253          *         by this EL; null if none found.
254          */
255         private String matchMap(ArrayList functions) {
256 
257             String mapName = null;
258             for (int i = 0; i < functions.size(); i++) {
259                 ELNode.Function f = (ELNode.Function) functions.get(i);
260                 String temName = (String) gMap.get(f.getPrefix() + ':' +
261                         f.getName() + ':' + f.getUri());
262                 if (temName == null) {
263                     return null;
264                 }
265                 if (mapName == null) {
266                     mapName = temName;
267                 } else if (!temName.equals(mapName)) {
268                     // If not all in the previous match, then no match.
269                     return null;
270                 }
271             }
272             return mapName;
273         }
274 
275         /*
276          * @return An unique name for a function mapper.
277          */
278         private String getMapName() {
279             return "_jspx_fnmap_" + currFunc++;
280         }
281     }
282 }
283