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 }