001 package org.apache.commons.ognl; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022 import org.apache.commons.ognl.enhance.ExpressionCompiler; 023 import org.apache.commons.ognl.enhance.OgnlExpressionCompiler; 024 import org.apache.commons.ognl.enhance.OrderedReturn; 025 import org.apache.commons.ognl.enhance.UnsupportedCompilationException; 026 027 import java.lang.reflect.Method; 028 029 /** 030 * $Id: ASTMethod.java 1198659 2011-11-07 09:12:44Z mcucchiara $ 031 * 032 * @author Luke Blanshard (blanshlu@netscape.net) 033 * @author Drew Davidson (drew@ognl.org) 034 */ 035 public class ASTMethod 036 extends SimpleNode 037 implements OrderedReturn, NodeType 038 { 039 040 private String methodName; 041 042 private String lastExpression; 043 044 private String coreExpression; 045 046 private Class getterClass; 047 048 public ASTMethod( int id ) 049 { 050 super( id ); 051 } 052 053 public ASTMethod( OgnlParser p, int id ) 054 { 055 super( p, id ); 056 } 057 058 /** 059 * Called from parser action. 060 * 061 * @param methodName sets the name of the method 062 */ 063 public void setMethodName( String methodName ) 064 { 065 this.methodName = methodName; 066 } 067 068 /** 069 * Returns the method name that this node will call. 070 * 071 * @return the method name 072 */ 073 public String getMethodName() 074 { 075 return methodName; 076 } 077 078 protected Object getValueBody( OgnlContext context, Object source ) 079 throws OgnlException 080 { 081 Object[] args = OgnlRuntime.getObjectArrayPool().create( jjtGetNumChildren() ); 082 083 try 084 { 085 Object result, root = context.getRoot(); 086 087 for ( int i = 0; i < args.length; ++i ) 088 { 089 args[i] = children[i].getValue( context, root ); 090 } 091 092 result = OgnlRuntime.callMethod( context, source, methodName, args ); 093 094 if ( result == null ) 095 { 096 NullHandler nullHandler = OgnlRuntime.getNullHandler( OgnlRuntime.getTargetClass( source ) ); 097 result = nullHandler.nullMethodResult( context, source, methodName, args ); 098 } 099 100 return result; 101 102 } 103 finally 104 { 105 OgnlRuntime.getObjectArrayPool().recycle( args ); 106 } 107 } 108 109 public String getLastExpression() 110 { 111 return lastExpression; 112 } 113 114 public String getCoreExpression() 115 { 116 return coreExpression; 117 } 118 119 public Class getGetterClass() 120 { 121 return getterClass; 122 } 123 124 public Class getSetterClass() 125 { 126 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 if ( target == null ) 136 { 137 throw new UnsupportedCompilationException( "Target object is null." ); 138 } 139 140 String post = ""; 141 StringBuilder sourceStringBuilder; 142 Method method; 143 144 OgnlExpressionCompiler compiler = OgnlRuntime.getCompiler( context ); 145 try 146 { 147 148 method = OgnlRuntime.getMethod( context, context.getCurrentType() != null 149 ? context.getCurrentType() 150 : target.getClass(), methodName, children, false ); 151 if ( method == null ) 152 { 153 method = OgnlRuntime.getReadMethod( target.getClass(), methodName, 154 children != null ? children.length : -1 ); 155 } 156 157 if ( method == null ) 158 { 159 method = OgnlRuntime.getWriteMethod( target.getClass(), methodName, 160 children != null ? children.length : -1 ); 161 162 if ( method != null ) 163 { 164 165 context.setCurrentType( method.getReturnType() ); 166 context.setCurrentAccessor( 167 compiler.getSuperOrInterfaceClass( method, method.getDeclaringClass() ) ); 168 169 coreExpression = toSetSourceString( context, target ); 170 if ( coreExpression == null || coreExpression.length() < 1 ) 171 { 172 throw new UnsupportedCompilationException( "can't find suitable getter method" ); 173 } 174 175 coreExpression += ";"; 176 lastExpression = "null"; 177 178 return coreExpression; 179 } 180 181 return ""; 182 } 183 else 184 { 185 getterClass = method.getReturnType(); 186 } 187 188 // TODO: This is a hacky workaround until javassist supports varargs method invocations 189 boolean varArgs = method.isVarArgs(); 190 191 if ( varArgs ) 192 { 193 throw new UnsupportedCompilationException( 194 "Javassist does not currently support varargs method calls" ); 195 } 196 197 sourceStringBuilder = new StringBuilder().append( "." ).append( method.getName() ).append( "(" ); 198 199 if ( ( children != null ) && ( children.length > 0 ) ) 200 { 201 Class[] parms = method.getParameterTypes(); 202 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 for ( int i = 0; i < children.length; i++ ) 210 { 211 if ( i > 0 ) 212 { 213 sourceStringBuilder.append( ", " ); 214 } 215 216 Class prevType = context.getCurrentType(); 217 218 Object root = context.getRoot(); 219 context.setCurrentObject( root ); 220 context.setCurrentType( root != null ? root.getClass() : null ); 221 context.setCurrentAccessor( null ); 222 context.setPreviousType( null ); 223 224 Node child = children[i]; 225 226 String parmString = ASTMethodUtil.getParmString( context, root, child, prevType ); 227 228 Class valueClass = ASTMethodUtil.getValueClass( context, root, child ); 229 230 if ( ( !varArgs || varArgs && ( i + 1 ) < parms.length ) && valueClass != parms[i] ) 231 { 232 parmString = ASTMethodUtil.getParmString( context, parms[i], parmString, child, valueClass, 233 ".class, true)" ); 234 } 235 236 sourceStringBuilder.append( parmString ); 237 } 238 239 if ( prevCast != null ) 240 { 241 context.put( ExpressionCompiler.PRE_CAST, prevCast ); 242 } 243 } 244 245 } 246 catch ( Throwable t ) 247 { 248 throw OgnlOps.castToRuntime( t ); 249 } 250 251 try 252 { 253 Object contextObj = getValueBody( context, target ); 254 context.setCurrentObject( contextObj ); 255 } 256 catch ( Throwable t ) 257 { 258 throw OgnlOps.castToRuntime( t ); 259 } 260 261 sourceStringBuilder.append( ")" ).append( post ); 262 263 if ( method.getReturnType() == void.class ) 264 { 265 coreExpression = sourceStringBuilder.toString() + ";"; 266 lastExpression = "null"; 267 } 268 269 context.setCurrentType( method.getReturnType() ); 270 context.setCurrentAccessor( compiler.getSuperOrInterfaceClass( method, method.getDeclaringClass() ) ); 271 272 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 Method method = 282 OgnlRuntime.getWriteMethod( context.getCurrentType() != null ? context.getCurrentType() : target.getClass(), 283 methodName, children != null ? children.length : -1 ); 284 if ( method == null ) 285 { 286 throw new UnsupportedCompilationException( 287 "Unable to determine setter method generation for " + methodName ); 288 } 289 290 String post = ""; 291 String result = "." + method.getName() + "("; 292 293 if ( method.getReturnType() != void.class && method.getReturnType().isPrimitive() && ( parent == null 294 || !ASTTest.class.isInstance( parent ) ) ) 295 { 296 Class wrapper = OgnlRuntime.getPrimitiveWrapperClass( method.getReturnType() ); 297 298 ExpressionCompiler.addCastString( context, "new " + wrapper.getName() + "(" ); 299 post = ")"; 300 getterClass = wrapper; 301 } 302 303 boolean varArgs = method.isVarArgs(); 304 305 if ( varArgs ) 306 { 307 throw new UnsupportedCompilationException( "Javassist does not currently support varargs method calls" ); 308 } 309 310 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 if ( ( children != null ) && ( children.length > 0 ) ) 319 { 320 Class[] parms = method.getParameterTypes(); 321 String prevCast = (String) context.remove( ExpressionCompiler.PRE_CAST ); 322 323 for ( int i = 0; i < children.length; i++ ) 324 { 325 if ( i > 0 ) 326 { 327 result += ", "; 328 } 329 330 Class prevType = context.getCurrentType(); 331 332 context.setCurrentObject( context.getRoot() ); 333 context.setCurrentType( context.getRoot() != null ? context.getRoot().getClass() : null ); 334 context.setCurrentAccessor( null ); 335 context.setPreviousType( null ); 336 337 Node child = children[i]; 338 Object value = child.getValue( context, context.getRoot() ); 339 String parmString = child.toSetSourceString( context, context.getRoot() ); 340 341 if ( context.getCurrentType() == Void.TYPE || context.getCurrentType() == void.class ) 342 { 343 throw new UnsupportedCompilationException( "Method argument can't be a void type." ); 344 } 345 346 if ( parmString == null || parmString.trim().length() < 1 ) 347 { 348 if ( ASTProperty.class.isInstance( child ) || ASTMethod.class.isInstance( child ) 349 || ASTStaticMethod.class.isInstance( child ) || ASTChain.class.isInstance( child ) ) 350 { 351 throw new UnsupportedCompilationException( 352 "ASTMethod setter child returned null from a sub property expression." ); 353 } 354 parmString = "null"; 355 } 356 357 // to undo type setting of constants when used as method parameters 358 if ( ASTConst.class.isInstance( child ) ) 359 { 360 context.setCurrentType( prevType ); 361 } 362 363 parmString = ExpressionCompiler.getRootExpression( child, context.getRoot(), context ) + parmString; 364 365 String cast = ""; 366 if ( ExpressionCompiler.shouldCast( child ) ) 367 { 368 cast = (String) context.remove( ExpressionCompiler.PRE_CAST ); 369 } 370 371 if ( cast == null ) 372 { 373 cast = ""; 374 } 375 376 parmString = cast + parmString; 377 378 Class valueClass = value != null ? value.getClass() : null; 379 if ( NodeType.class.isAssignableFrom( child.getClass() ) ) 380 { 381 valueClass = ( (NodeType) child ).getGetterClass(); 382 } 383 384 if ( valueClass != parms[i] ) 385 { 386 parmString = 387 ASTMethodUtil.getParmString( context, parms[i], parmString, child, valueClass, ".class)" ); 388 } 389 390 result += parmString; 391 } 392 393 if ( prevCast != null ) 394 { 395 context.put( ExpressionCompiler.PRE_CAST, prevCast ); 396 } 397 } 398 399 } 400 catch ( Throwable t ) 401 { 402 throw OgnlOps.castToRuntime( t ); 403 } 404 405 try 406 { 407 Object contextObj = getValueBody( context, target ); 408 context.setCurrentObject( contextObj ); 409 } 410 catch ( Throwable t ) 411 { 412 // ignore 413 } 414 415 context.setCurrentType( method.getReturnType() ); 416 context.setCurrentAccessor( compiler.getSuperOrInterfaceClass( method, method.getDeclaringClass() ) ); 417 418 return result + ")" + post; 419 } 420 421 public <R, P> R accept( NodeVisitor<? extends R, ? super P> visitor, P data ) 422 throws OgnlException 423 { 424 return visitor.visit( this, data ); 425 } 426 }