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 if (intf[i].getName().indexOf("util.List") > 0) 062 return intf[i].getName(); 063 else if (intf[i].getName().indexOf("Iterator") > 0) 064 return intf[i].getName(); 065 } 066 067 if (clazz.getSuperclass() != null && clazz.getSuperclass().getInterfaces().length > 0) 068 return getClassName(clazz.getSuperclass()); 069 070 return clazz.getName(); 071 } 072 073 public Class getInterfaceClass(Class clazz) 074 { 075 if (IRender.class.isAssignableFrom(clazz) || clazz.isInterface() 076 || Modifier.isPublic(clazz.getModifiers())) 077 return clazz; 078 079 if (clazz.getName().equals("java.util.AbstractList$Itr")) 080 return Iterator.class; 081 082 if (Modifier.isPublic(clazz.getModifiers()) 083 && clazz.isInterface() || clazz.isPrimitive()) { 084 085 return clazz; 086 } 087 088 Class[] intf = clazz.getInterfaces(); 089 090 for (int i = 0; i < intf.length; i++) { 091 092 if (List.class.isAssignableFrom(intf[i])) 093 return List.class; 094 else if (Iterator.class.isAssignableFrom(intf[i])) 095 return Iterator.class; 096 else if (Map.class.isAssignableFrom(intf[i])) 097 return Map.class; 098 else if (Set.class.isAssignableFrom(intf[i])) 099 return Set.class; 100 else if (Collection.class.isAssignableFrom(intf[i])) 101 return Collection.class; 102 } 103 104 if (clazz.getSuperclass() != null && clazz.getSuperclass().getInterfaces().length > 0) 105 return getInterfaceClass(clazz.getSuperclass()); 106 107 return clazz; 108 } 109 110 public Class getRootExpressionClass(Node rootNode, OgnlContext context) 111 { 112 if (context.getRoot() == null) 113 return null; 114 115 Class ret = context.getRoot().getClass(); 116 117 if (!IRender.class.isInstance(context.getRoot()) && context.getFirstAccessor() != null && context.getFirstAccessor().isInstance(context.getRoot())) 118 ret = context.getFirstAccessor(); 119 120 return ret; 121 } 122 123 public void compileExpression(OgnlContext context, Node expression, Object root) 124 throws Exception 125 { 126 if (_log.isDebugEnabled()) 127 _log.debug("Compiling expr class " + expression.getClass().getName() + " and root " + root.getClass().getName() + " with toString:" + expression.toString()); 128 129 synchronized (expression) { 130 131 if (expression.getAccessor() != null) 132 return; 133 134 String getBody = null; 135 String setBody; 136 137 ClassFab classFab = _classFactory.newClass(ClassFabUtils.generateClassName(expression.getClass()), Object.class); 138 classFab.addInterface(ExpressionAccessor.class); 139 140 MethodSignature valueGetter = new MethodSignature(Object.class, "get", new Class[]{OgnlContext.class, Object.class}, null); 141 MethodSignature valueSetter = new MethodSignature(void.class, "set", new Class[]{OgnlContext.class, Object.class, Object.class}, null); 142 143 MethodSignature expressionSetter = new MethodSignature(void.class, "setExpression", new Class[]{Node.class}, null); 144 145 // must evaluate expression value at least once if object isn't null 146 147 if (root != null) 148 Ognl.getValue(expression, context, root); 149 150 try { 151 152 getBody = generateGetter(context, classFab, valueGetter, expression, root); 153 154 } catch (UnsupportedCompilationException uc) { 155 156 // uc.printStackTrace(); 157 // The target object may not fully resolve yet because of a partial tree with a null somewhere, we 158 // don't want to bail out forever because it might be enhancable on another pass eventually 159 return; 160 } catch (javassist.CannotCompileException e) { 161 162 _log.error("Error generating OGNL getter for expression " + expression + " with root " + root + " and body:\n" + getBody, e); 163 164 e.printStackTrace(); 165 166 generateFailSafe(context, expression, root); 167 return; 168 } 169 170 try { 171 172 classFab.addMethod(Modifier.PUBLIC, valueGetter, getBody); 173 174 } catch (Throwable t) { 175 176 _log.error("Error generating OGNL getter for expression " + expression + " with root " + root + " and body:\n" + getBody, t); 177 178 t.printStackTrace(); 179 180 generateFailSafe(context, expression, root); 181 return; 182 } 183 184 try { 185 186 setBody = generateSetter(context, classFab, valueSetter, expression, root); 187 188 } catch (UnsupportedCompilationException uc) { 189 190 //_log.warn("Unsupported setter compilation caught: " + uc.getMessage() + " for expression: " + expression.toString(), uc); 191 192 setBody = generateOgnlSetter(classFab, valueSetter); 193 194 if (!classFab.containsMethod(expressionSetter)) { 195 196 classFab.addField("_node", Node.class); 197 classFab.addMethod(Modifier.PUBLIC, expressionSetter, "{ _node = $1; }"); 198 } 199 } 200 201 try { 202 203 if (setBody == null) { 204 setBody = generateOgnlSetter(classFab, valueSetter); 205 206 if (!classFab.containsMethod(expressionSetter)) { 207 208 classFab.addField("_node", Node.class); 209 classFab.addMethod(Modifier.PUBLIC, expressionSetter, "{ _node = $1; }"); 210 } 211 } 212 213 if (setBody != null) 214 classFab.addMethod(Modifier.PUBLIC, valueSetter, setBody); 215 216 classFab.addConstructor(new Class[0], new Class[0], "{}"); 217 218 Class clazz = ((AbstractFab) classFab).createClass(true); 219 220 expression.setAccessor((ExpressionAccessor) clazz.newInstance()); 221 222 } catch (Throwable t) { 223 224 _log.error("Error generating OGNL statements for expression " + expression + " with root " + root, t); 225 t.printStackTrace(); 226 227 generateFailSafe(context, expression, root); 228 return; 229 } 230 231 // need to set expression on node if the field was just defined. 232 233 if (classFab.containsMethod(expressionSetter)) { 234 235 expression.getAccessor().setExpression(expression); 236 } 237 238 } 239 } 240 241 protected void generateFailSafe(OgnlContext context, Node expression, Object root) 242 { 243 if (expression.getAccessor() != null) 244 return; 245 246 try { 247 ClassFab classFab = _classFactory.newClass(expression.getClass().getName() + expression.hashCode() + "Accessor", Object.class); 248 classFab.addInterface(ExpressionAccessor.class); 249 250 MethodSignature valueGetter = new MethodSignature(Object.class, "get", new Class[]{OgnlContext.class, Object.class}, null); 251 MethodSignature valueSetter = new MethodSignature(void.class, "set", new Class[]{OgnlContext.class, Object.class, Object.class}, null); 252 253 MethodSignature expressionSetter = new MethodSignature(void.class, "setExpression", new Class[]{Node.class}, null); 254 255 if (!classFab.containsMethod(expressionSetter)) { 256 257 classFab.addField("_node", Node.class); 258 classFab.addMethod(Modifier.PUBLIC, expressionSetter, "{ _node = $1; }"); 259 } 260 261 classFab.addMethod(Modifier.PUBLIC, valueGetter, generateOgnlGetter(classFab, valueGetter)); 262 classFab.addMethod(Modifier.PUBLIC, valueSetter, generateOgnlSetter(classFab, valueSetter)); 263 264 classFab.addConstructor(new Class[0], new Class[0], "{}"); 265 266 Class clazz = ((AbstractFab) classFab).createClass(true); 267 268 expression.setAccessor((ExpressionAccessor) clazz.newInstance()); 269 270 // need to set expression on node if the field was just defined. 271 272 if (classFab.containsMethod(expressionSetter)) { 273 274 expression.getAccessor().setExpression(expression); 275 } 276 277 } catch (Throwable t) { 278 279 t.printStackTrace(); 280 } 281 } 282 283 protected String generateGetter(OgnlContext context, ClassFab newClass, MethodSignature valueGetter, Node expression, Object root) 284 throws Exception 285 { 286 String pre = ""; 287 String post = ""; 288 String body; 289 String getterCode; 290 291 context.setRoot(root); 292 context.setCurrentObject(root); 293 context.remove(PRE_CAST); 294 295 try { 296 297 getterCode = expression.toGetSourceString(context, root); 298 } catch (NullPointerException e) { 299 if (_log.isDebugEnabled()) 300 _log.warn("NullPointer caught compiling getter, may be normal ognl method artifact.", e); 301 302 throw new UnsupportedCompilationException("Statement threw nullpointer."); 303 } 304 305 if (getterCode == null || getterCode.trim().length() <= 0 && !ASTVarRef.class.isAssignableFrom(expression.getClass())) 306 getterCode = "null"; 307 308 String castExpression = (String) context.get(PRE_CAST); 309 310 if (context.getCurrentType() == null 311 || context.getCurrentType().isPrimitive() 312 || Character.class.isAssignableFrom(context.getCurrentType()) 313 || Object.class == context.getCurrentType()) 314 { 315 pre = pre + " ($w) ("; 316 post = post + ")"; 317 } 318 319 String rootExpr = !getterCode.equals("null") ? getRootExpression(expression, root, context) : ""; 320 321 String noRoot = (String) context.remove("_noRoot"); 322 if (noRoot != null) 323 rootExpr = ""; 324 325 createLocalReferences(context, newClass, valueGetter.getParameterTypes()); 326 327 if (OrderedReturn.class.isInstance(expression) && ((OrderedReturn) expression).getLastExpression() != null) { 328 329 body = "{ " 330 + (ASTMethod.class.isInstance(expression) || ASTChain.class.isInstance(expression) ? rootExpr : "") 331 + (castExpression != null ? castExpression : "") 332 + ((OrderedReturn) expression).getCoreExpression() 333 + " return " + pre + ((OrderedReturn) expression).getLastExpression() 334 + post 335 + ";}"; 336 337 } else { 338 339 body = "{ return " + pre 340 + (castExpression != null ? castExpression : "") 341 + rootExpr 342 + getterCode 343 + post 344 + ";}"; 345 } 346 347 body = body.replaceAll("\\.\\.", "."); 348 349 if (_log.isDebugEnabled()) 350 _log.debug("Getter Body: ===================================\n" + body); 351 352 return body; 353 } 354 355 void createLocalReferences(OgnlContext context, ClassFab classFab, Class[] params) 356 throws CannotCompileException, NotFoundException 357 { 358 context.remove(LOCAL_REFERENCE_COUNTER); 359 360 Map referenceMap = (Map) context.remove(LOCAL_REFERENCE_MAP); 361 if (referenceMap == null) 362 return; 363 364 Iterator it = referenceMap.keySet().iterator(); 365 366 while (it.hasNext()) { 367 368 String key = (String) it.next(); 369 LocalReference ref = (LocalReference) referenceMap.get(key); 370 371 String widener = ref.getType().isPrimitive() ? " " : " ($w) "; 372 373 String body = "{"; 374 body += " return " + widener + ref.getExpression() + ";"; 375 body += "}"; 376 377 body = body.replaceAll("\\.\\.", "."); 378 379 if (_log.isDebugEnabled()) 380 _log.debug("createLocalReferences() body is:\n" + body); 381 382 MethodSignature method = new MethodSignature(ref.getType(), ref.getName(), params, null); 383 classFab.addMethod(Modifier.PUBLIC, method, body); 384 } 385 } 386 387 protected String generateSetter(OgnlContext context, ClassFab newClass, MethodSignature valueSetter, Node expression, Object root) 388 throws Exception 389 { 390 if (ExpressionNode.class.isInstance(expression) 391 || ASTConst.class.isInstance(expression)) 392 throw new UnsupportedCompilationException("Can't compile expression/constant setters."); 393 394 context.setRoot(root); 395 context.setCurrentObject(root); 396 context.remove(PRE_CAST); 397 398 String body; 399 400 String setterCode = expression.toSetSourceString(context, root); 401 String castExpression = (String) context.get(PRE_CAST); 402 403 if (setterCode == null || setterCode.trim().length() < 1) 404 throw new UnsupportedCompilationException("Can't compile null setter body."); 405 406 if (root == null) 407 throw new UnsupportedCompilationException("Can't compile setters with a null root object."); 408 409 String pre = getRootExpression(expression, root, context); 410 411 String noRoot = (String) context.remove("_noRoot"); 412 if (noRoot != null) 413 pre = ""; 414 415 String setterValue = (String) context.remove("setterConversion"); 416 if (setterValue == null) 417 setterValue = ""; 418 419 createLocalReferences(context, newClass, valueSetter.getParameterTypes()); 420 421 body = "{" 422 + setterValue 423 + (castExpression != null ? castExpression : "") 424 + pre 425 + setterCode + ";}"; 426 427 body = body.replaceAll("\\.\\.", "."); 428 429 if (_log.isDebugEnabled()) 430 _log.debug("Setter Body: ===================================\n" + body); 431 432 return body; 433 } 434 435 String generateOgnlGetter(ClassFab newClass, MethodSignature valueGetter) 436 throws Exception 437 { 438 return "{ return _node.getValue($1, $2); }"; 439 } 440 441 String generateOgnlSetter(ClassFab newClass, MethodSignature valueSetter) 442 throws Exception 443 { 444 return "{ _node.setValue($1, $2, $3); }"; 445 } 446 }