001    package org.apache.commons.ognl.internal.entry;
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    /*
023     * $Id: PropertyDescriptorCacheEntryFactory.java 1430159 2013-01-08 07:40:04Z lukaszlenart $
024     */
025    
026    import org.apache.commons.ognl.ObjectIndexedPropertyDescriptor;
027    import org.apache.commons.ognl.OgnlException;
028    import org.apache.commons.ognl.OgnlRuntime;
029    import org.apache.commons.ognl.internal.CacheException;
030    
031    import java.beans.IntrospectionException;
032    import java.beans.Introspector;
033    import java.beans.PropertyDescriptor;
034    import java.lang.reflect.Method;
035    import java.util.ArrayList;
036    import java.util.HashMap;
037    import java.util.List;
038    import java.util.Map;
039    
040    public class PropertyDescriptorCacheEntryFactory
041        implements ClassCacheEntryFactory<Map<String, PropertyDescriptor>>
042    {
043        public Map<String, PropertyDescriptor> create( Class<?> targetClass )
044            throws CacheException
045        {
046            Map<String, PropertyDescriptor> result = new HashMap<String, PropertyDescriptor>( 101 );
047            PropertyDescriptor[] pda;
048            try
049            {
050                pda = Introspector.getBeanInfo( targetClass ).getPropertyDescriptors();
051    
052                for (PropertyDescriptor aPda : pda) {
053                    // workaround for Introspector bug 6528714 (bugs.sun.com)
054                    if (aPda.getReadMethod() != null && !OgnlRuntime.isMethodCallable(aPda.getReadMethod())) {
055                        aPda.setReadMethod(
056                                findClosestMatchingMethod(targetClass, aPda.getReadMethod(), aPda.getName(),
057                                        aPda.getPropertyType(), true));
058                    }
059                    if (aPda.getWriteMethod() != null && !OgnlRuntime.isMethodCallable(aPda.getWriteMethod())) {
060                        aPda.setWriteMethod(
061                                findClosestMatchingMethod(targetClass, aPda.getWriteMethod(), aPda.getName(),
062                                        aPda.getPropertyType(), false));
063                    }
064    
065                    result.put(aPda.getName(), aPda);
066                }
067    
068                findObjectIndexedPropertyDescriptors( targetClass, result );
069            }
070            catch ( IntrospectionException e )
071            {
072                throw new CacheException( e );
073            }
074            catch ( OgnlException e )
075            {
076                throw new CacheException( e );
077            }
078            return result;
079        }
080    
081        static Method findClosestMatchingMethod( Class<?> targetClass, Method m, String propertyName, Class<?> propertyType,
082                                                 boolean isReadMethod )
083            throws OgnlException
084        {
085            List<Method> methods = OgnlRuntime.getDeclaredMethods( targetClass, propertyName, !isReadMethod );
086    
087            for ( Method method : methods )
088            {
089                if ( method.getName().equals( m.getName() ) && m.getReturnType().isAssignableFrom( m.getReturnType() )
090                    && method.getReturnType() == propertyType
091                    && method.getParameterTypes().length == m.getParameterTypes().length )
092                {
093                    return method;
094                }
095            }
096    
097            return m;
098        }
099    
100        private static void findObjectIndexedPropertyDescriptors( Class<?> targetClass,
101                                                                 Map<String, PropertyDescriptor> intoMap )
102            throws OgnlException
103        {
104            Map<String, List<Method>> allMethods = OgnlRuntime.getMethods( targetClass, false );
105            Map<String, List<Method>> pairs = new HashMap<String, List<Method>>( 101 );
106    
107            for ( Map.Entry<String, List<Method>> entry : allMethods.entrySet() )
108            {
109                String methodName = entry.getKey();
110                List<Method> methods = entry.getValue();
111    
112                /*
113                 * Only process set/get where there is exactly one implementation of the method per class and those
114                 * implementations are all the same
115                 */
116                if ( indexMethodCheck( methods ) )
117                {
118                    boolean isGet = false, isSet;
119                    Method method = methods.get( 0 );
120    
121                    if ( ( ( isSet = methodName.startsWith( OgnlRuntime.SET_PREFIX ) ) || ( isGet =
122                        methodName.startsWith( OgnlRuntime.GET_PREFIX ) ) ) && ( methodName.length() > 3 ) )
123                    {
124                        String propertyName = Introspector.decapitalize( methodName.substring( 3 ) );
125                        Class<?>[] parameterTypes = OgnlRuntime.getParameterTypes( method );
126                        int parameterCount = parameterTypes.length;
127    
128                        if ( isGet && ( parameterCount == 1 ) && ( method.getReturnType() != Void.TYPE ) )
129                        {
130                            List<Method> pair = pairs.get( propertyName );
131    
132                            if ( pair == null )
133                            {
134                                pairs.put( propertyName, pair = new ArrayList<Method>() );
135                            }
136                            pair.add( method );
137                        }
138                        if ( isSet && ( parameterCount == 2 ) && ( method.getReturnType() == Void.TYPE ) )
139                        {
140                            List<Method> pair = pairs.get( propertyName );
141    
142                            if ( pair == null )
143                            {
144                                pairs.put( propertyName, pair = new ArrayList<Method>() );
145                            }
146                            pair.add( method );
147                        }
148                    }
149                }
150            }
151    
152            for ( Map.Entry<String, List<Method>> entry : pairs.entrySet() )
153            {
154                String propertyName = entry.getKey();
155                List<Method> methods = entry.getValue();
156    
157                if ( methods.size() == 2 )
158                {
159                    Method method1 = methods.get( 0 ), method2 = methods.get( 1 ), setMethod =
160                        ( method1.getParameterTypes().length == 2 ) ? method1 : method2, getMethod =
161                        ( setMethod == method1 ) ? method2 : method1;
162                    Class<?> keyType = getMethod.getParameterTypes()[0], propertyType = getMethod.getReturnType();
163    
164                    if ( keyType == setMethod.getParameterTypes()[0] )
165                    {
166                        if ( propertyType == setMethod.getParameterTypes()[1] )
167                        {
168                            ObjectIndexedPropertyDescriptor propertyDescriptor;
169    
170                            try
171                            {
172                                propertyDescriptor =
173                                    new ObjectIndexedPropertyDescriptor( propertyName, propertyType, getMethod, setMethod );
174                            }
175                            catch ( Exception ex )
176                            {
177                                throw new OgnlException(
178                                    "creating object indexed property descriptor for '" + propertyName + "' in "
179                                        + targetClass, ex );
180                            }
181                            intoMap.put( propertyName, propertyDescriptor );
182                        }
183                    }
184    
185                }
186            }
187        }
188        private static boolean indexMethodCheck( List<Method> methods )
189        {
190            boolean result = false;
191    
192            if ( methods.size() > 0 )
193            {
194                Method method = methods.get( 0 );
195                Class<?>[] parameterTypes = OgnlRuntime.getParameterTypes( method );
196                int numParameterTypes = parameterTypes.length;
197                Class<?> lastMethodClass = method.getDeclaringClass();
198    
199                result = true;
200                for ( int i = 1; result && ( i < methods.size() ); i++ )
201                {
202                    Class<?> clazz = methods.get( i ).getDeclaringClass();
203    
204                    // Check to see if more than one method implemented per class
205                    if ( lastMethodClass == clazz )
206                    {
207                        result = false;
208                    }
209                    else
210                    {
211                        Class<?>[] mpt = OgnlRuntime.getParameterTypes( method );
212                        int mpc = parameterTypes.length;
213    
214                        if ( numParameterTypes != mpc )
215                        {
216                            result = false;
217                        }
218                        for ( int j = 0; j < numParameterTypes; j++ )
219                        {
220                            if ( parameterTypes[j] != mpt[j] )
221                            {
222                                result = false;
223                                break;
224                            }
225                        }
226                    }
227                    lastMethodClass = clazz;
228                }
229            }
230            return result;
231        }
232    
233    
234    }