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 // must evaluate expression value at least once if object isn't null 151 152 if (root != null) 153 Ognl.getValue(expression, context, root); 154 155 try 156 { 157 getBody = generateGetter(context, compiled); 158 } catch (UnsupportedCompilationException uc) 159 { 160 // uc.printStackTrace(); 161 // The target object may not fully resolve yet because of a partial tree with a null somewhere, we 162 // don't want to bail out forever because it might be enhancable on another pass eventually 163 return; 164 } catch (javassist.CannotCompileException e) 165 { 166 _log.error("Error generating OGNL getter for expression " + expression + " with root " + root + " and body:\n" + getBody, e); 167 168 e.printStackTrace(); 169 170 generateFailSafe(context, expression, root); 171 return; 172 } 173 174 try 175 { 176 generateClassFab(compiled).addMethod(Modifier.PUBLIC, valueGetter, getBody); 177 } catch (Throwable t) 178 { 179 _log.error("Error generating OGNL getter for expression " + expression + " with root " + root + " and body:\n" + getBody, t); 180 181 t.printStackTrace(); 182 183 generateFailSafe(context, expression, root); 184 return; 185 } 186 187 try 188 { 189 setBody = generateSetter(context, compiled); 190 } catch (UnsupportedCompilationException uc) 191 { 192 //_log.warn("Unsupported setter compilation caught: " + uc.getMessage() + " for expression: " + expression.toString(), uc); 193 194 setBody = generateOgnlSetter(generateClassFab(compiled), valueSetter); 195 196 if (!generateClassFab(compiled).containsMethod(expressionSetter)) 197 { 198 generateClassFab(compiled).addField("_node", Node.class); 199 generateClassFab(compiled).addMethod(Modifier.PUBLIC, expressionSetter, "{ _node = $1; }"); 200 } 201 } 202 203 try 204 { 205 if (setBody == null) 206 { 207 setBody = generateOgnlSetter(generateClassFab(compiled), valueSetter); 208 209 if (!generateClassFab(compiled).containsMethod(expressionSetter)) 210 { 211 generateClassFab(compiled).addField("_node", Node.class); 212 generateClassFab(compiled).addMethod(Modifier.PUBLIC, expressionSetter, "{ _node = $1; }"); 213 } 214 } 215 216 if (setBody != null) 217 generateClassFab(compiled).addMethod(Modifier.PUBLIC, valueSetter, setBody); 218 219 generateClassFab(compiled).addConstructor(new Class[0], new Class[0], "{}"); 220 221 Class clazz = ((AbstractFab) generateClassFab(compiled)).createClass(true); 222 223 expression.setAccessor((ExpressionAccessor) clazz.newInstance()); 224 225 } catch (Throwable t) 226 { 227 _log.error("Error generating OGNL statements for expression " + expression + " with root " + root, t); 228 t.printStackTrace(); 229 230 generateFailSafe(context, expression, root); 231 return; 232 } 233 234 // need to set expression on node if the field was just defined. 235 236 if (generateClassFab(compiled).containsMethod(expressionSetter)) 237 { 238 expression.getAccessor().setExpression(expression); 239 } 240 } 241 } 242 243 ClassFab generateClassFab(CompiledExpression compiled) 244 throws Exception 245 { 246 if (compiled.getGeneratedClass() != null) 247 return compiled.getGeneratedClass(); 248 249 ClassFab classFab = _classFactory.newClass(ClassFabUtils.generateClassName(compiled.getExpression().getClass()), Object.class); 250 classFab.addInterface(ExpressionAccessor.class); 251 252 compiled.setGeneratedClass(classFab); 253 254 return classFab; 255 } 256 257 protected void generateFailSafe(OgnlContext context, Node expression, Object root) 258 { 259 if (expression.getAccessor() != null) 260 return; 261 262 try 263 { 264 ClassFab classFab = _classFactory.newClass(expression.getClass().getName() + expression.hashCode() + "Accessor", Object.class); 265 classFab.addInterface(ExpressionAccessor.class); 266 267 MethodSignature valueGetter = new MethodSignature(Object.class, "get", new Class[]{OgnlContext.class, Object.class}, null); 268 MethodSignature valueSetter = new MethodSignature(void.class, "set", new Class[]{OgnlContext.class, Object.class, Object.class}, null); 269 270 MethodSignature expressionSetter = new MethodSignature(void.class, "setExpression", new Class[]{Node.class}, null); 271 272 if (!classFab.containsMethod(expressionSetter)) 273 { 274 classFab.addField("_node", Node.class); 275 classFab.addMethod(Modifier.PUBLIC, expressionSetter, "{ _node = $1; }"); 276 } 277 278 classFab.addMethod(Modifier.PUBLIC, valueGetter, generateOgnlGetter(classFab, valueGetter)); 279 classFab.addMethod(Modifier.PUBLIC, valueSetter, generateOgnlSetter(classFab, valueSetter)); 280 281 classFab.addConstructor(new Class[0], new Class[0], "{}"); 282 283 Class clazz = ((AbstractFab) classFab).createClass(true); 284 285 expression.setAccessor((ExpressionAccessor) clazz.newInstance()); 286 287 // need to set expression on node if the field was just defined. 288 289 if (classFab.containsMethod(expressionSetter)) 290 { 291 expression.getAccessor().setExpression(expression); 292 } 293 294 } catch (Throwable t) 295 { 296 t.printStackTrace(); 297 } 298 } 299 300 protected String generateGetter(OgnlContext context, CompiledExpression compiled) 301 throws Exception 302 { 303 String pre = ""; 304 String post = ""; 305 String body; 306 String getterCode; 307 308 context.setRoot(compiled.getRoot()); 309 context.setCurrentObject(compiled.getRoot()); 310 context.remove(PRE_CAST); 311 312 try 313 { 314 getterCode = compiled.getExpression().toGetSourceString(context, compiled.getRoot()); 315 } catch (NullPointerException e) 316 { 317 if (_log.isDebugEnabled()) 318 _log.warn("NullPointer caught compiling getter, may be normal ognl method artifact.", e); 319 320 throw new UnsupportedCompilationException("Statement threw nullpointer."); 321 } 322 323 if (getterCode == null || getterCode.trim().length() <= 0 324 && !ASTVarRef.class.isAssignableFrom(compiled.getExpression().getClass())) 325 { 326 getterCode = "null"; 327 } 328 329 String castExpression = (String) context.get(PRE_CAST); 330 331 if (context.getCurrentType() == null 332 || context.getCurrentType().isPrimitive() 333 || Character.class.isAssignableFrom(context.getCurrentType()) 334 || Object.class == context.getCurrentType()) 335 { 336 pre = pre + " ($w) ("; 337 post = post + ")"; 338 } 339 340 String rootExpr = !getterCode.equals("null") ? getRootExpression(compiled.getExpression(), compiled.getRoot(), context) : ""; 341 342 String noRoot = (String) context.remove("_noRoot"); 343 if (noRoot != null) 344 rootExpr = ""; 345 346 createLocalReferences(context, generateClassFab(compiled), compiled.getGetterMethod().getParameterTypes()); 347 348 if (OrderedReturn.class.isInstance(compiled.getExpression()) && ((OrderedReturn) compiled.getExpression()).getLastExpression() != null) 349 { 350 body = "{ " 351 + (ASTMethod.class.isInstance(compiled.getExpression()) || ASTChain.class.isInstance(compiled.getExpression()) ? rootExpr : "") 352 + (castExpression != null ? castExpression : "") 353 + ((OrderedReturn) compiled.getExpression()).getCoreExpression() 354 + " return " + pre + ((OrderedReturn) compiled.getExpression()).getLastExpression() 355 + post 356 + ";}"; 357 358 } else 359 { 360 body = "{ return " + pre 361 + (castExpression != null ? castExpression : "") 362 + rootExpr 363 + getterCode 364 + post 365 + ";}"; 366 } 367 368 body = body.replaceAll("\\.\\.", "."); 369 370 if (_log.isDebugEnabled()) 371 _log.debug("Getter Body: ===================================\n" + body); 372 373 return body; 374 } 375 376 void createLocalReferences(OgnlContext context, ClassFab classFab, Class[] params) 377 throws CannotCompileException, NotFoundException 378 { 379 Map referenceMap = context.getLocalReferences(); 380 if (referenceMap == null || referenceMap.size() < 1) 381 return; 382 383 Iterator it = referenceMap.keySet().iterator(); 384 385 while (it.hasNext()) 386 { 387 String key = (String) it.next(); 388 LocalReference ref = (LocalReference) referenceMap.get(key); 389 390 String widener = ref.getType().isPrimitive() ? " " : " ($w) "; 391 392 String body = "{"; 393 body += " return " + widener + ref.getExpression() + ";"; 394 body += "}"; 395 396 body = body.replaceAll("\\.\\.", "."); 397 398 if (_log.isDebugEnabled()) 399 _log.debug("createLocalReferences() body is:\n" + body); 400 401 MethodSignature method = new MethodSignature(ref.getType(), ref.getName(), params, null); 402 classFab.addMethod(Modifier.PUBLIC, method, body); 403 404 it.remove(); 405 } 406 } 407 408 protected String generateSetter(OgnlContext context, CompiledExpression compiled) 409 throws Exception 410 { 411 if (ExpressionNode.class.isInstance(compiled.getExpression()) 412 || ASTConst.class.isInstance(compiled.getExpression())) 413 throw new UnsupportedCompilationException("Can't compile expression/constant setters."); 414 415 context.setRoot(compiled.getRoot()); 416 context.setCurrentObject(compiled.getRoot()); 417 context.remove(PRE_CAST); 418 419 String body; 420 421 String setterCode = compiled.getExpression().toSetSourceString(context, compiled.getRoot()); 422 String castExpression = (String) context.get(PRE_CAST); 423 424 if (setterCode == null || setterCode.trim().length() < 1) 425 throw new UnsupportedCompilationException("Can't compile null setter body."); 426 427 if (compiled.getRoot() == null) 428 throw new UnsupportedCompilationException("Can't compile setters with a null root object."); 429 430 String pre = getRootExpression(compiled.getExpression(), compiled.getRoot(), context); 431 432 String noRoot = (String) context.remove("_noRoot"); 433 if (noRoot != null) 434 pre = ""; 435 436 String setterValue = (String) context.remove("setterConversion"); 437 if (setterValue == null) 438 setterValue = ""; 439 440 createLocalReferences(context, generateClassFab(compiled), compiled.getSettermethod().getParameterTypes()); 441 442 body = "{" 443 + setterValue 444 + (castExpression != null ? castExpression : "") 445 + pre 446 + setterCode + ";}"; 447 448 body = body.replaceAll("\\.\\.", "."); 449 450 if (_log.isDebugEnabled()) 451 _log.debug("Setter Body: ===================================\n" + body); 452 453 return body; 454 } 455 456 String generateOgnlGetter(ClassFab newClass, MethodSignature valueGetter) 457 throws Exception 458 { 459 return "{ return _node.getValue($1, $2); }"; 460 } 461 462 String generateOgnlSetter(ClassFab newClass, MethodSignature valueSetter) 463 throws Exception 464 { 465 return "{ _node.setValue($1, $2, $3); }"; 466 } 467 }