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.UnsupportedCompilationException;
025    
026    import java.beans.IntrospectionException;
027    import java.lang.reflect.Field;
028    import java.lang.reflect.Method;
029    import java.util.Map;
030    
031    /**
032     * Implementation of PropertyAccessor that uses reflection on the target object's class to find a field or a pair of
033     * set/get methods with the given property name.
034     * 
035     * @author Luke Blanshard (blanshlu@netscape.net)
036     * @author Drew Davidson (drew@ognl.org)
037     */
038    public class ObjectPropertyAccessor
039        implements PropertyAccessor
040    {
041    
042        /**
043         * Returns OgnlRuntime.NotFound if the property does not exist.
044         */
045        public Object getPossibleProperty( Map<String, Object> context, Object target, String name )
046            throws OgnlException
047        {
048            Object result;
049            OgnlContext ognlContext = (OgnlContext) context;
050    
051            try
052            {
053                result = OgnlRuntime.getMethodValue( ognlContext, target, name, true );
054                if ( result == OgnlRuntime.NotFound )
055                {
056                    result = OgnlRuntime.getFieldValue( ognlContext, target, name, true );
057                }
058            }
059            catch ( IntrospectionException ex )
060            {
061                throw new OgnlException( name, ex );
062            }
063            catch ( OgnlException ex )
064            {
065                throw ex;
066            }
067            catch ( Exception ex )
068            {
069                throw new OgnlException( name, ex );
070            }
071    
072            return result;
073        }
074    
075        /**
076         * Returns OgnlRuntime.NotFound if the property does not exist.
077         */
078        public Object setPossibleProperty( Map<String, Object> context, Object target, String name, Object value )
079            throws OgnlException
080        {
081            Object result = null;
082            OgnlContext ognlContext = (OgnlContext) context;
083    
084            try
085            {
086                if ( !OgnlRuntime.setMethodValue( ognlContext, target, name, value, true ) )
087                {
088                    result = OgnlRuntime.setFieldValue( ognlContext, target, name, value ) ? null : OgnlRuntime.NotFound;
089                }
090    
091                if ( result == OgnlRuntime.NotFound )
092                {
093                    Method m = OgnlRuntime.getWriteMethod( target.getClass(), name );
094                    if ( m != null )
095                    {
096                        result = m.invoke( target, new Object[] { value } );
097                    }
098                }
099            }
100            catch ( IntrospectionException ex )
101            {
102                throw new OgnlException( name, ex );
103            }
104            catch ( OgnlException ex )
105            {
106                throw ex;
107            }
108            catch ( Exception ex )
109            {
110                throw new OgnlException( name, ex );
111            }
112    
113            return result;
114        }
115    
116        public boolean hasGetProperty( OgnlContext context, Object target, Object oname )
117            throws OgnlException
118        {
119            try
120            {
121                return OgnlRuntime.hasGetProperty( context, target, oname );
122            }
123            catch ( IntrospectionException ex )
124            {
125                throw new OgnlException( "checking if " + target + " has gettable property " + oname, ex );
126            }
127        }
128    
129        public boolean hasGetProperty( Map<String, Object> context, Object target, Object oname )
130            throws OgnlException
131        {
132            return hasGetProperty( (OgnlContext) context, target, oname );
133        }
134    
135        public boolean hasSetProperty( OgnlContext context, Object target, Object oname )
136            throws OgnlException
137        {
138            try
139            {
140                return OgnlRuntime.hasSetProperty( context, target, oname );
141            }
142            catch ( IntrospectionException ex )
143            {
144                throw new OgnlException( "checking if " + target + " has settable property " + oname, ex );
145            }
146        }
147    
148        public boolean hasSetProperty( Map<String, Object> context, Object target, Object oname )
149            throws OgnlException
150        {
151            return hasSetProperty( (OgnlContext) context, target, oname );
152        }
153    
154        public Object getProperty( Map<String, Object> context, Object target, Object oname )
155            throws OgnlException
156        {
157            String name = oname.toString();
158    
159            Object result = getPossibleProperty( context, target, name );
160    
161            if ( result == OgnlRuntime.NotFound )
162            {
163                throw new NoSuchPropertyException( target, name );
164            }
165    
166            return result;
167        }
168    
169        public void setProperty( Map<String, Object> context, Object target, Object oname, Object value )
170            throws OgnlException
171        {
172            String name = oname.toString();
173    
174            Object result = setPossibleProperty( context, target, name, value );
175    
176            if ( result == OgnlRuntime.NotFound )
177            {
178                throw new NoSuchPropertyException( target, name );
179            }
180        }
181    
182        public Class<?> getPropertyClass( OgnlContext context, Object target, Object index )
183        {
184            try
185            {
186                Method m = OgnlRuntime.getReadMethod( target.getClass(), index.toString() );
187    
188                if ( m == null )
189                {
190    
191                    if ( String.class.isAssignableFrom( index.getClass() ) && !target.getClass().isArray() )
192                    {
193                        String key = ( (String) index ).replaceAll( "\"", "" );
194                        try
195                        {
196                            Field f = target.getClass().getField( key );
197                            if ( f != null )
198                            {
199    
200                                return f.getType();
201                            }
202                        }
203                        catch ( NoSuchFieldException e )
204                        {
205                            return null;
206                        }
207                    }
208    
209                    return null;
210                }
211    
212                return m.getReturnType();
213    
214            }
215            catch ( Throwable t )
216            {
217                throw OgnlOps.castToRuntime( t );
218            }
219        }
220    
221        public String getSourceAccessor( OgnlContext context, Object target, Object index )
222        {
223            try
224            {
225    
226                String methodName = index.toString().replaceAll( "\"", "" );
227                Method m = OgnlRuntime.getReadMethod( target.getClass(), methodName );
228    
229                // try last ditch effort of checking if they were trying to do reflection via a return method value
230    
231                if ( m == null && context.getCurrentObject() != null )
232                {
233                    m =
234                        OgnlRuntime.getReadMethod( target.getClass(),
235                                                   context.getCurrentObject().toString().replaceAll( "\"", "" ) );
236                }
237                // System.out.println("tried to get read method from target: " + target.getClass() + " with methodName:" +
238                // methodName + " result: " + m);
239                // try to get field if no method could be found
240    
241                if ( m == null )
242                {
243                    try
244                    {
245                        if ( String.class.isAssignableFrom( index.getClass() ) && !target.getClass().isArray() )
246                        {
247                            Field f = target.getClass().getField( methodName );
248    
249                            if ( f != null )
250                            {
251                                context.setCurrentType( f.getType() );
252                                context.setCurrentAccessor( f.getDeclaringClass() );
253    
254                                return "." + f.getName();
255                            }
256                        }
257                    }
258                    catch ( NoSuchFieldException e )
259                    {
260                        // ignore
261                    }
262    
263                    return "";
264                }
265    
266                context.setCurrentType( m.getReturnType() );
267                final OgnlExpressionCompiler compiler = OgnlRuntime.getCompiler( context );
268                context.setCurrentAccessor( compiler.getSuperOrInterfaceClass( m, m.getDeclaringClass() ) );
269    
270                return "." + m.getName() + "()";
271    
272            }
273            catch ( Throwable t )
274            {
275                throw OgnlOps.castToRuntime( t );
276            }
277        }
278    
279        public String getSourceSetter( OgnlContext context, Object target, Object index )
280        {
281            try
282            {
283    
284                String methodName = index.toString().replaceAll( "\"", "" );
285                Method m = OgnlRuntime.getWriteMethod( target.getClass(), methodName );
286    
287                if ( m == null && context.getCurrentObject() != null && context.getCurrentObject().toString() != null )
288                {
289                    m =
290                        OgnlRuntime.getWriteMethod( target.getClass(),
291                                                    context.getCurrentObject().toString().replaceAll( "\"", "" ) );
292                }
293    
294                if ( m == null || m.getParameterTypes() == null || m.getParameterTypes().length <= 0 )
295                {
296                    throw new UnsupportedCompilationException( "Unable to determine setting expression on "
297                        + context.getCurrentObject() + " with index of " + index );
298                }
299    
300                Class<?> parm = m.getParameterTypes()[0];
301                String conversion;
302    
303                if ( m.getParameterTypes().length > 1 )
304                {
305                    throw new UnsupportedCompilationException(
306                        "Object property accessors can only support single parameter setters." );
307                }
308    
309                final OgnlExpressionCompiler compiler = OgnlRuntime.getCompiler( context );
310                if ( parm.isPrimitive() )
311                {
312                    Class<?> wrapClass = OgnlRuntime.getPrimitiveWrapperClass( parm );
313                    conversion = compiler.createLocalReference( context, "((" + wrapClass.getName()
314                        + ")org.apache.commons.ognl.OgnlOps#convertValue($3," + wrapClass.getName() + ".class, true))."
315                        + OgnlRuntime.getNumericValueGetter( wrapClass ), parm );
316    
317                }
318                else if ( parm.isArray() )
319                {
320                    conversion = compiler.createLocalReference( context, "(" + ExpressionCompiler.getCastString( parm )
321                        + ")org.apache.commons.ognl.OgnlOps#toArray($3," + parm.getComponentType().getName() + ".class)",
322                                                                parm );
323    
324                }
325                else
326                {
327                    conversion = compiler.createLocalReference( context, "(" + parm.getName()
328                        + ")org.apache.commons.ognl.OgnlOps#convertValue($3," + parm.getName() + ".class)", parm );
329                }
330    
331                context.setCurrentType( m.getReturnType() );
332                context.setCurrentAccessor(
333                    compiler.getSuperOrInterfaceClass( m, m.getDeclaringClass() ) );
334    
335                return "." + m.getName() + "(" + conversion + ")";
336    
337            }
338            catch ( Throwable t )
339            {
340                throw OgnlOps.castToRuntime( t );
341            }
342        }
343    }