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 }