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    }