Coverage Report - org.apache.tapestry.services.impl.HiveMindExpressionCompiler
 
Classes in this File Line Coverage Branch Coverage Complexity
HiveMindExpressionCompiler
54% 
50% 
8.182
 
 1  
 //Copyright 2004, 2005 The Apache Software Foundation
 2  
 
 3  
 //Licensed under the Apache License, Version 2.0 (the "License");
 4  
 //you may not use this file except in compliance with the License.
 5  
 //You may obtain a copy of the License at
 6  
 
 7  
 //http://www.apache.org/licenses/LICENSE-2.0
 8  
 
 9  
 //Unless required by applicable law or agreed to in writing, software
 10  
 //distributed under the License is distributed on an "AS IS" BASIS,
 11  
 //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12  
 //See the License for the specific language governing permissions and
 13  
 //limitations under the License.
 14  
 package org.apache.tapestry.services.impl;
 15  
 
 16  
 import javassist.CannotCompileException;
 17  
 import javassist.NotFoundException;
 18  
 import ognl.*;
 19  
 import ognl.enhance.*;
 20  
 import org.apache.commons.logging.Log;
 21  
 import org.apache.commons.logging.LogFactory;
 22  
 import org.apache.hivemind.service.ClassFab;
 23  
 import org.apache.hivemind.service.ClassFabUtils;
 24  
 import org.apache.hivemind.service.ClassFactory;
 25  
 import org.apache.hivemind.service.MethodSignature;
 26  
 import org.apache.tapestry.IRender;
 27  
 import org.apache.tapestry.enhance.AbstractFab;
 28  
 
 29  
 import java.lang.reflect.Modifier;
 30  
 import java.util.*;
 31  
 
 32  
 /**
 33  
  * Adds to default ognl compiler class pools.
 34  
  *
 35  
  */
 36  
 public class HiveMindExpressionCompiler extends ExpressionCompiler implements OgnlExpressionCompiler {
 37  
     
 38  11
     private static final Log _log = LogFactory.getLog(HiveMindExpressionCompiler.class);
 39  
     
 40  
     private ClassFactory _classFactory;
 41  
 
 42  
     public HiveMindExpressionCompiler(ClassFactory classfactory)
 43  17
     {
 44  17
         _classFactory = classfactory;
 45  17
     }
 46  
 
 47  
     public String getClassName(Class clazz)
 48  
     {
 49  15
         if (IRender.class.isAssignableFrom(clazz) || Modifier.isPublic(clazz.getModifiers()))
 50  15
             return clazz.getName();
 51  
 
 52  0
         if (clazz.getName().equals("java.util.AbstractList$Itr"))
 53  0
             return Iterator.class.getName();
 54  
 
 55  0
         if (Modifier.isPublic(clazz.getModifiers()) && clazz.isInterface())
 56  0
             return clazz.getName();
 57  
 
 58  0
         Class[] intf = clazz.getInterfaces();
 59  
 
 60  0
         for (int i = 0; i < intf.length; i++) {
 61  0
             if (intf[i].getName().indexOf("util.List") > 0)
 62  0
                 return intf[i].getName();
 63  0
             else if (intf[i].getName().indexOf("Iterator") > 0)
 64  0
                 return intf[i].getName();
 65  
         }
 66  
 
 67  0
         if (clazz.getSuperclass() != null && clazz.getSuperclass().getInterfaces().length > 0)
 68  0
             return getClassName(clazz.getSuperclass());
 69  
 
 70  0
         return clazz.getName();
 71  
     }
 72  
 
 73  
     public Class getInterfaceClass(Class clazz)
 74  
     {
 75  29
         if (IRender.class.isAssignableFrom(clazz) || clazz.isInterface()
 76  
             || Modifier.isPublic(clazz.getModifiers()))
 77  29
             return clazz;
 78  
 
 79  0
         if (clazz.getName().equals("java.util.AbstractList$Itr"))
 80  0
             return Iterator.class;
 81  
 
 82  0
         if (Modifier.isPublic(clazz.getModifiers())
 83  
             && clazz.isInterface() || clazz.isPrimitive()) {
 84  
 
 85  0
             return clazz;
 86  
         }
 87  
 
 88  0
         Class[] intf = clazz.getInterfaces();
 89  
 
 90  0
         for (int i = 0; i < intf.length; i++) {
 91  
 
 92  0
             if (List.class.isAssignableFrom(intf[i]))
 93  0
                 return List.class;
 94  0
             else if (Iterator.class.isAssignableFrom(intf[i]))
 95  0
                 return Iterator.class;
 96  0
             else if (Map.class.isAssignableFrom(intf[i]))
 97  0
                 return Map.class;
 98  0
             else if (Set.class.isAssignableFrom(intf[i]))
 99  0
                 return Set.class;
 100  0
             else if (Collection.class.isAssignableFrom(intf[i]))
 101  0
                 return Collection.class;
 102  
         }
 103  
 
 104  0
         if (clazz.getSuperclass() != null && clazz.getSuperclass().getInterfaces().length > 0)
 105  0
             return getInterfaceClass(clazz.getSuperclass());
 106  
 
 107  0
         return clazz;
 108  
     }
 109  
 
 110  
     public Class getRootExpressionClass(Node rootNode, OgnlContext context)
 111  
     {
 112  15
         if (context.getRoot() == null)
 113  0
             return null;
 114  
 
 115  15
         Class ret = context.getRoot().getClass();
 116  
 
 117  15
         if (!IRender.class.isInstance(context.getRoot()) && context.getFirstAccessor() != null && context.getFirstAccessor().isInstance(context.getRoot()))
 118  15
             ret = context.getFirstAccessor();
 119  
 
 120  15
         return ret;
 121  
     }
 122  
 
 123  
     public void compileExpression(OgnlContext context, Node expression, Object root)
 124  
             throws Exception
 125  
     {
 126  13
         if (_log.isDebugEnabled())
 127  0
             _log.debug("Compiling expr class " + expression.getClass().getName() + " and root " + root.getClass().getName() + " with toString:" + expression.toString());
 128  
 
 129  13
         synchronized (expression) {
 130  
 
 131  13
             if (expression.getAccessor() != null)
 132  0
                 return;
 133  
 
 134  13
             String getBody = null;
 135  
             String setBody;
 136  
 
 137  13
             ClassFab classFab = _classFactory.newClass(ClassFabUtils.generateClassName(expression.getClass()), Object.class);
 138  13
             classFab.addInterface(ExpressionAccessor.class);
 139  
 
 140  13
             MethodSignature valueGetter = new MethodSignature(Object.class, "get", new Class[]{OgnlContext.class, Object.class}, null);
 141  13
             MethodSignature valueSetter = new MethodSignature(void.class, "set", new Class[]{OgnlContext.class, Object.class, Object.class}, null);
 142  
 
 143  13
             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  13
             if (root != null)
 148  13
                 Ognl.getValue(expression, context, root);
 149  
 
 150  
             try {
 151  
 
 152  13
                 getBody = generateGetter(context, classFab, valueGetter, expression, root);
 153  
 
 154  0
             } 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  0
                 return;
 160  0
             } catch (javassist.CannotCompileException e) {
 161  
 
 162  0
                 _log.error("Error generating OGNL getter for expression " + expression + " with root " + root + " and body:\n" + getBody, e);
 163  
 
 164  0
                 e.printStackTrace();
 165  
 
 166  0
                 generateFailSafe(context, expression, root);
 167  0
                 return;
 168  13
             }
 169  
 
 170  
             try {
 171  
 
 172  13
                 classFab.addMethod(Modifier.PUBLIC, valueGetter, getBody);
 173  
 
 174  0
             } catch (Throwable t) {
 175  
 
 176  0
                 _log.error("Error generating OGNL getter for expression " + expression + " with root " + root + " and body:\n" + getBody, t);
 177  
 
 178  0
                 t.printStackTrace();
 179  
 
 180  0
                 generateFailSafe(context, expression, root);
 181  0
                 return;
 182  13
             }
 183  
 
 184  
             try {
 185  
 
 186  13
                 setBody = generateSetter(context, classFab, valueSetter, expression, root);
 187  
 
 188  6
             } catch (UnsupportedCompilationException uc) {
 189  
                 
 190  
                 //_log.warn("Unsupported setter compilation caught: " + uc.getMessage() + " for expression: " + expression.toString(), uc);
 191  
 
 192  6
                 setBody = generateOgnlSetter(classFab, valueSetter);
 193  
 
 194  6
                 if (!classFab.containsMethod(expressionSetter)) {
 195  
 
 196  6
                     classFab.addField("_node", Node.class);
 197  6
                     classFab.addMethod(Modifier.PUBLIC, expressionSetter, "{ _node = $1; }");
 198  
                 }
 199  7
             }
 200  
 
 201  
             try {
 202  
 
 203  13
                 if (setBody == null) {
 204  0
                     setBody = generateOgnlSetter(classFab, valueSetter);
 205  
 
 206  0
                     if (!classFab.containsMethod(expressionSetter)) {
 207  
 
 208  0
                         classFab.addField("_node", Node.class);
 209  0
                         classFab.addMethod(Modifier.PUBLIC, expressionSetter, "{ _node = $1; }");
 210  
                     }
 211  
                 }
 212  
 
 213  13
                 if (setBody != null)
 214  13
                     classFab.addMethod(Modifier.PUBLIC, valueSetter, setBody);
 215  
 
 216  13
                 classFab.addConstructor(new Class[0], new Class[0], "{}");
 217  
 
 218  13
                 Class clazz = ((AbstractFab) classFab).createClass(true);
 219  
 
 220  13
                 expression.setAccessor((ExpressionAccessor) clazz.newInstance());
 221  
 
 222  0
             }  catch (Throwable t) {
 223  
 
 224  0
                 _log.error("Error generating OGNL statements for expression " + expression + " with root " + root, t);
 225  0
                 t.printStackTrace();
 226  
 
 227  0
                 generateFailSafe(context, expression, root);
 228  0
                 return;
 229  13
             }
 230  
 
 231  
             // need to set expression on node if the field was just defined.
 232  
 
 233  13
             if (classFab.containsMethod(expressionSetter)) {
 234  
 
 235  6
                 expression.getAccessor().setExpression(expression);
 236  
             }
 237  
 
 238  13
         }
 239  13
     }
 240  
 
 241  
     protected void generateFailSafe(OgnlContext context, Node expression, Object root)
 242  
     {
 243  0
         if (expression.getAccessor() != null)
 244  0
             return;
 245  
         
 246  
         try {
 247  0
             ClassFab classFab = _classFactory.newClass(expression.getClass().getName() + expression.hashCode() + "Accessor", Object.class);
 248  0
             classFab.addInterface(ExpressionAccessor.class);
 249  
 
 250  0
             MethodSignature valueGetter = new MethodSignature(Object.class, "get", new Class[]{OgnlContext.class, Object.class}, null);
 251  0
             MethodSignature valueSetter = new MethodSignature(void.class, "set", new Class[]{OgnlContext.class, Object.class, Object.class}, null);
 252  
 
 253  0
             MethodSignature expressionSetter = new MethodSignature(void.class, "setExpression", new Class[]{Node.class}, null);
 254  
 
 255  0
             if (!classFab.containsMethod(expressionSetter)) {
 256  
 
 257  0
                 classFab.addField("_node", Node.class);
 258  0
                 classFab.addMethod(Modifier.PUBLIC, expressionSetter, "{ _node = $1; }");
 259  
             }
 260  
 
 261  0
             classFab.addMethod(Modifier.PUBLIC, valueGetter, generateOgnlGetter(classFab, valueGetter));
 262  0
             classFab.addMethod(Modifier.PUBLIC, valueSetter, generateOgnlSetter(classFab, valueSetter));
 263  
             
 264  0
             classFab.addConstructor(new Class[0], new Class[0], "{}");
 265  
 
 266  0
             Class clazz = ((AbstractFab) classFab).createClass(true);
 267  
 
 268  0
             expression.setAccessor((ExpressionAccessor) clazz.newInstance());
 269  
 
 270  
             // need to set expression on node if the field was just defined.
 271  
 
 272  0
             if (classFab.containsMethod(expressionSetter)) {
 273  
 
 274  0
                 expression.getAccessor().setExpression(expression);
 275  
             }
 276  
 
 277  0
         } catch (Throwable t) {
 278  
             
 279  0
             t.printStackTrace();
 280  0
         }
 281  0
     }
 282  
 
 283  
     protected String generateGetter(OgnlContext context, ClassFab newClass, MethodSignature valueGetter, Node expression, Object root)
 284  
             throws Exception
 285  
     {
 286  13
         String pre = "";
 287  13
         String post = "";
 288  
         String body;
 289  
         String getterCode;
 290  
 
 291  13
         context.setRoot(root);
 292  13
         context.setCurrentObject(root);
 293  13
         context.remove(PRE_CAST);
 294  
         
 295  
         try {
 296  
 
 297  13
             getterCode = expression.toGetSourceString(context, root);
 298  0
         } catch (NullPointerException e) {
 299  0
             if (_log.isDebugEnabled())
 300  0
                 _log.warn("NullPointer caught compiling getter, may be normal ognl method artifact.", e);
 301  
 
 302  0
             throw new UnsupportedCompilationException("Statement threw nullpointer.");
 303  13
         }
 304  
 
 305  13
         if (getterCode == null || getterCode.trim().length() <= 0 && !ASTVarRef.class.isAssignableFrom(expression.getClass()))
 306  0
             getterCode = "null";
 307  
 
 308  13
         String castExpression = (String) context.get(PRE_CAST);
 309  
 
 310  13
         if (context.getCurrentType() == null
 311  
             || context.getCurrentType().isPrimitive()
 312  
             || Character.class.isAssignableFrom(context.getCurrentType())
 313  
             || Object.class == context.getCurrentType())
 314  
         {
 315  7
             pre = pre + " ($w) (";
 316  7
             post = post + ")";
 317  
         }
 318  
 
 319  13
         String rootExpr = !getterCode.equals("null") ? getRootExpression(expression, root, context) : "";
 320  
 
 321  13
         String noRoot = (String) context.remove("_noRoot");
 322  13
         if (noRoot != null)
 323  0
             rootExpr = "";
 324  
 
 325  13
         createLocalReferences(context, newClass, valueGetter.getParameterTypes());
 326  
 
 327  13
         if (OrderedReturn.class.isInstance(expression) && ((OrderedReturn) expression).getLastExpression() != null) {
 328  
 
 329  0
             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  13
             body = "{ return " + pre
 340  
                    + (castExpression != null ? castExpression : "")
 341  
                    + rootExpr
 342  
                    + getterCode
 343  
                    + post
 344  
                    + ";}";
 345  
         }
 346  
         
 347  13
         body = body.replaceAll("\\.\\.", ".");
 348  
 
 349  13
         if (_log.isDebugEnabled())
 350  0
             _log.debug("Getter Body: ===================================\n" + body);
 351  
 
 352  13
         return body;
 353  
     }
 354  
 
 355  
     void createLocalReferences(OgnlContext context, ClassFab classFab, Class[] params)
 356  
             throws CannotCompileException, NotFoundException
 357  
     {
 358  20
         context.remove(LOCAL_REFERENCE_COUNTER);
 359  
 
 360  20
         Map referenceMap = (Map) context.remove(LOCAL_REFERENCE_MAP);
 361  20
         if (referenceMap == null)
 362  17
             return;
 363  
 
 364  3
         Iterator it = referenceMap.keySet().iterator();
 365  
 
 366  6
         while (it.hasNext()) {
 367  
 
 368  3
             String key = (String) it.next();
 369  3
             LocalReference ref = (LocalReference) referenceMap.get(key);
 370  
 
 371  3
             String widener = ref.getType().isPrimitive() ? " " : " ($w) ";
 372  
 
 373  3
             String body = "{";
 374  3
             body += " return  " + widener + ref.getExpression() + ";";
 375  3
             body += "}";
 376  
 
 377  3
             body = body.replaceAll("\\.\\.", ".");
 378  
 
 379  3
             if (_log.isDebugEnabled())
 380  0
                 _log.debug("createLocalReferences() body is:\n" + body);
 381  
 
 382  3
             MethodSignature method = new MethodSignature(ref.getType(), ref.getName(), params, null);
 383  3
             classFab.addMethod(Modifier.PUBLIC, method, body);
 384  3
         }
 385  3
     }
 386  
 
 387  
     protected String generateSetter(OgnlContext context, ClassFab newClass, MethodSignature valueSetter, Node expression, Object root)
 388  
             throws Exception
 389  
     {
 390  13
         if (ExpressionNode.class.isInstance(expression)
 391  
             || ASTConst.class.isInstance(expression))
 392  1
             throw new UnsupportedCompilationException("Can't compile expression/constant setters.");
 393  
 
 394  12
         context.setRoot(root);
 395  12
         context.setCurrentObject(root);
 396  12
         context.remove(PRE_CAST);
 397  
 
 398  
         String body;
 399  
 
 400  12
         String setterCode = expression.toSetSourceString(context, root);
 401  12
         String castExpression = (String) context.get(PRE_CAST);
 402  
 
 403  12
         if (setterCode == null || setterCode.trim().length() < 1)
 404  5
             throw new UnsupportedCompilationException("Can't compile null setter body.");
 405  
 
 406  7
         if (root == null)
 407  0
             throw new UnsupportedCompilationException("Can't compile setters with a null root object.");
 408  
 
 409  7
         String pre = getRootExpression(expression, root, context);
 410  
 
 411  7
         String noRoot = (String) context.remove("_noRoot");
 412  7
         if (noRoot != null)
 413  0
             pre = "";
 414  
 
 415  7
         String setterValue = (String) context.remove("setterConversion");
 416  7
         if (setterValue == null)
 417  7
             setterValue = "";
 418  
 
 419  7
         createLocalReferences(context, newClass, valueSetter.getParameterTypes());
 420  
 
 421  7
         body = "{"
 422  
                + setterValue
 423  
                + (castExpression != null ? castExpression : "")
 424  
                + pre
 425  
                + setterCode + ";}";
 426  
 
 427  7
         body = body.replaceAll("\\.\\.", ".");
 428  
 
 429  7
         if (_log.isDebugEnabled())
 430  0
             _log.debug("Setter Body: ===================================\n" + body);
 431  
 
 432  7
         return body;
 433  
     }
 434  
 
 435  
     String generateOgnlGetter(ClassFab newClass, MethodSignature valueGetter)
 436  
             throws Exception
 437  
     {
 438  0
         return "{ return _node.getValue($1, $2); }";
 439  
     }
 440  
 
 441  
     String generateOgnlSetter(ClassFab newClass, MethodSignature valueSetter)
 442  
             throws Exception
 443  
     {
 444  6
         return "{ _node.setValue($1, $2, $3); }";
 445  
     }
 446  
 }