Coverage Report - org.apache.commons.ognl.enhance.ExpressionCompiler
 
Classes in this File Line Coverage Branch Coverage Complexity
ExpressionCompiler
88%
216/245
81%
219/270
9.45
 
 1  
 package org.apache.commons.ognl.enhance;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *   http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 20  
  */
 21  
 
 22  
 import javassist.CannotCompileException;
 23  
 import javassist.ClassPool;
 24  
 import javassist.CtClass;
 25  
 import javassist.CtField;
 26  
 import javassist.CtMethod;
 27  
 import javassist.CtNewConstructor;
 28  
 import javassist.CtNewMethod;
 29  
 import javassist.LoaderClassPath;
 30  
 import javassist.NotFoundException;
 31  
 import org.apache.commons.ognl.ASTAnd;
 32  
 import org.apache.commons.ognl.ASTChain;
 33  
 import org.apache.commons.ognl.ASTConst;
 34  
 import org.apache.commons.ognl.ASTCtor;
 35  
 import org.apache.commons.ognl.ASTList;
 36  
 import org.apache.commons.ognl.ASTMethod;
 37  
 import org.apache.commons.ognl.ASTOr;
 38  
 import org.apache.commons.ognl.ASTProperty;
 39  
 import org.apache.commons.ognl.ASTRootVarRef;
 40  
 import org.apache.commons.ognl.ASTStaticField;
 41  
 import org.apache.commons.ognl.ASTStaticMethod;
 42  
 import org.apache.commons.ognl.ASTThisVarRef;
 43  
 import org.apache.commons.ognl.ASTVarRef;
 44  
 import org.apache.commons.ognl.ClassResolver;
 45  
 import org.apache.commons.ognl.ExpressionNode;
 46  
 import org.apache.commons.ognl.Node;
 47  
 import org.apache.commons.ognl.OgnlContext;
 48  
 import org.apache.commons.ognl.OgnlRuntime;
 49  
 
 50  
 import java.lang.reflect.Method;
 51  
 import java.lang.reflect.Modifier;
 52  
 import java.util.Collection;
 53  
 import java.util.HashMap;
 54  
 import java.util.Iterator;
 55  
 import java.util.List;
 56  
 import java.util.Map;
 57  
 import java.util.Set;
 58  
 
 59  
 import static java.lang.String.format;
 60  
 
 61  
 /**
 62  
  * Responsible for managing/providing functionality related to compiling generated java source expressions via bytecode
 63  
  * enhancements for a given ognl expression.
 64  
  */
 65  62
 public class ExpressionCompiler
 66  
     implements OgnlExpressionCompiler
 67  
 {
 68  
 
 69  
     /**
 70  
      * Key used to store any java source string casting statements in the {@link OgnlContext} during class compilation.
 71  
      */
 72  
     public static final String PRE_CAST = "_preCast";
 73  
 
 74  
     /**
 75  
      * {@link ClassLoader} instances.
 76  
      */
 77  62
     protected Map<ClassResolver, EnhancedClassLoader> loaders = new HashMap<ClassResolver, EnhancedClassLoader>();
 78  
 
 79  
     /**
 80  
      * Javassist class definition poool.
 81  
      */
 82  
     protected ClassPool pool;
 83  
 
 84  62
     protected int classCounter = 0;
 85  
 
 86  
     /**
 87  
      * Used by {@link #castExpression(org.apache.commons.ognl.OgnlContext, org.apache.commons.ognl.Node, String)} to
 88  
      * store the cast java source string in to the current {@link org.apache.commons.ognl.OgnlContext}. This will either
 89  
      * add to the existing string present if it already exists or create a new instance and store it using the static
 90  
      * key of {@link #PRE_CAST}.
 91  
      * 
 92  
      * @param context The current execution context.
 93  
      * @param cast The java source string to store in to the context.
 94  
      */
 95  
     public static void addCastString( OgnlContext context, String cast )
 96  
     {
 97  196
         String value = (String) context.get( PRE_CAST );
 98  
 
 99  196
         if ( value != null )
 100  
         {
 101  18
             value = cast + value;
 102  
         }
 103  
         else
 104  
         {
 105  178
             value = cast;
 106  
         }
 107  
 
 108  196
         context.put( PRE_CAST, value );
 109  196
     }
 110  
 
 111  
     /**
 112  
      * Returns the appropriate casting expression (minus parens) for the specified class type.
 113  
      * <p/>
 114  
      * For instance, if given an {@link Integer} object the string <code>"java.lang.Integer"</code> would be returned.
 115  
      * For an array of primitive ints <code>"int[]"</code> and so on..
 116  
      * </p>
 117  
      * 
 118  
      * @param type The class to cast a string expression for.
 119  
      * @return The converted raw string version of the class name.
 120  
      */
 121  
     public static String getCastString( Class<?> type )
 122  
     {
 123  972
         if ( type == null )
 124  
         {
 125  0
             return null;
 126  
         }
 127  
 
 128  972
         return type.isArray() ? type.getComponentType().getName() + "[]" : type.getName();
 129  
     }
 130  
 
 131  
     /**
 132  
      * Convenience method called by many different property/method resolving AST types to get a root expression
 133  
      * resolving string for the given node. The callers are mostly ignorant and rely on this method to properly
 134  
      * determine if the expression should be cast at all and take the appropriate actions if it should.
 135  
      * 
 136  
      * @param expression The node to check and generate a root expression to if necessary.
 137  
      * @param root The root object for this execution.
 138  
      * @param context The current execution context.
 139  
      * @return Either an empty string or a root path java source string compatible with javassist compilations from the
 140  
      *         root object up to the specified {@link Node}.
 141  
      */
 142  
     public static String getRootExpression( Node expression, Object root, OgnlContext context )
 143  
     {
 144  1184
         String rootExpr = "";
 145  
 
 146  1184
         if ( !shouldCast( expression ) )
 147  
         {
 148  380
             return rootExpr;
 149  
         }
 150  
 
 151  804
         if ( ( !ASTList.class.isInstance( expression ) && !ASTVarRef.class.isInstance( expression )
 152  
             && !ASTStaticMethod.class.isInstance( expression ) && !ASTStaticField.class.isInstance( expression )
 153  
             && !ASTConst.class.isInstance( expression ) && !ExpressionNode.class.isInstance( expression )
 154  
             && !ASTCtor.class.isInstance( expression ) && !ASTStaticMethod.class.isInstance( expression )
 155  
             && root != null ) || ( root != null && ASTRootVarRef.class.isInstance( expression ) ) )
 156  
         {
 157  
 
 158  526
             Class<?> castClass = OgnlRuntime.getCompiler( context ).getRootExpressionClass( expression, context );
 159  
 
 160  526
             if ( castClass.isArray() || ASTRootVarRef.class.isInstance( expression ) || ASTThisVarRef.class.isInstance(
 161  
                 expression ) )
 162  
             {
 163  17
                 rootExpr = "((" + getCastString( castClass ) + ")$2)";
 164  
 
 165  17
                 if ( ASTProperty.class.isInstance( expression ) && !( (ASTProperty) expression ).isIndexedAccess() )
 166  
                 {
 167  2
                     rootExpr += ".";
 168  
                 }
 169  
             }
 170  509
             else if ( ( ASTProperty.class.isInstance( expression ) && ( (ASTProperty) expression ).isIndexedAccess() )
 171  
                 || ASTChain.class.isInstance( expression ) )
 172  
             {
 173  217
                 rootExpr = "((" + getCastString( castClass ) + ")$2)";
 174  
             }
 175  
             else
 176  
             {
 177  292
                 rootExpr = "((" + getCastString( castClass ) + ")$2).";
 178  
             }
 179  
         }
 180  
 
 181  804
         return rootExpr;
 182  
     }
 183  
 
 184  
     /**
 185  
      * Used by {@link #getRootExpression(org.apache.commons.ognl.Node, Object, org.apache.commons.ognl.OgnlContext)} to
 186  
      * determine if the expression needs to be cast at all.
 187  
      *
 188  
      * @param expression The node to check against.
 189  
      * @return Yes if the node type should be cast - false otherwise.
 190  
      */
 191  
     public static boolean shouldCast( Node expression )
 192  
     {
 193  1393
         if ( ASTChain.class.isInstance( expression ) )
 194  
         {
 195  249
             Node child = expression.jjtGetChild( 0 );
 196  249
             if ( ASTConst.class.isInstance( child ) || ASTStaticMethod.class.isInstance( child )
 197  
                 || ASTStaticField.class.isInstance( child ) || ( ASTVarRef.class.isInstance( child )
 198  
                 && !ASTRootVarRef.class.isInstance( child ) ) )
 199  
             {
 200  17
                 return false;
 201  
             }
 202  
         }
 203  
 
 204  1376
         return !ASTConst.class.isInstance( expression );
 205  
     }
 206  
 
 207  
     /**
 208  
      * {@inheritDoc}
 209  
      */
 210  
     public String castExpression( OgnlContext context, Node expression, String body )
 211  
     {
 212  
         //TODO: ok - so this looks really f-ed up ...and it is ..eh if you can do it better I'm all for it :)
 213  
 
 214  702
         if ( context.getCurrentAccessor() == null || context.getPreviousType() == null
 215  
             || context.getCurrentAccessor().isAssignableFrom( context.getPreviousType() ) || (
 216  
             context.getCurrentType() != null && context.getCurrentObject() != null
 217  
                 && context.getCurrentType().isAssignableFrom( context.getCurrentObject().getClass() )
 218  
                 && context.getCurrentAccessor().isAssignableFrom( context.getPreviousType() ) ) || body == null
 219  
             || body.trim().length() < 1 || ( context.getCurrentType() != null && context.getCurrentType().isArray() && (
 220  
             context.getPreviousType() == null || context.getPreviousType() != Object.class ) )
 221  
             || ASTOr.class.isInstance( expression ) || ASTAnd.class.isInstance( expression )
 222  
             || ASTRootVarRef.class.isInstance( expression ) || context.getCurrentAccessor() == Class.class || (
 223  
             context.get( ExpressionCompiler.PRE_CAST ) != null && ( (String) context.get(
 224  
                 ExpressionCompiler.PRE_CAST ) ).startsWith( "new" ) ) || ASTStaticField.class.isInstance( expression )
 225  
             || ASTStaticMethod.class.isInstance( expression ) || ( OrderedReturn.class.isInstance( expression )
 226  
             && ( (OrderedReturn) expression ).getLastExpression() != null ) )
 227  
         {
 228  520
             return body;
 229  
         }
 230  
 
 231  
         /*
 232  
          * System.out.println("castExpression() with expression " + expression + " expr class: " + expression.getClass()
 233  
          * + " currentType is: " + context.getCurrentType() + " previousType: " + context.getPreviousType() +
 234  
          * "\n current Accessor: " + context.getCurrentAccessor() + " previous Accessor: " +
 235  
          * context.getPreviousAccessor() + " current object " + context.getCurrentObject());
 236  
          */
 237  
 
 238  182
         ExpressionCompiler.addCastString( context,
 239  
                                           "((" + ExpressionCompiler.getCastString( context.getCurrentAccessor() )
 240  
                                               + ")" );
 241  
 
 242  182
         return ")" + body;
 243  
     }
 244  
 
 245  
     /**
 246  
      * {@inheritDoc}
 247  
      */
 248  
     public String getClassName( Class<?> clazz )
 249  
     {
 250  0
         if ( "java.util.AbstractList$Itr".equals( clazz.getName() ) )
 251  
         {
 252  0
             return Iterator.class.getName();
 253  
         }
 254  
 
 255  0
         if ( Modifier.isPublic( clazz.getModifiers() ) && clazz.isInterface() )
 256  
         {
 257  0
             return clazz.getName();
 258  
         }
 259  
 
 260  0
         Class<?>[] interfaces = clazz.getInterfaces();
 261  
 
 262  0
         for ( Class<?> intface : interfaces )
 263  
         {
 264  0
             if ( intface.getName().indexOf( "util.List" ) > 0 )
 265  
             {
 266  0
                 return intface.getName();
 267  
             }
 268  0
             else if ( intface.getName().indexOf( "Iterator" ) > 0 )
 269  
             {
 270  0
                 return intface.getName();
 271  
             }
 272  
         }
 273  
 
 274  0
         if ( clazz.getSuperclass() != null && clazz.getSuperclass().getInterfaces().length > 0 )
 275  
         {
 276  0
             return getClassName( clazz.getSuperclass() );
 277  
         }
 278  
 
 279  0
         return clazz.getName();
 280  
     }
 281  
 
 282  
     /**
 283  
      * {@inheritDoc}
 284  
      */
 285  
     public Class<?> getSuperOrInterfaceClass( Method m, Class<?> clazz )
 286  
     {
 287  1767
         if ( clazz.getInterfaces() != null && clazz.getInterfaces().length > 0 )
 288  
         {
 289  94
             Class<?>[] intfs = clazz.getInterfaces();
 290  
             Class<?> intClass;
 291  
 
 292  218
             for ( Class<?> intf : intfs )
 293  
             {
 294  165
                 intClass = getSuperOrInterfaceClass( m, intf );
 295  
 
 296  165
                 if ( intClass != null )
 297  
                 {
 298  41
                     return intClass;
 299  
                 }
 300  
 
 301  124
                 if ( Modifier.isPublic( intf.getModifiers() ) && containsMethod( m, intf ) )
 302  
                 {
 303  0
                     return intf;
 304  
                 }
 305  
             }
 306  
         }
 307  
 
 308  1726
         if ( clazz.getSuperclass() != null )
 309  
         {
 310  798
             Class<?> superClass = getSuperOrInterfaceClass( m, clazz.getSuperclass() );
 311  
 
 312  798
             if ( superClass != null )
 313  
             {
 314  9
                 return superClass;
 315  
             }
 316  
         }
 317  
 
 318  1717
         if ( Modifier.isPublic( clazz.getModifiers() ) && containsMethod( m, clazz ) )
 319  
         {
 320  804
             return clazz;
 321  
         }
 322  
 
 323  913
         return null;
 324  
     }
 325  
 
 326  
     /**
 327  
      * Helper utility method used by compiler to help resolve class->method mappings during method calls to
 328  
      * {@link OgnlExpressionCompiler#getSuperOrInterfaceClass(java.lang.reflect.Method, Class)}.
 329  
      * 
 330  
      * @param m The method to check for existance of.
 331  
      * @param clazz The class to check for the existance of a matching method definition to the method passed in.
 332  
      * @return True if the class contains the specified method, false otherwise.
 333  
      */
 334  
     public boolean containsMethod( Method m, Class<?> clazz )
 335  
     {
 336  1840
         Method[] methods = clazz.getMethods();
 337  
 
 338  1840
         if ( methods == null )
 339  
         {
 340  0
             return false;
 341  
         }
 342  
 
 343  19554
         for ( Method method : methods )
 344  
         {
 345  18518
             if ( method.getName().equals( m.getName() ) && method.getReturnType() == m.getReturnType() )
 346  
             {
 347  817
                 Class<?>[] parms = m.getParameterTypes();
 348  817
                 if ( parms == null )
 349  
                 {
 350  0
                     continue;
 351  
                 }
 352  
 
 353  817
                 Class<?>[] mparms = method.getParameterTypes();
 354  817
                 if ( mparms == null || mparms.length != parms.length )
 355  
                 {
 356  6
                     continue;
 357  
                 }
 358  
 
 359  811
                 boolean parmsMatch = true;
 360  1017
                 for ( int p = 0; p < parms.length; p++ )
 361  
                 {
 362  213
                     if ( parms[p] != mparms[p] )
 363  
                     {
 364  7
                         parmsMatch = false;
 365  7
                         break;
 366  
                     }
 367  
                 }
 368  
 
 369  811
                 if ( !parmsMatch )
 370  
                 {
 371  7
                     continue;
 372  
                 }
 373  
 
 374  804
                 Class<?>[] exceptions = m.getExceptionTypes();
 375  804
                 if ( exceptions == null )
 376  
                 {
 377  0
                     continue;
 378  
                 }
 379  
 
 380  804
                 Class<?>[] mexceptions = method.getExceptionTypes();
 381  804
                 if ( mexceptions == null || mexceptions.length != exceptions.length )
 382  
                 {
 383  0
                     continue;
 384  
                 }
 385  
 
 386  804
                 boolean exceptionsMatch = true;
 387  810
                 for ( int e = 0; e < exceptions.length; e++ )
 388  
                 {
 389  6
                     if ( exceptions[e] != mexceptions[e] )
 390  
                     {
 391  0
                         exceptionsMatch = false;
 392  0
                         break;
 393  
                     }
 394  
                 }
 395  
 
 396  804
                 if ( !exceptionsMatch )
 397  
                 {
 398  0
                     continue;
 399  
                 }
 400  
 
 401  804
                 return true;
 402  
             }
 403  
         }
 404  
 
 405  1036
         return false;
 406  
     }
 407  
 
 408  
     /**
 409  
      * {@inheritDoc}
 410  
      */
 411  
     public Class<?> getInterfaceClass( Class<?> clazz )
 412  
     {
 413  1241
         if ( "java.util.AbstractList$Itr".equals( clazz.getName() ) )
 414  
         {
 415  4
             return Iterator.class;
 416  
         }
 417  
 
 418  1237
         if ( Modifier.isPublic( clazz.getModifiers() ) && clazz.isInterface() || clazz.isPrimitive() )
 419  
         {
 420  0
             return clazz;
 421  
         }
 422  
 
 423  1237
         Class<?>[] intf = clazz.getInterfaces();
 424  
 
 425  1399
         for ( Class<?> anIntf : intf )
 426  
         {
 427  290
             if ( List.class.isAssignableFrom( anIntf ) )
 428  
             {
 429  21
                 return List.class;
 430  
             }
 431  269
             else if ( Iterator.class.isAssignableFrom( anIntf ) )
 432  
             {
 433  0
                 return Iterator.class;
 434  
             }
 435  269
             else if ( Map.class.isAssignableFrom( anIntf ) )
 436  
             {
 437  105
                 return Map.class;
 438  
             }
 439  164
             else if ( Set.class.isAssignableFrom( anIntf ) )
 440  
             {
 441  2
                 return Set.class;
 442  
             }
 443  162
             else if ( Collection.class.isAssignableFrom( anIntf ) )
 444  
             {
 445  0
                 return Collection.class;
 446  
             }
 447  
         }
 448  
 
 449  1109
         if ( clazz.getSuperclass() != null && clazz.getSuperclass().getInterfaces().length > 0 )
 450  
         {
 451  40
             return getInterfaceClass( clazz.getSuperclass() );
 452  
         }
 453  
 
 454  1069
         return clazz;
 455  
     }
 456  
 
 457  
     /**
 458  
      * {@inheritDoc}
 459  
      */
 460  
     public Class<?> getRootExpressionClass( Node rootNode, OgnlContext context )
 461  
     {
 462  526
         if ( context.getRoot() == null )
 463  
         {
 464  0
             return null;
 465  
         }
 466  
 
 467  526
         Class<?> ret = context.getRoot().getClass();
 468  
 
 469  526
         if ( context.getFirstAccessor() != null && context.getFirstAccessor().isInstance( context.getRoot() ) )
 470  
         {
 471  448
             ret = context.getFirstAccessor();
 472  
         }
 473  
 
 474  526
         return ret;
 475  
     }
 476  
 
 477  
     /**
 478  
      * {@inheritDoc}
 479  
      */
 480  
     public void compileExpression( OgnlContext context, Node expression, Object root )
 481  
         throws Exception
 482  
     {
 483  
         // System.out.println("Compiling expr class " + expression.getClass().getName() + " and root " + root);
 484  
 
 485  568
         if ( expression.getAccessor() != null )
 486  
         {
 487  0
             return;
 488  
         }
 489  
 
 490  
         String getBody, setBody;
 491  
 
 492  568
         EnhancedClassLoader loader = getClassLoader( context );
 493  568
         ClassPool classPool = getClassPool( context, loader );
 494  
 
 495  568
         CtClass newClass = classPool.makeClass(
 496  
             expression.getClass().getName() + expression.hashCode() + classCounter++ + "Accessor" );
 497  568
         newClass.addInterface( getCtClass( ExpressionAccessor.class ) );
 498  
 
 499  568
         CtClass ognlClass = getCtClass( OgnlContext.class );
 500  568
         CtClass objClass = getCtClass( Object.class );
 501  
 
 502  568
         CtMethod valueGetter = new CtMethod( objClass, "get", new CtClass[] { ognlClass, objClass }, newClass );
 503  568
         CtMethod valueSetter =
 504  
             new CtMethod( CtClass.voidType, "set", new CtClass[] { ognlClass, objClass, objClass }, newClass );
 505  
 
 506  568
         CtField nodeMember = null; // will only be set if uncompilable exception is thrown
 507  
 
 508  568
         CtClass nodeClass = getCtClass( Node.class );
 509  568
         CtMethod setExpression = null;
 510  
 
 511  
         try
 512  
         {
 513  
 
 514  568
             getBody = generateGetter( context, newClass, objClass, classPool, valueGetter, expression, root );
 515  
 
 516  
         }
 517  155
         catch ( UnsupportedCompilationException uc )
 518  
         {
 519  
             // uc.printStackTrace();
 520  
 
 521  155
             nodeMember = new CtField( nodeClass, "_node", newClass );
 522  155
             newClass.addField( nodeMember );
 523  
 
 524  155
             getBody = generateOgnlGetter( newClass, valueGetter, nodeMember );
 525  
 
 526  155
             setExpression = CtNewMethod.setter( "setExpression", nodeMember );
 527  155
             newClass.addMethod( setExpression );
 528  412
         }
 529  
 
 530  
         try
 531  
         {
 532  
 
 533  567
             setBody = generateSetter( context, newClass, objClass, classPool, valueSetter, expression, root );
 534  
 
 535  
         }
 536  438
         catch ( UnsupportedCompilationException uc )
 537  
         {
 538  
 
 539  
             // uc.printStackTrace();
 540  
 
 541  438
             if ( nodeMember == null )
 542  
             {
 543  285
                 nodeMember = new CtField( nodeClass, "_node", newClass );
 544  285
                 newClass.addField( nodeMember );
 545  
             }
 546  
 
 547  438
             setBody = generateOgnlSetter( newClass, valueSetter, nodeMember );
 548  
 
 549  438
             if ( setExpression == null )
 550  
             {
 551  285
                 setExpression = CtNewMethod.setter( "setExpression", nodeMember );
 552  285
                 newClass.addMethod( setExpression );
 553  
             }
 554  129
         }
 555  
 
 556  
         try
 557  
         {
 558  567
             newClass.addConstructor( CtNewConstructor.defaultConstructor( newClass ) );
 559  
 
 560  567
             Class<?> clazz = classPool.toClass( newClass );
 561  567
             newClass.detach();
 562  
 
 563  567
             expression.setAccessor( (ExpressionAccessor) clazz.newInstance() );
 564  
 
 565  
             // need to set expression on node if the field was just defined.
 566  
 
 567  567
             if ( nodeMember != null )
 568  
             {
 569  440
                 expression.getAccessor().setExpression( expression );
 570  
             }
 571  
 
 572  
         }
 573  0
         catch ( Throwable t )
 574  
         {
 575  
             // t.printStackTrace();
 576  
 
 577  0
             throw new RuntimeException( "Error compiling expression on object " + root + " with expression node "
 578  
                 + expression + " getter body: " + getBody + " setter body: " + setBody, t );
 579  567
         }
 580  
 
 581  567
     }
 582  
 
 583  
     protected String generateGetter( OgnlContext context, CtClass newClass, CtClass objClass, ClassPool classPool,
 584  
                                      CtMethod valueGetter, Node expression, Object root )
 585  
         throws Exception
 586  
     {
 587  568
         String pre = "";
 588  568
         String post = "";
 589  
         String body;
 590  
 
 591  568
         context.setRoot( root );
 592  
 
 593  
         // the ExpressionAccessor API has to reference the generic Object class for get/set operations, so this sets up
 594  
         // that known
 595  
         // type beforehand
 596  
 
 597  568
         context.remove( PRE_CAST );
 598  
 
 599  
         // Recursively generate the java source code representation of the top level expression
 600  
 
 601  568
         String getterCode = expression.toGetSourceString( context, root );
 602  
 
 603  412
         if ( getterCode == null || getterCode.trim().length() <= 0
 604  
             && !ASTVarRef.class.isAssignableFrom( expression.getClass() ) )
 605  
         {
 606  2
             getterCode = "null";
 607  
         }
 608  
 
 609  412
         String castExpression = (String) context.get( PRE_CAST );
 610  
 
 611  412
         if ( context.getCurrentType() == null || context.getCurrentType().isPrimitive()
 612  
             || Character.class.isAssignableFrom( context.getCurrentType() )
 613  
             || Object.class == context.getCurrentType() )
 614  
         {
 615  278
             pre = pre + " ($w) (";
 616  278
             post = post + ")";
 617  
         }
 618  
 
 619  412
         String rootExpr = !"null".equals( getterCode ) ? getRootExpression( expression, root, context ) : "";
 620  
 
 621  412
         String noRoot = (String) context.remove( "_noRoot" );
 622  412
         if ( noRoot != null )
 623  
         {
 624  11
             rootExpr = "";
 625  
         }
 626  
 
 627  412
         createLocalReferences( context, classPool, newClass, objClass, valueGetter.getParameterTypes() );
 628  
 
 629  412
         if ( OrderedReturn.class.isInstance( expression )
 630  
             && ( (OrderedReturn) expression ).getLastExpression() != null )
 631  
         {
 632  3
             body = "{ " + ( ASTMethod.class.isInstance( expression ) || ASTChain.class.isInstance( expression )
 633  
                 ? rootExpr
 634  
                 : "" ) + ( castExpression != null ? castExpression : "" )
 635  
                 + ( (OrderedReturn) expression ).getCoreExpression() + " return " + pre
 636  
                 + ( (OrderedReturn) expression ).getLastExpression() + post + ";}";
 637  
 
 638  
         }
 639  
         else
 640  
         {
 641  
 
 642  409
             body =
 643  
                 "{  return " + pre + ( castExpression != null ? castExpression : "" ) + rootExpr + getterCode + post
 644  
                     + ";}";
 645  
         }
 646  
 
 647  412
         body = body.replaceAll( "\\.\\.", "." );
 648  
 
 649  
         // System.out.println("Getter Body: ===================================\n" + body);
 650  412
         valueGetter.setBody( body );
 651  412
         newClass.addMethod( valueGetter );
 652  
 
 653  412
         return body;
 654  
     }
 655  
 
 656  
     /**
 657  
      * {@inheritDoc}
 658  
      */
 659  
     public String createLocalReference( OgnlContext context, String expression, Class<?> type )
 660  
     {
 661  149
         String referenceName = "ref" + context.incrementLocalReferenceCounter();
 662  149
         context.addLocalReference( referenceName, new LocalReferenceImpl( referenceName, expression, type ) );
 663  
 
 664  149
         String castString = "";
 665  149
         if ( !type.isPrimitive() )
 666  
         {
 667  107
             castString = "(" + ExpressionCompiler.getCastString( type ) + ") ";
 668  
         }
 669  
 
 670  149
         return castString + referenceName + "($$)";
 671  
     }
 672  
 
 673  
     void createLocalReferences( OgnlContext context, ClassPool classPool, CtClass clazz, CtClass unused,
 674  
                                 CtClass[] params )
 675  
         throws NotFoundException, CannotCompileException
 676  
     {
 677  541
         Map<String, LocalReference> referenceMap = context.getLocalReferences();
 678  541
         if ( referenceMap == null || referenceMap.isEmpty() )
 679  
         {
 680  450
             return;
 681  
         }
 682  
 
 683  91
         Iterator<LocalReference> it = referenceMap.values().iterator();
 684  237
         while( it.hasNext() )
 685  
         {
 686  146
             LocalReference ref = it.next();
 687  146
             String widener = ref.getType().isPrimitive() ? " " : " ($w) ";
 688  
 
 689  146
             String body = format( "{ return %s %s; }", widener, ref.getExpression() ).replaceAll( "\\.\\.", "." );
 690  
 
 691  
             // System.out.println("adding method " + ref.getName() + " with body:\n" + body + " and return type: " +
 692  
             // ref.getType());
 693  
 
 694  146
             CtMethod method =
 695  
                 new CtMethod( classPool.get( getCastString( ref.getType() ) ), ref.getName(), params, clazz );
 696  146
             method.setBody( body );
 697  
 
 698  146
             clazz.addMethod( method );
 699  146
             it.remove();
 700  146
         }
 701  91
     }
 702  
 
 703  
     protected String generateSetter( OgnlContext context, CtClass newClass, CtClass objClass, ClassPool classPool,
 704  
                                      CtMethod valueSetter, Node expression, Object root )
 705  
         throws Exception
 706  
     {
 707  567
         if ( ExpressionNode.class.isInstance( expression ) || ASTConst.class.isInstance( expression ) )
 708  
         {
 709  230
             throw new UnsupportedCompilationException( "Can't compile expression/constant setters." );
 710  
         }
 711  
 
 712  337
         context.setRoot( root );
 713  337
         context.remove( PRE_CAST );
 714  
 
 715  
         String body;
 716  
 
 717  337
         String setterCode = expression.toSetSourceString( context, root );
 718  177
         String castExpression = (String) context.get( PRE_CAST );
 719  
 
 720  177
         if ( setterCode == null || setterCode.trim().length() < 1 )
 721  
         {
 722  42
             throw new UnsupportedCompilationException( "Can't compile null setter body." );
 723  
         }
 724  
 
 725  135
         if ( root == null )
 726  
         {
 727  6
             throw new UnsupportedCompilationException( "Can't compile setters with a null root object." );
 728  
         }
 729  
 
 730  129
         String pre = getRootExpression( expression, root, context );
 731  
 
 732  129
         String noRoot = (String) context.remove( "_noRoot" );
 733  129
         if ( noRoot != null )
 734  
         {
 735  2
             pre = "";
 736  
         }
 737  
 
 738  129
         createLocalReferences( context, classPool, newClass, objClass, valueSetter.getParameterTypes() );
 739  
 
 740  129
         body = "{" + ( castExpression != null ? castExpression : "" ) + pre + setterCode + ";}";
 741  
 
 742  129
         body = body.replaceAll( "\\.\\.", "." );
 743  
 
 744  
         // System.out.println("Setter Body: ===================================\n" + body);
 745  
 
 746  129
         valueSetter.setBody( body );
 747  129
         newClass.addMethod( valueSetter );
 748  
 
 749  129
         return body;
 750  
     }
 751  
 
 752  
     /**
 753  
      * Fail safe getter creation when normal compilation fails.
 754  
      * 
 755  
      * @param clazz The javassist class the new method should be attached to.
 756  
      * @param valueGetter The method definition the generated code will be contained within.
 757  
      * @param node The root expression node.
 758  
      * @return The generated source string for this method, the method will still be added via the javassist API either
 759  
      *         way so this is really a convenience for exception reporting / debugging.
 760  
      * @throws Exception If a javassist error occurs.
 761  
      */
 762  
     protected String generateOgnlGetter( CtClass clazz, CtMethod valueGetter, CtField node )
 763  
         throws Exception
 764  
     {
 765  155
         String body = "return " + node.getName() + ".getValue($1, $2);";
 766  
 
 767  155
         valueGetter.setBody( body );
 768  155
         clazz.addMethod( valueGetter );
 769  
 
 770  155
         return body;
 771  
     }
 772  
 
 773  
     /**
 774  
      * Fail safe setter creation when normal compilation fails.
 775  
      * 
 776  
      * @param clazz The javassist class the new method should be attached to.
 777  
      * @param valueSetter The method definition the generated code will be contained within.
 778  
      * @param node The root expression node.
 779  
      * @return The generated source string for this method, the method will still be added via the javassist API either
 780  
      *         way so this is really a convenience for exception reporting / debugging.
 781  
      * @throws Exception If a javassist error occurs.
 782  
      */
 783  
     protected String generateOgnlSetter( CtClass clazz, CtMethod valueSetter, CtField node )
 784  
         throws Exception
 785  
     {
 786  438
         String body = node.getName() + ".setValue($1, $2, $3);";
 787  
 
 788  438
         valueSetter.setBody( body );
 789  438
         clazz.addMethod( valueSetter );
 790  
 
 791  438
         return body;
 792  
     }
 793  
 
 794  
     /**
 795  
      * Creates a {@link ClassLoader} instance compatible with the javassist classloader and normal OGNL class resolving
 796  
      * semantics.
 797  
      * 
 798  
      * @param context The current execution context.
 799  
      * @return The created {@link ClassLoader} instance.
 800  
      */
 801  
     protected EnhancedClassLoader getClassLoader( OgnlContext context )
 802  
     {
 803  568
         EnhancedClassLoader ret = loaders.get( context.getClassResolver() );
 804  
 
 805  568
         if ( ret != null )
 806  
         {
 807  561
             return ret;
 808  
         }
 809  
 
 810  7
         ClassLoader classLoader = new ContextClassLoader( OgnlContext.class.getClassLoader(), context );
 811  
 
 812  7
         ret = new EnhancedClassLoader( classLoader );
 813  7
         loaders.put( context.getClassResolver(), ret );
 814  
 
 815  7
         return ret;
 816  
     }
 817  
 
 818  
     /**
 819  
      * Loads a new class definition via javassist for the specified class.
 820  
      * 
 821  
      * @param searchClass The class to load.
 822  
      * @return The javassist class equivalent.
 823  
      * @throws javassist.NotFoundException When the class definition can't be found.
 824  
      */
 825  
     protected CtClass getCtClass( Class<?> searchClass )
 826  
         throws NotFoundException
 827  
     {
 828  2272
         return pool.get( searchClass.getName() );
 829  
     }
 830  
 
 831  
     /**
 832  
      * Gets either a new or existing {@link ClassPool} for use in compiling javassist classes. A new class path object
 833  
      * is inserted in to the returned {@link ClassPool} using the passed in <code>loader</code> instance if a new pool
 834  
      * needs to be created.
 835  
      * 
 836  
      * @param context The current execution context.
 837  
      * @param loader The {@link ClassLoader} instance to use - as returned by
 838  
      *            {@link #getClassLoader(org.apache.commons.ognl.OgnlContext)}.
 839  
      * @return The existing or new {@link ClassPool} instance.
 840  
      */
 841  
     protected ClassPool getClassPool( OgnlContext context, EnhancedClassLoader loader )
 842  
     {
 843  568
         if ( pool != null )
 844  
         {
 845  561
             return pool;
 846  
         }
 847  
 
 848  7
         pool = ClassPool.getDefault();
 849  7
         pool.insertClassPath( new LoaderClassPath( loader.getParent() ) );
 850  
 
 851  7
         return pool;
 852  
     }
 853  
 }