Coverage Report - org.apache.commons.ognl.ASTMethod
 
Classes in this File Line Coverage Branch Coverage Complexity
ASTMethod
90%
128/142
74%
76/102
6.667
 
 1  
 package org.apache.commons.ognl;
 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 org.apache.commons.ognl.enhance.ExpressionCompiler;
 23  
 import org.apache.commons.ognl.enhance.OgnlExpressionCompiler;
 24  
 import org.apache.commons.ognl.enhance.OrderedReturn;
 25  
 import org.apache.commons.ognl.enhance.UnsupportedCompilationException;
 26  
 
 27  
 import java.lang.reflect.Method;
 28  
 
 29  
 /**
 30  
  * $Id: ASTMethod.java 1198659 2011-11-07 09:12:44Z mcucchiara $
 31  
  *
 32  
  * @author Luke Blanshard (blanshlu@netscape.net)
 33  
  * @author Drew Davidson (drew@ognl.org)
 34  
  */
 35  
 public class ASTMethod
 36  
     extends SimpleNode
 37  
     implements OrderedReturn, NodeType
 38  
 {
 39  
 
 40  
     private String methodName;
 41  
 
 42  
     private String lastExpression;
 43  
 
 44  
     private String coreExpression;
 45  
 
 46  
     private Class getterClass;
 47  
 
 48  
     public ASTMethod( int id )
 49  
     {
 50  192
         super( id );
 51  192
     }
 52  
 
 53  
     public ASTMethod( OgnlParser p, int id )
 54  
     {
 55  0
         super( p, id );
 56  0
     }
 57  
 
 58  
     /**
 59  
      * Called from parser action.
 60  
      *
 61  
      * @param methodName sets the name of the method
 62  
      */
 63  
     public void setMethodName( String methodName )
 64  
     {
 65  192
         this.methodName = methodName;
 66  192
     }
 67  
 
 68  
     /**
 69  
      * Returns the method name that this node will call.
 70  
      *
 71  
      * @return the method name
 72  
      */
 73  
     public String getMethodName()
 74  
     {
 75  0
         return methodName;
 76  
     }
 77  
 
 78  
     protected Object getValueBody( OgnlContext context, Object source )
 79  
         throws OgnlException
 80  
     {
 81  219
         Object[] args = OgnlRuntime.getObjectArrayPool().create( jjtGetNumChildren() );
 82  
 
 83  
         try
 84  
         {
 85  219
             Object result, root = context.getRoot();
 86  
 
 87  353
             for ( int i = 0; i < args.length; ++i )
 88  
             {
 89  134
                 args[i] = children[i].getValue( context, root );
 90  
             }
 91  
 
 92  219
             result = OgnlRuntime.callMethod( context, source, methodName, args );
 93  
 
 94  211
             if ( result == null )
 95  
             {
 96  24
                 NullHandler nullHandler = OgnlRuntime.getNullHandler( OgnlRuntime.getTargetClass( source ) );
 97  24
                 result = nullHandler.nullMethodResult( context, source, methodName, args );
 98  
             }
 99  
 
 100  211
             return result;
 101  
 
 102  
         }
 103  
         finally
 104  
         {
 105  211
             OgnlRuntime.getObjectArrayPool().recycle( args );
 106  
         }
 107  
     }
 108  
 
 109  
     public String getLastExpression()
 110  
     {
 111  176
         return lastExpression;
 112  
     }
 113  
 
 114  
     public String getCoreExpression()
 115  
     {
 116  1
         return coreExpression;
 117  
     }
 118  
 
 119  
     public Class getGetterClass()
 120  
     {
 121  131
         return getterClass;
 122  
     }
 123  
 
 124  
     public Class getSetterClass()
 125  
     {
 126  46
         return getterClass;
 127  
     }
 128  
 
 129  
     public String toGetSourceString( OgnlContext context, Object target )
 130  
     {
 131  
         /*
 132  
          * System.out.println("methodName is " + methodName + " for target " + target + " target class: " + (target !=
 133  
          * null ? target.getClass() : null) + " current type: " + context.getCurrentType());
 134  
          */
 135  109
         if ( target == null )
 136  
         {
 137  1
             throw new UnsupportedCompilationException( "Target object is null." );
 138  
         }
 139  
 
 140  108
         String post = "";
 141  
         StringBuilder sourceStringBuilder;
 142  
         Method method;
 143  
 
 144  108
         OgnlExpressionCompiler compiler = OgnlRuntime.getCompiler( context );
 145  
         try
 146  
         {
 147  
 
 148  108
             method = OgnlRuntime.getMethod( context, context.getCurrentType() != null
 149  
                 ? context.getCurrentType()
 150  
                 : target.getClass(), methodName, children, false );
 151  108
             if ( method == null )
 152  
             {
 153  35
                 method = OgnlRuntime.getReadMethod( target.getClass(), methodName,
 154  
                                                     children != null ? children.length : -1 );
 155  
             }
 156  
 
 157  108
             if ( method == null )
 158  
             {
 159  2
                 method = OgnlRuntime.getWriteMethod( target.getClass(), methodName,
 160  
                                                      children != null ? children.length : -1 );
 161  
 
 162  2
                 if ( method != null )
 163  
                 {
 164  
 
 165  2
                     context.setCurrentType( method.getReturnType() );
 166  2
                     context.setCurrentAccessor(
 167  
                         compiler.getSuperOrInterfaceClass( method, method.getDeclaringClass() ) );
 168  
 
 169  2
                     coreExpression = toSetSourceString( context, target );
 170  0
                     if ( coreExpression == null || coreExpression.length() < 1 )
 171  
                     {
 172  0
                         throw new UnsupportedCompilationException( "can't find suitable getter method" );
 173  
                     }
 174  
 
 175  0
                     coreExpression += ";";
 176  0
                     lastExpression = "null";
 177  
 
 178  0
                     return coreExpression;
 179  
                 }
 180  
 
 181  0
                 return "";
 182  
             }
 183  
             else
 184  
             {
 185  106
                 getterClass = method.getReturnType();
 186  
             }
 187  
 
 188  
             // TODO: This is a hacky workaround until javassist supports varargs method invocations
 189  106
             boolean varArgs = method.isVarArgs();
 190  
 
 191  106
             if ( varArgs )
 192  
             {
 193  2
                 throw new UnsupportedCompilationException(
 194  
                     "Javassist does not currently support varargs method calls" );
 195  
             }
 196  
 
 197  104
             sourceStringBuilder = new StringBuilder().append( "." ).append( method.getName() ).append( "(" );
 198  
 
 199  104
             if ( ( children != null ) && ( children.length > 0 ) )
 200  
             {
 201  53
                 Class[] parms = method.getParameterTypes();
 202  53
                 String prevCast = (String) context.remove( ExpressionCompiler.PRE_CAST );
 203  
                 /*
 204  
                  * System.out.println("before children methodName is " + methodName + " for target " + target +
 205  
                  * " target class: " + (target != null ? target.getClass() : null) + " current type: " +
 206  
                  * context.getCurrentType() + " and previous type: " + context.getPreviousType());
 207  
                  */
 208  
 
 209  116
                 for ( int i = 0; i < children.length; i++ )
 210  
                 {
 211  63
                     if ( i > 0 )
 212  
                     {
 213  10
                         sourceStringBuilder.append( ", " );
 214  
                     }
 215  
 
 216  63
                     Class prevType = context.getCurrentType();
 217  
 
 218  63
                     Object root = context.getRoot();
 219  63
                     context.setCurrentObject( root );
 220  63
                     context.setCurrentType( root != null ? root.getClass() : null );
 221  63
                     context.setCurrentAccessor( null );
 222  63
                     context.setPreviousType( null );
 223  
 
 224  63
                     Node child = children[i];
 225  
 
 226  63
                     String parmString = ASTMethodUtil.getParmString( context, root, child, prevType );
 227  
 
 228  63
                     Class valueClass = ASTMethodUtil.getValueClass( context, root, child );
 229  
 
 230  63
                     if ( ( !varArgs || varArgs && ( i + 1 ) < parms.length ) && valueClass != parms[i] )
 231  
                     {
 232  24
                         parmString = ASTMethodUtil.getParmString( context, parms[i], parmString, child, valueClass,
 233  
                                                                   ".class, true)" );
 234  
                     }
 235  
 
 236  63
                     sourceStringBuilder.append( parmString );
 237  
                 }
 238  
 
 239  53
                 if ( prevCast != null )
 240  
                 {
 241  1
                     context.put( ExpressionCompiler.PRE_CAST, prevCast );
 242  
                 }
 243  
             }
 244  
 
 245  
         }
 246  4
         catch ( Throwable t )
 247  
         {
 248  4
             throw OgnlOps.castToRuntime( t );
 249  104
         }
 250  
 
 251  
         try
 252  
         {
 253  104
             Object contextObj = getValueBody( context, target );
 254  103
             context.setCurrentObject( contextObj );
 255  
         }
 256  1
         catch ( Throwable t )
 257  
         {
 258  1
             throw OgnlOps.castToRuntime( t );
 259  103
         }
 260  
 
 261  103
         sourceStringBuilder.append( ")" ).append( post );
 262  
 
 263  103
         if ( method.getReturnType() == void.class )
 264  
         {
 265  1
             coreExpression = sourceStringBuilder.toString() + ";";
 266  1
             lastExpression = "null";
 267  
         }
 268  
 
 269  103
         context.setCurrentType( method.getReturnType() );
 270  103
         context.setCurrentAccessor( compiler.getSuperOrInterfaceClass( method, method.getDeclaringClass() ) );
 271  
 
 272  103
         return sourceStringBuilder.toString();
 273  
     }
 274  
 
 275  
     public String toSetSourceString( OgnlContext context, Object target )
 276  
     {
 277  
         /*
 278  
          * System.out.println("current type: " + context.getCurrentType() + " target:" + target + " " +
 279  
          * context.getCurrentObject() + " last child? " + lastChild(context));
 280  
          */
 281  52
         Method method =
 282  
             OgnlRuntime.getWriteMethod( context.getCurrentType() != null ? context.getCurrentType() : target.getClass(),
 283  
                                         methodName, children != null ? children.length : -1 );
 284  52
         if ( method == null )
 285  
         {
 286  31
             throw new UnsupportedCompilationException(
 287  
                 "Unable to determine setter method generation for " + methodName );
 288  
         }
 289  
 
 290  21
         String post = "";
 291  21
         String result = "." + method.getName() + "(";
 292  
 
 293  21
         if ( method.getReturnType() != void.class && method.getReturnType().isPrimitive() && ( parent == null
 294  
             || !ASTTest.class.isInstance( parent ) ) )
 295  
         {
 296  11
             Class wrapper = OgnlRuntime.getPrimitiveWrapperClass( method.getReturnType() );
 297  
 
 298  11
             ExpressionCompiler.addCastString( context, "new " + wrapper.getName() + "(" );
 299  11
             post = ")";
 300  11
             getterClass = wrapper;
 301  
         }
 302  
 
 303  21
         boolean varArgs = method.isVarArgs();
 304  
 
 305  21
         if ( varArgs )
 306  
         {
 307  0
             throw new UnsupportedCompilationException( "Javassist does not currently support varargs method calls" );
 308  
         }
 309  
 
 310  21
         OgnlExpressionCompiler compiler = OgnlRuntime.getCompiler( context );
 311  
         try
 312  
         {
 313  
             /*
 314  
              * if (lastChild(context) && method.getParameterTypes().length > 0 && _children.length <= 0) throw new
 315  
              * UnsupportedCompilationException("Unable to determine setter method generation for " + method);
 316  
              */
 317  
 
 318  21
             if ( ( children != null ) && ( children.length > 0 ) )
 319  
             {
 320  16
                 Class[] parms = method.getParameterTypes();
 321  16
                 String prevCast = (String) context.remove( ExpressionCompiler.PRE_CAST );
 322  
 
 323  36
                 for ( int i = 0; i < children.length; i++ )
 324  
                 {
 325  25
                     if ( i > 0 )
 326  
                     {
 327  9
                         result += ", ";
 328  
                     }
 329  
 
 330  25
                     Class prevType = context.getCurrentType();
 331  
 
 332  25
                     context.setCurrentObject( context.getRoot() );
 333  25
                     context.setCurrentType( context.getRoot() != null ? context.getRoot().getClass() : null );
 334  25
                     context.setCurrentAccessor( null );
 335  25
                     context.setPreviousType( null );
 336  
 
 337  25
                     Node child = children[i];
 338  25
                     Object value = child.getValue( context, context.getRoot() );
 339  25
                     String parmString = child.toSetSourceString( context, context.getRoot() );
 340  
 
 341  20
                     if ( context.getCurrentType() == Void.TYPE || context.getCurrentType() == void.class )
 342  
                     {
 343  0
                         throw new UnsupportedCompilationException( "Method argument can't be a void type." );
 344  
                     }
 345  
 
 346  20
                     if ( parmString == null || parmString.trim().length() < 1 )
 347  
                     {
 348  3
                         if ( ASTProperty.class.isInstance( child ) || ASTMethod.class.isInstance( child )
 349  
                             || ASTStaticMethod.class.isInstance( child ) || ASTChain.class.isInstance( child ) )
 350  
                         {
 351  0
                             throw new UnsupportedCompilationException(
 352  
                                 "ASTMethod setter child returned null from a sub property expression." );
 353  
                         }
 354  3
                         parmString = "null";
 355  
                     }
 356  
 
 357  
                     // to undo type setting of constants when used as method parameters
 358  20
                     if ( ASTConst.class.isInstance( child ) )
 359  
                     {
 360  13
                         context.setCurrentType( prevType );
 361  
                     }
 362  
 
 363  20
                     parmString = ExpressionCompiler.getRootExpression( child, context.getRoot(), context ) + parmString;
 364  
 
 365  20
                     String cast = "";
 366  20
                     if ( ExpressionCompiler.shouldCast( child ) )
 367  
                     {
 368  7
                         cast = (String) context.remove( ExpressionCompiler.PRE_CAST );
 369  
                     }
 370  
 
 371  20
                     if ( cast == null )
 372  
                     {
 373  7
                         cast = "";
 374  
                     }
 375  
 
 376  20
                     parmString = cast + parmString;
 377  
 
 378  20
                     Class valueClass = value != null ? value.getClass() : null;
 379  20
                     if ( NodeType.class.isAssignableFrom( child.getClass() ) )
 380  
                     {
 381  18
                         valueClass = ( (NodeType) child ).getGetterClass();
 382  
                     }
 383  
 
 384  20
                     if ( valueClass != parms[i] )
 385  
                     {
 386  10
                         parmString =
 387  
                             ASTMethodUtil.getParmString( context, parms[i], parmString, child, valueClass, ".class)" );
 388  
                     }
 389  
 
 390  20
                     result += parmString;
 391  
                 }
 392  
 
 393  11
                 if ( prevCast != null )
 394  
                 {
 395  4
                     context.put( ExpressionCompiler.PRE_CAST, prevCast );
 396  
                 }
 397  
             }
 398  
 
 399  
         }
 400  5
         catch ( Throwable t )
 401  
         {
 402  5
             throw OgnlOps.castToRuntime( t );
 403  16
         }
 404  
 
 405  
         try
 406  
         {
 407  16
             Object contextObj = getValueBody( context, target );
 408  16
             context.setCurrentObject( contextObj );
 409  
         }
 410  0
         catch ( Throwable t )
 411  
         {
 412  
             // ignore
 413  16
         }
 414  
 
 415  16
         context.setCurrentType( method.getReturnType() );
 416  16
         context.setCurrentAccessor( compiler.getSuperOrInterfaceClass( method, method.getDeclaringClass() ) );
 417  
 
 418  16
         return result + ")" + post;
 419  
     }
 420  
 
 421  
     public <R, P> R accept( NodeVisitor<? extends R, ? super P> visitor, P data )
 422  
         throws OgnlException
 423  
     {
 424  0
         return visitor.visit( this, data );
 425  
     }
 426  
 }