View Javadoc

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          super( id );
51      }
52  
53      public ASTMethod( OgnlParser p, int id )
54      {
55          super( p, id );
56      }
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          this.methodName = methodName;
66      }
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          return methodName;
76      }
77  
78      protected Object getValueBody( OgnlContext context, Object source )
79          throws OgnlException
80      {
81          Object[] args = OgnlRuntime.getObjectArrayPool().create( jjtGetNumChildren() );
82  
83          try
84          {
85              Object result, root = context.getRoot();
86  
87              for ( int i = 0; i < args.length; ++i )
88              {
89                  args[i] = children[i].getValue( context, root );
90              }
91  
92              result = OgnlRuntime.callMethod( context, source, methodName, args );
93  
94              if ( result == null )
95              {
96                  NullHandler nullHandler = OgnlRuntime.getNullHandler( OgnlRuntime.getTargetClass( source ) );
97                  result = nullHandler.nullMethodResult( context, source, methodName, args );
98              }
99  
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 }