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    
024    import java.lang.reflect.Array;
025    import java.lang.reflect.Constructor;
026    import java.util.List;
027    
028    /**
029     * $Id: ASTCtor.java 1194869 2011-10-29 11:10:16Z mcucchiara $
030     * @author Luke Blanshard (blanshlu@netscape.net)
031     * @author Drew Davidson (drew@ognl.org)
032     */
033    public class ASTCtor
034        extends SimpleNode
035    {
036    
037        private String className;
038    
039        private boolean isArray;
040    
041        public ASTCtor( int id )
042        {
043            super( id );
044        }
045    
046        public ASTCtor( OgnlParser p, int id )
047        {
048            super( p, id );
049        }
050    
051        /** Called from parser action. */
052        void setClassName( String className )
053        {
054            this.className = className;
055        }
056    
057        /**
058         * Get the class name for this constructor.
059         *
060         * @return the class name.
061         * @since 4.0
062         */
063        String getClassName()
064        {
065            return className;
066        }
067    
068    
069        void setArray( boolean value )
070        {
071            isArray = value;
072        }
073    
074        public boolean isArray()
075        {
076            return isArray;
077        }
078    
079        protected Object getValueBody( OgnlContext context, Object source )
080            throws OgnlException
081        {
082            Object result, root = context.getRoot();
083            int count = jjtGetNumChildren();
084            Object[] args = OgnlRuntime.getObjectArrayPool().create( count );
085    
086            try
087            {
088                for ( int i = 0; i < count; ++i )
089                {
090                    args[i] = children[i].getValue( context, root );
091                }
092                if ( isArray )
093                {
094                    if ( args.length == 1 )
095                    {
096                        try
097                        {
098                            Class componentClass = OgnlRuntime.classForName( context, className );
099                            List sourceList = null;
100                            int size;
101    
102                            if ( args[0] instanceof List )
103                            {
104                                sourceList = (List) args[0];
105                                size = sourceList.size();
106                            }
107                            else
108                            {
109                                size = (int) OgnlOps.longValue( args[0] );
110                            }
111                            result = Array.newInstance( componentClass, size );
112                            if ( sourceList != null )
113                            {
114                                TypeConverter converter = context.getTypeConverter();
115    
116                                for ( int i = 0, icount = sourceList.size(); i < icount; i++ )
117                                {
118                                    Object o = sourceList.get( i );
119    
120                                    if ( ( o == null ) || componentClass.isInstance( o ) )
121                                    {
122                                        Array.set( result, i, o );
123                                    }
124                                    else
125                                    {
126                                        Array.set( result, i,
127                                                   converter.convertValue( context, null, null, null, o, componentClass ) );
128                                    }
129                                }
130                            }
131                        }
132                        catch ( ClassNotFoundException ex )
133                        {
134                            throw new OgnlException( "array component class '" + className + "' not found", ex );
135                        }
136                    }
137                    else
138                    {
139                        throw new OgnlException( "only expect array size or fixed initializer list" );
140                    }
141                }
142                else
143                {
144                    result = OgnlRuntime.callConstructor( context, className, args );
145                }
146    
147                return result;
148            }
149            finally
150            {
151                OgnlRuntime.getObjectArrayPool().recycle( args );
152            }
153        }
154    
155    
156        public String toGetSourceString( OgnlContext context, Object target )
157        {
158            String result = "new " + className;
159    
160            Class clazz = null;
161            Object ctorValue = null;
162            try
163            {
164    
165                clazz = OgnlRuntime.classForName( context, className );
166    
167                ctorValue = this.getValueBody( context, target );
168                context.setCurrentObject( ctorValue );
169    
170                if ( clazz != null && ctorValue != null )
171                {
172    
173                    context.setCurrentType( ctorValue.getClass() );
174                    context.setCurrentAccessor( ctorValue.getClass() );
175                }
176    
177                if ( isArray )
178                {
179                    context.put( "_ctorClass", clazz );
180                }
181            }
182            catch ( Throwable t )
183            {
184                throw OgnlOps.castToRuntime( t );
185            }
186    
187            try
188            {
189    
190                if ( isArray )
191                {
192                    if ( children[0] instanceof ASTConst )
193                    {
194    
195                        result = result + "[" + children[0].toGetSourceString( context, target ) + "]";
196                    }
197                    else if ( ASTProperty.class.isInstance( children[0] ) )
198                    {
199    
200                        result =
201                            result + "[" + ExpressionCompiler.getRootExpression( children[0], target, context )
202                                + children[0].toGetSourceString( context, target ) + "]";
203                    }
204                    else if ( ASTChain.class.isInstance( children[0] ) )
205                    {
206    
207                        result = result + "[" + children[0].toGetSourceString( context, target ) + "]";
208                    }
209                    else
210                    {
211    
212                        result = result + "[] " + children[0].toGetSourceString( context, target );
213                    }
214    
215                }
216                else
217                {
218                    result = result + "(";
219    
220                    if ( ( children != null ) && ( children.length > 0 ) )
221                    {
222    
223                        Object[] values = new Object[children.length];
224                        String[] expressions = new String[children.length];
225                        Class[] types = new Class[children.length];
226    
227                        // first populate arrays with child values
228    
229                        for ( int i = 0; i < children.length; i++ )
230                        {
231    
232                            Object objValue = children[i].getValue( context, context.getRoot() );
233                            String value = children[i].toGetSourceString( context, target );
234    
235                            if ( !ASTRootVarRef.class.isInstance( children[i] ) )
236                            {
237                                value = ExpressionCompiler.getRootExpression( children[i], target, context ) + value;
238                            }
239    
240                            String cast = "";
241                            if ( ExpressionCompiler.shouldCast( children[i] ) )
242                            {
243    
244                                cast = (String) context.remove( ExpressionCompiler.PRE_CAST );
245                            }
246                            if ( cast == null )
247                            {
248                                cast = "";
249                            }
250                            
251                            if ( !ASTConst.class.isInstance( children[i] ) )
252                            {
253                                value = cast + value;
254                            }
255                            
256                            values[i] = objValue;
257                            expressions[i] = value;
258                            types[i] = context.getCurrentType();
259                        }
260    
261                        // now try and find a matching constructor
262    
263                        Constructor[] cons = clazz.getConstructors();
264                        Constructor ctor = null;
265                        Class[] ctorParamTypes = null;
266    
267                        for ( int i = 0; i < cons.length; i++ )
268                        {
269                            Class[] ctorTypes = cons[i].getParameterTypes();
270    
271                            if ( OgnlRuntime.areArgsCompatible( values, ctorTypes )
272                                && ( ctor == null || OgnlRuntime.isMoreSpecific( ctorTypes, ctorParamTypes ) ) )
273                            {
274                                ctor = cons[i];
275                                ctorParamTypes = ctorTypes;
276                            }
277                        }
278    
279                        if ( ctor == null )
280                        {
281                            ctor =
282                                OgnlRuntime.getConvertedConstructorAndArgs( context, clazz,
283                                                                            OgnlRuntime.getConstructors( clazz ), values,
284                                                                            new Object[values.length] );
285                        }
286                        
287                        if ( ctor == null ) 
288                        {
289                            throw new NoSuchMethodException(
290                                "Unable to find constructor appropriate for arguments in class: " + clazz );
291                        }
292                        ctorParamTypes = ctor.getParameterTypes();
293    
294                        // now loop over child values again and build up the actual source string
295    
296                        for ( int i = 0; i < children.length; i++ )
297                        {
298                            if ( i > 0 )
299                            {
300                                result = result + ", ";
301                            }
302    
303                            String value = expressions[i];
304    
305                            if ( types[i].isPrimitive() )
306                            {
307    
308                                String literal = OgnlRuntime.getNumericLiteral( types[i] );
309                                if ( literal != null )
310                                {
311                                    value += literal;
312                                }
313                            }
314    
315                            if ( ctorParamTypes[i] != types[i] )
316                            {
317    
318                                if ( values[i] != null && !types[i].isPrimitive() && !values[i].getClass().isArray()
319                                    && !ASTConst.class.isInstance( children[i] ) )
320                                {
321    
322                                    value =
323                                        "(" + OgnlRuntime.getCompiler( context ).getInterfaceClass( values[i].getClass() ).getName()
324                                            + ")" + value;
325                                }
326                                else if ( !ASTConst.class.isInstance( children[i] )
327                                    || ( ASTConst.class.isInstance( children[i] ) && !types[i].isPrimitive() ) )
328                                {
329    
330                                    if ( !types[i].isArray() && types[i].isPrimitive() && !ctorParamTypes[i].isPrimitive() )
331                                    {
332                                        value =
333                                            "new "
334                                                + ExpressionCompiler.getCastString( 
335                                                    OgnlRuntime.getPrimitiveWrapperClass( types[i] ) )
336                                                + "(" + value + ")";
337                                    }
338                                    else
339                                    {
340                                        value = " ($w) " + value;
341                                    }
342                                }
343                            }
344    
345                            result += value;
346                        }
347    
348                    }
349                    result = result + ")";
350                }
351    
352                context.setCurrentType( ctorValue != null ? ctorValue.getClass() : clazz );
353                context.setCurrentAccessor( clazz );
354                context.setCurrentObject( ctorValue );
355    
356            }
357            catch ( Throwable t )
358            {
359                throw OgnlOps.castToRuntime( t );
360            }
361    
362            context.remove( "_ctorClass" );
363    
364            return result;
365        }
366    
367        public String toSetSourceString( OgnlContext context, Object target )
368        {
369            return "";
370        }
371    
372        public <R, P> R accept( NodeVisitor<? extends R, ? super P> visitor, P data )
373            throws OgnlException
374        {
375            return visitor.visit( this, data );
376        }
377    }