001    /*
002     * Copyright 2002-2004 The Apache Software Foundation
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *     http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.apache.commons.clazz.reflect.extended;
017    
018    import java.lang.reflect.Method;
019    import java.util.HashMap;
020    import java.util.Iterator;
021    import java.util.Map;
022    
023    import org.apache.commons.clazz.reflect.ReflectedClazz;
024    import org.apache.commons.clazz.reflect.common.*;
025    
026    /**
027     * A ReflectedPropertyIntrospector that discovers list (aka indexed) properties.
028     * 
029     * @author <a href="mailto:dmitri@apache.org">Dmitri Plotnikov</a>
030     * @version $Id: ExtendedReflectedListPropertyIntrospector.java 155436 2005-02-26 13:17:48Z dirkv $
031     */
032    public class ExtendedReflectedListPropertyIntrospector
033        extends ReflectedListPropertyIntrospectorSupport 
034    {
035        protected static final AccessorMethodParser ADD_METHOD_PARSER =
036            new AddAccessorMethodParser();
037    
038        protected static final AccessorMethodParser ADD_INDEXED_METHOD_PARSER =
039            new AddIndexedAccessorMethodParser();
040    
041        protected static final AccessorMethodParser REMOVE_INDEXED_METHOD_PARSER =
042            new RemoveIndexedAccessorMethodParser();
043    
044        protected static final AccessorMethodParser REMOVE_METHOD_PARSER =
045            new RemoveAccessorMethodParser();
046    
047        protected static final AccessorMethodParser SIZE_METHOD_PARSER =
048            new SizeAccessorMethodParser();
049                            
050                               
051        public void introspectProperties(
052                ReflectedClazz clazz,
053                Class javaClass,
054                Map parseResultMap)
055        {
056            HashMap parseResultMapSingular = new HashMap();
057            Method methods[] = javaClass.getMethods();
058            ReflectedListPropertyParseResults parseResults;
059            AccessorMethodParseResults results;
060            for (int i = 0; i < methods.length; i++) {
061                Method method = methods[i];
062                Method method3 = method;
063    
064                // Check getFooCount() before we check getFooList(),
065                // because the parser for the latter is generic enough
066                // to include the former            
067                results = getSizeAccessorMethodParser().parse(method3);
068                if (results != null) {
069                    parseResults =
070                        getParseResults(
071                            clazz,
072                            parseResultMapSingular,
073                            results.getPropertyName());
074                    parseResults.setSizeMethodParseResults(results);
075                    continue;
076                }
077                
078                results = getReadAccessMethodParser().parse(method);
079                if (results != null) {
080                    parseResults =
081                        getParseResults(
082                            clazz,
083                            parseResultMap,
084                            results.getPropertyName());
085                    parseResults.setReadMethodParseResults(results);
086                    continue;
087                }
088    
089                results = getWriteAccessMethodParser().parse(method);
090                if (results != null) {
091                    parseResults =
092                        getParseResults(
093                            clazz,
094                            parseResultMap,
095                            results.getPropertyName());
096                    parseResults.setWriteMethodParseResults(results);
097                    continue;
098                }
099    
100                results = getGetAccessMethodParser().parse(method);
101                if (results != null) {
102                    parseResults =
103                        getParseResults(
104                            clazz,
105                            parseResultMapSingular,
106                            results.getPropertyName());
107                    parseResults.setGetMethodParseResults(results);
108                    continue;
109                }
110                
111                results = getSetAccessMethodParser().parse(method);
112                if (results != null) {
113                    parseResults =
114                        getParseResults(
115                            clazz,
116                            parseResultMapSingular,
117                            results.getPropertyName());
118                    parseResults.setSetMethodParseResults(results);
119                    continue;
120                }
121                
122                results = getAddAccessorMethodParser().parse(method);
123                if (results != null) {
124                    parseResults =
125                        getParseResults(
126                            clazz,
127                            parseResultMapSingular,
128                            results.getPropertyName());
129                    parseResults.setAddMethodParseResults(results);
130                    continue;
131                }
132    
133                results = getAddIndexedAccessorMethodParser().parse(method);
134                if (results != null) {
135                    parseResults =
136                        getParseResults(
137                            clazz,
138                            parseResultMapSingular,
139                            results.getPropertyName());
140                    parseResults.setAddIndexedMethodParseResults(results);
141                    continue;
142                }
143    
144                results = getRemoveAccessorMethodParser().parse(method);
145                if (results != null) {
146                    parseResults =
147                        getParseResults(
148                            clazz,
149                            parseResultMapSingular,
150                            results.getPropertyName());
151                    parseResults.setRemoveMethodParseResults(results);
152                    continue;
153                }
154    
155                results = getRemoveIndexedAccessorMethodParser().parse(method);
156                if (results != null) {
157                    parseResults =
158                        getParseResults(
159                            clazz,
160                            parseResultMapSingular,
161                            results.getPropertyName());
162                    parseResults.setRemoveIndexedMethodParseResults(results);
163                    continue;
164                }
165            }
166            
167            Iterator iter = parseResultMap.entrySet().iterator();
168            while (iter.hasNext()) {
169                Map.Entry entry = (Map.Entry) iter.next();
170                ReflectedListPropertyParseResults result = 
171                    (ReflectedListPropertyParseResults) entry.getValue();
172                if (!result.isList()) {
173                    iter.remove();
174                }
175            }
176            
177            mergeSingularMethods(parseResultMap, parseResultMapSingular);
178        }
179        
180        protected AccessorMethodParser getAddAccessorMethodParser() {
181            return ADD_METHOD_PARSER;
182        }
183    
184        protected AccessorMethodParser getAddIndexedAccessorMethodParser() {
185            return ADD_INDEXED_METHOD_PARSER;
186        }
187    
188        protected AccessorMethodParser getRemoveAccessorMethodParser() {
189            return REMOVE_METHOD_PARSER;
190        }
191    
192        protected AccessorMethodParser getRemoveIndexedAccessorMethodParser() {
193            return REMOVE_INDEXED_METHOD_PARSER;
194        }
195    
196        protected AccessorMethodParser getSizeAccessorMethodParser() {
197            return SIZE_METHOD_PARSER;
198        }
199    
200        /**
201         * Returns <code>true</code> if the suffix is "s" or 
202         * "List", "Array" or "Vector".
203         *  
204         * @see ReflectedPropertyIntrospectorSupport#isCorrectPluralSuffix(String,String)
205         */
206        protected boolean isCorrectPluralSuffix(String singular, String suffix) {
207            return super.isCorrectPluralSuffix(singular, suffix)
208                || suffix.equals("List")
209                || suffix.equals("Array")
210                || suffix.equals("Vector");
211        }
212    
213        /**
214         * Parser for the <code>addFoo(value)</code> method:
215         * <ul>
216         *  <li>Return type void</li>
217         *  <li>Name starts with "add" followed by capitalized singular
218         *      form of the property name</li>
219         *  <li>One parameter</li>
220         * </ul>
221         */                        
222        public static class AddAccessorMethodParser extends AccessorMethodParser {
223            protected String requiredPrefix() {
224                return "add";
225            }
226            protected int requiredParameterCount() {
227                return 1;
228            }
229            protected Class getValueType(Method method) {
230                return method.getParameterTypes()[0];
231            }
232        }
233        
234        /**
235         * Parser for the <code>addFoo(index, value)</code> method:
236         * <ul>
237         *  <li>Return type void</li>
238         *  <li>Name starts with "add" followed by capitalized singular
239         *      form of the property name</li>
240         *  <li>Two parameters, first integer</li>
241         * </ul>
242         */                        
243        public static class AddIndexedAccessorMethodParser
244            extends AccessorMethodParser 
245        {
246            protected String requiredPrefix() {
247                return "add";
248            }
249            protected int requiredParameterCount() {
250                return 2;
251            }
252            protected boolean testParameterType(int index, Class parameterType) {
253                if (index == 0) {
254                    return parameterType.equals(Integer.TYPE);
255                }
256                return true;
257            }
258            protected Class getValueType(Method method) {
259                return method.getParameterTypes()[1];
260            }
261        }
262            
263        /**
264         * Parser for the <code>removeFoo(index)</code> method:
265         * <ul>
266         *  <li>Name starts with "remove" followed by capitalized singular
267         *      form of the property name</li>
268         *  <li>One integer parameter</li>
269         * </ul>
270         */
271        public static class RemoveIndexedAccessorMethodParser
272            extends AccessorMethodParser 
273        {
274            protected String requiredPrefix() {
275                return "remove";
276            }
277            protected int requiredParameterCount() {
278                return 1;
279            }
280            protected boolean testParameterType(int index, Class parameterType) {
281                return parameterType.equals(Integer.TYPE);
282            }
283        }
284    
285        /**
286         * Parser for the <code>removeFoo(value)</code> method:
287         * <ul>
288         *  <li>Name starts with "remove" followed by capitalized singular
289         *      form of the property name</li>
290         *  <li>One parameter</li>
291         * </ul>
292         */                        
293        public static class RemoveAccessorMethodParser 
294            extends AccessorMethodParser 
295        {
296            protected String requiredPrefix() {
297                return "remove";
298            }
299            protected int requiredParameterCount() {
300                return 1;
301            }
302            protected boolean testParameterType(int index, Class parameterType) {
303                return !parameterType.equals(Integer.TYPE);
304            }
305            protected Class getValueType(Method method) {
306                return method.getParameterTypes()[0];
307            }
308        }
309        
310        /**
311         * Parser for the <code>getFooCount()</code> method:
312         * <ul>
313         *  <li>Returns integer</li>
314         *  <li>Name starts with "get" followed by capitalized singular
315         *      form of the property name, followed by "Count" or "Size"</li>
316         *  <li>No parameters</li>
317         * </ul>
318         */                        
319        public static class SizeAccessorMethodParser extends AccessorMethodParser {
320            protected boolean testReturnType(Class javaClass) {
321                return javaClass.equals(Integer.TYPE);
322            }
323            protected String requiredPrefix() {
324                return "get";
325            }
326            protected int requiredParameterCount() {
327                return 0;
328            }
329            protected String testAndRemoveSuffix(String methodName) {
330                if (methodName.endsWith("Count")) {                
331                    return methodName.substring(0, methodName.length() - 5);
332                }
333                if (methodName.endsWith("Size")) {
334                    return methodName.substring(0, methodName.length() - 4);
335                }
336                return null;
337            }
338        }
339                
340    }