001 //Copyright 2004, 2005 The Apache Software Foundation
002
003 //Licensed under the Apache License, Version 2.0 (the "License");
004 //you may not use this file except in compliance with the License.
005 //You may obtain a copy of the License at
006
007 //http://www.apache.org/licenses/LICENSE-2.0
008
009 //Unless required by applicable law or agreed to in writing, software
010 //distributed under the License is distributed on an "AS IS" BASIS,
011 //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 //See the License for the specific language governing permissions and
013 //limitations under the License.
014 package org.apache.tapestry.services.impl;
015
016 import javassist.CannotCompileException;
017 import javassist.NotFoundException;
018 import ognl.*;
019 import ognl.enhance.*;
020 import org.apache.commons.logging.Log;
021 import org.apache.commons.logging.LogFactory;
022 import org.apache.hivemind.service.ClassFab;
023 import org.apache.hivemind.service.ClassFabUtils;
024 import org.apache.hivemind.service.ClassFactory;
025 import org.apache.hivemind.service.MethodSignature;
026 import org.apache.tapestry.IRender;
027 import org.apache.tapestry.enhance.AbstractFab;
028
029 import java.lang.reflect.Modifier;
030 import java.util.*;
031
032 /**
033 * Adds to default ognl compiler class pools.
034 *
035 */
036 public class HiveMindExpressionCompiler extends ExpressionCompiler implements OgnlExpressionCompiler {
037
038 private static final Log _log = LogFactory.getLog(HiveMindExpressionCompiler.class);
039
040 private ClassFactory _classFactory;
041
042 public HiveMindExpressionCompiler(ClassFactory classfactory)
043 {
044 _classFactory = classfactory;
045 }
046
047 public String getClassName(Class clazz)
048 {
049 if (IRender.class.isAssignableFrom(clazz) || Modifier.isPublic(clazz.getModifiers()))
050 return clazz.getName();
051
052 if (clazz.getName().equals("java.util.AbstractList$Itr"))
053 return Iterator.class.getName();
054
055 if (Modifier.isPublic(clazz.getModifiers()) && clazz.isInterface())
056 return clazz.getName();
057
058 Class[] intf = clazz.getInterfaces();
059
060 for (int i = 0; i < intf.length; i++)
061 {
062 if (intf[i].getName().indexOf("util.List") > 0)
063 return intf[i].getName();
064 else if (intf[i].getName().indexOf("Iterator") > 0)
065 return intf[i].getName();
066 }
067
068 if (clazz.getSuperclass() != null && clazz.getSuperclass().getInterfaces().length > 0)
069 return getClassName(clazz.getSuperclass());
070
071 return clazz.getName();
072 }
073
074 public Class getInterfaceClass(Class clazz)
075 {
076 if (IRender.class.isAssignableFrom(clazz) || clazz.isInterface()
077 || Modifier.isPublic(clazz.getModifiers()))
078 return clazz;
079
080 if (clazz.getName().equals("java.util.AbstractList$Itr"))
081 return Iterator.class;
082
083 if (Modifier.isPublic(clazz.getModifiers())
084 && clazz.isInterface() || clazz.isPrimitive())
085 {
086 return clazz;
087 }
088
089 Class[] intf = clazz.getInterfaces();
090
091 for (int i = 0; i < intf.length; i++)
092 {
093 if (List.class.isAssignableFrom(intf[i]))
094 return List.class;
095 else if (Iterator.class.isAssignableFrom(intf[i]))
096 return Iterator.class;
097 else if (Map.class.isAssignableFrom(intf[i]))
098 return Map.class;
099 else if (Set.class.isAssignableFrom(intf[i]))
100 return Set.class;
101 else if (Collection.class.isAssignableFrom(intf[i]))
102 return Collection.class;
103 }
104
105 if (clazz.getSuperclass() != null && clazz.getSuperclass().getInterfaces().length > 0)
106 return getInterfaceClass(clazz.getSuperclass());
107
108 return clazz;
109 }
110
111 public Class getRootExpressionClass(Node rootNode, OgnlContext context)
112 {
113 if (context.getRoot() == null)
114 return null;
115
116 Class ret = context.getRoot().getClass();
117
118 if (!IRender.class.isInstance(context.getRoot())
119 && context.getFirstAccessor() != null
120 && context.getFirstAccessor().isInstance(context.getRoot()))
121 {
122 ret = context.getFirstAccessor();
123 }
124
125 return ret;
126 }
127
128 public void compileExpression(OgnlContext context, Node expression, Object root)
129 throws Exception
130 {
131 if (_log.isDebugEnabled())
132 _log.debug("Compiling expr class " + expression.getClass().getName()
133 + " and root " + root.getClass().getName() + " with toString:" + expression.toString());
134
135 synchronized (expression)
136 {
137 if (expression.getAccessor() != null)
138 return;
139
140 String getBody = null;
141 String setBody;
142
143 MethodSignature valueGetter = new MethodSignature(Object.class, "get", new Class[]{OgnlContext.class, Object.class}, null);
144 MethodSignature valueSetter = new MethodSignature(void.class, "set", new Class[]{OgnlContext.class, Object.class, Object.class}, null);
145
146 CompiledExpression compiled = new CompiledExpression(expression, root, valueGetter, valueSetter);
147
148 MethodSignature expressionSetter = new MethodSignature(void.class, "setExpression", new Class[]{Node.class}, null);
149
150 try
151 {
152 getBody = generateGetter(context, compiled);
153 } catch (UnsupportedCompilationException uc)
154 {
155 // uc.printStackTrace();
156 // The target object may not fully resolve yet because of a partial tree with a null somewhere, we
157 // don't want to bail out forever because it might be enhancable on another pass eventually
158 return;
159 } catch (javassist.CannotCompileException e)
160 {
161 _log.error("Error generating OGNL getter for expression " + expression + " with root " + root + " and body:\n" + getBody, e);
162
163 e.printStackTrace();
164
165 generateFailSafe(context, expression, root);
166 return;
167 }
168
169 try
170 {
171 generateClassFab(compiled).addMethod(Modifier.PUBLIC, valueGetter, getBody);
172 } catch (Throwable t)
173 {
174 _log.error("Error generating OGNL getter for expression " + expression + " with root " + root + " and body:\n" + getBody, t);
175
176 t.printStackTrace();
177
178 generateFailSafe(context, expression, root);
179 return;
180 }
181
182 try
183 {
184 setBody = generateSetter(context, compiled);
185 } catch (UnsupportedCompilationException uc)
186 {
187 //_log.warn("Unsupported setter compilation caught: " + uc.getMessage() + " for expression: " + expression.toString(), uc);
188
189 setBody = generateOgnlSetter(generateClassFab(compiled), valueSetter);
190
191 if (!generateClassFab(compiled).containsMethod(expressionSetter))
192 {
193 generateClassFab(compiled).addField("_node", Node.class);
194 generateClassFab(compiled).addMethod(Modifier.PUBLIC, expressionSetter, "{ _node = $1; }");
195 }
196 }
197
198 try
199 {
200 if (setBody == null)
201 {
202 setBody = generateOgnlSetter(generateClassFab(compiled), valueSetter);
203
204 if (!generateClassFab(compiled).containsMethod(expressionSetter))
205 {
206 generateClassFab(compiled).addField("_node", Node.class);
207 generateClassFab(compiled).addMethod(Modifier.PUBLIC, expressionSetter, "{ _node = $1; }");
208 }
209 }
210
211 if (setBody != null)
212 generateClassFab(compiled).addMethod(Modifier.PUBLIC, valueSetter, setBody);
213
214 generateClassFab(compiled).addConstructor(new Class[0], new Class[0], "{}");
215
216 Class clazz = ((AbstractFab) generateClassFab(compiled)).createClass(true);
217
218 expression.setAccessor((ExpressionAccessor) clazz.newInstance());
219
220 } catch (Throwable t)
221 {
222 _log.error("Error generating OGNL statements for expression " + expression + " with root " + root, t);
223 t.printStackTrace();
224
225 generateFailSafe(context, expression, root);
226 return;
227 }
228
229 // need to set expression on node if the field was just defined.
230
231 if (generateClassFab(compiled).containsMethod(expressionSetter))
232 {
233 expression.getAccessor().setExpression(expression);
234 }
235 }
236 }
237
238 ClassFab generateClassFab(CompiledExpression compiled)
239 throws Exception
240 {
241 if (compiled.getGeneratedClass() != null)
242 return compiled.getGeneratedClass();
243
244 ClassFab classFab = _classFactory.newClass(ClassFabUtils.generateClassName(compiled.getExpression().getClass()), Object.class);
245 classFab.addInterface(ExpressionAccessor.class);
246
247 compiled.setGeneratedClass(classFab);
248
249 return classFab;
250 }
251
252 protected void generateFailSafe(OgnlContext context, Node expression, Object root)
253 {
254 if (expression.getAccessor() != null)
255 return;
256
257 try
258 {
259 ClassFab classFab = _classFactory.newClass(expression.getClass().getName() + expression.hashCode() + "Accessor", Object.class);
260 classFab.addInterface(ExpressionAccessor.class);
261
262 MethodSignature valueGetter = new MethodSignature(Object.class, "get", new Class[]{OgnlContext.class, Object.class}, null);
263 MethodSignature valueSetter = new MethodSignature(void.class, "set", new Class[]{OgnlContext.class, Object.class, Object.class}, null);
264
265 MethodSignature expressionSetter = new MethodSignature(void.class, "setExpression", new Class[]{Node.class}, null);
266
267 if (!classFab.containsMethod(expressionSetter))
268 {
269 classFab.addField("_node", Node.class);
270 classFab.addMethod(Modifier.PUBLIC, expressionSetter, "{ _node = $1; }");
271 }
272
273 classFab.addMethod(Modifier.PUBLIC, valueGetter, generateOgnlGetter(classFab, valueGetter));
274 classFab.addMethod(Modifier.PUBLIC, valueSetter, generateOgnlSetter(classFab, valueSetter));
275
276 classFab.addConstructor(new Class[0], new Class[0], "{}");
277
278 Class clazz = ((AbstractFab) classFab).createClass(true);
279
280 expression.setAccessor((ExpressionAccessor) clazz.newInstance());
281
282 // need to set expression on node if the field was just defined.
283
284 if (classFab.containsMethod(expressionSetter))
285 {
286 expression.getAccessor().setExpression(expression);
287 }
288
289 } catch (Throwable t)
290 {
291 t.printStackTrace();
292 }
293 }
294
295 protected String generateGetter(OgnlContext context, CompiledExpression compiled)
296 throws Exception
297 {
298 String pre = "";
299 String post = "";
300 String body;
301 String getterCode;
302
303 context.setRoot(compiled.getRoot());
304 context.setCurrentObject(compiled.getRoot());
305 context.remove(PRE_CAST);
306
307 try
308 {
309 getterCode = compiled.getExpression().toGetSourceString(context, compiled.getRoot());
310 } catch (NullPointerException e)
311 {
312 if (_log.isDebugEnabled())
313 _log.warn("NullPointer caught compiling getter, may be normal ognl method artifact.", e);
314
315 throw new UnsupportedCompilationException("Statement threw nullpointer.");
316 }
317
318 if (getterCode == null || getterCode.trim().length() <= 0
319 && !ASTVarRef.class.isAssignableFrom(compiled.getExpression().getClass()))
320 {
321 getterCode = "null";
322 }
323
324 String castExpression = (String) context.get(PRE_CAST);
325
326 if (context.getCurrentType() == null
327 || context.getCurrentType().isPrimitive()
328 || Character.class.isAssignableFrom(context.getCurrentType())
329 || Object.class == context.getCurrentType())
330 {
331 pre = pre + " ($w) (";
332 post = post + ")";
333 }
334
335 String rootExpr = !getterCode.equals("null") ? getRootExpression(compiled.getExpression(), compiled.getRoot(), context) : "";
336
337 String noRoot = (String) context.remove("_noRoot");
338 if (noRoot != null)
339 rootExpr = "";
340
341 createLocalReferences(context, generateClassFab(compiled), compiled.getGetterMethod().getParameterTypes());
342
343 if (OrderedReturn.class.isInstance(compiled.getExpression()) && ((OrderedReturn) compiled.getExpression()).getLastExpression() != null)
344 {
345 body = "{ "
346 + (ASTMethod.class.isInstance(compiled.getExpression()) || ASTChain.class.isInstance(compiled.getExpression()) ? rootExpr : "")
347 + (castExpression != null ? castExpression : "")
348 + ((OrderedReturn) compiled.getExpression()).getCoreExpression()
349 + " return " + pre + ((OrderedReturn) compiled.getExpression()).getLastExpression()
350 + post
351 + ";}";
352
353 } else
354 {
355 body = "{ return " + pre
356 + (castExpression != null ? castExpression : "")
357 + rootExpr
358 + getterCode
359 + post
360 + ";}";
361 }
362
363 body = body.replaceAll("\\.\\.", ".");
364
365 if (_log.isDebugEnabled())
366 _log.debug("Getter Body: ===================================\n" + body);
367
368 return body;
369 }
370
371 void createLocalReferences(OgnlContext context, ClassFab classFab, Class[] params)
372 throws CannotCompileException, NotFoundException
373 {
374 Map referenceMap = context.getLocalReferences();
375 if (referenceMap == null || referenceMap.size() < 1)
376 return;
377
378 Iterator it = referenceMap.keySet().iterator();
379
380 while (it.hasNext())
381 {
382 String key = (String) it.next();
383 LocalReference ref = (LocalReference) referenceMap.get(key);
384
385 String widener = ref.getType().isPrimitive() ? " " : " ($w) ";
386
387 String body = "{";
388 body += " return " + widener + ref.getExpression() + ";";
389 body += "}";
390
391 body = body.replaceAll("\\.\\.", ".");
392
393 if (_log.isDebugEnabled())
394 _log.debug("createLocalReferences() body is:\n" + body);
395
396 MethodSignature method = new MethodSignature(ref.getType(), ref.getName(), params, null);
397 classFab.addMethod(Modifier.PUBLIC, method, body);
398
399 it.remove();
400 }
401 }
402
403 protected String generateSetter(OgnlContext context, CompiledExpression compiled)
404 throws Exception
405 {
406 if (ExpressionNode.class.isInstance(compiled.getExpression())
407 || ASTConst.class.isInstance(compiled.getExpression()))
408 throw new UnsupportedCompilationException("Can't compile expression/constant setters.");
409
410 context.setRoot(compiled.getRoot());
411 context.setCurrentObject(compiled.getRoot());
412 context.remove(PRE_CAST);
413
414 String body;
415
416 String setterCode = compiled.getExpression().toSetSourceString(context, compiled.getRoot());
417 String castExpression = (String) context.get(PRE_CAST);
418
419 if (setterCode == null || setterCode.trim().length() < 1)
420 throw new UnsupportedCompilationException("Can't compile null setter body.");
421
422 if (compiled.getRoot() == null)
423 throw new UnsupportedCompilationException("Can't compile setters with a null root object.");
424
425 String pre = getRootExpression(compiled.getExpression(), compiled.getRoot(), context);
426
427 String noRoot = (String) context.remove("_noRoot");
428 if (noRoot != null)
429 pre = "";
430
431 String setterValue = (String) context.remove("setterConversion");
432 if (setterValue == null)
433 setterValue = "";
434
435 createLocalReferences(context, generateClassFab(compiled), compiled.getSettermethod().getParameterTypes());
436
437 body = "{"
438 + setterValue
439 + (castExpression != null ? castExpression : "")
440 + pre
441 + setterCode + ";}";
442
443 body = body.replaceAll("\\.\\.", ".");
444
445 if (_log.isDebugEnabled())
446 _log.debug("Setter Body: ===================================\n" + body);
447
448 return body;
449 }
450
451 String generateOgnlGetter(ClassFab newClass, MethodSignature valueGetter)
452 throws Exception
453 {
454 return "{ return _node.getValue($1, $2); }";
455 }
456
457 String generateOgnlSetter(ClassFab newClass, MethodSignature valueSetter)
458 throws Exception
459 {
460 return "{ _node.setValue($1, $2, $3); }";
461 }
462 }