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.common;
017    
018    import java.lang.reflect.Method;
019    import java.util.Iterator;
020    import java.util.Map;
021    
022    import org.apache.commons.clazz.reflect.ReflectedClazz;
023    
024    /**
025     * A ReflectedPropertyIntrospector that discovers list (aka indexed) properties.
026     * 
027     * @author <a href="mailto:dmitri@apache.org">Dmitri Plotnikov</a>
028     * @version $Id: ReflectedListPropertyIntrospectorSupport.java 155436 2005-02-26 13:17:48Z dirkv $
029     */
030    public abstract class ReflectedListPropertyIntrospectorSupport
031        extends ReflectedPropertyIntrospectorSupport 
032    {
033        protected static final AccessorMethodParser READ_METHOD_PARSER =
034            new ReadAccessorMethodParser();
035    
036        protected static final AccessorMethodParser WRITE_METHOD_PARSER =
037            new WriteAccessorMethodParser();
038    
039        protected static final AccessorMethodParser GET_METHOD_PARSER =
040            new GetAccessorMethodParser();
041    
042        protected static final AccessorMethodParser SET_METHOD_PARSER =
043            new SetAccessorMethodParser();
044    
045        protected AccessorMethodParser getReadAccessMethodParser() {
046            return READ_METHOD_PARSER;  
047        }
048        
049        protected AccessorMethodParser getWriteAccessMethodParser() {
050            return WRITE_METHOD_PARSER;  
051        }
052        
053        protected AccessorMethodParser getGetAccessMethodParser() {
054            return GET_METHOD_PARSER;  
055        }
056        
057        protected AccessorMethodParser getSetAccessMethodParser() {
058            return SET_METHOD_PARSER;  
059        }
060        
061        /**
062         * Finds a ReflectedListPropertyParseResults for the given
063         * propertyName or creates a new one and puts it in the map.
064         */
065        protected ReflectedListPropertyParseResults getParseResults(
066                ReflectedClazz clazz,
067                Map parseResultMap,
068                String propertyName) 
069        {
070            ReflectedListPropertyParseResults parseResults =
071                (ReflectedListPropertyParseResults) parseResultMap.get(
072                    propertyName);
073            if (parseResults == null) {
074                parseResults =
075                    new ReflectedListPropertyParseResults(clazz, propertyName);
076                parseResultMap.put(propertyName, parseResults);
077            }
078            return parseResults;
079        }
080    
081        /**
082         * Combines data collected from singular methods like
083         * <code>getFoo(index)</code> with Properties, which already
084         * contain data about plural methods like <code>getFooList()</code>.
085         */
086        protected void mergeSingularMethods(
087                Map parseResultMapPlural, Map parseResultMapSingular)
088        {
089            Iterator iter = parseResultMapSingular.values().iterator();
090            while (iter.hasNext()) {
091    
092                ReflectedListPropertyParseResults singular =
093                    (ReflectedListPropertyParseResults) iter.next();
094    
095                ReflectedListPropertyParseResults plural =
096                    findBySingularName(
097                            parseResultMapPlural, singular.getPropertyName());
098    
099                if (plural != null) {
100                    plural.merge(singular);
101                }
102                else {
103                    // We don't have any plural methods - let's just use
104                    // the singular ones then
105                    parseResultMapPlural.put(singular.getPropertyName(), singular);
106                }
107            }
108        }
109        
110        /**
111         * Given a singular form of a property name, locates parse results
112         * for a property with the corresponding plural name.
113         */
114        protected ReflectedListPropertyParseResults findBySingularName(
115                Map parseResultMapPlural,
116                String singularName)
117        {
118            ReflectedListPropertyParseResults plural =
119                (ReflectedListPropertyParseResults)
120                    parseResultMapPlural.get(singularName);
121            if (plural != null) {
122                return plural;
123            }
124    
125            Iterator iter = parseResultMapPlural.entrySet().iterator();
126            while (iter.hasNext()) {
127                Map.Entry entry = (Map.Entry) iter.next();
128                if (isCorrectPluralForm(singularName, (String) entry.getKey())) {
129                    return (ReflectedListPropertyParseResults) entry.getValue();
130                }
131            }
132            return null;
133        }
134        
135        /**
136         * Creates a new ReflectedListProperty based on parse results. 
137         */
138        protected ReflectedAccessorPairProperty createProperty(
139            ReflectedClazz clazz,
140            ReflectedPropertyParseResults parseResults)
141        {
142            ReflectedListProperty property =
143                new ReflectedListProperty(clazz, parseResults.getPropertyName());
144    
145            ReflectedListPropertyParseResults parseResultsList =
146                    (ReflectedListPropertyParseResults) parseResults;
147            
148            property.setAliases(parseResultsList.getAliases());
149            property.setType(parseResultsList.getPropertyType());
150            property.setContentType(parseResultsList.getContentType());
151            property.setReadMethod(parseResultsList.getReadMethod());
152            property.setWriteMethod(parseResultsList.getWriteMethod());
153            property.setGetMethod(parseResultsList.getGetMethod());
154            property.setSetMethod(parseResultsList.getSetMethod());
155            property.setAddMethod(parseResultsList.getAddMethod());
156            property.setAddIndexedMethod(parseResultsList.getAddIndexedMethod());
157            property.setRemoveMethod(parseResultsList.getRemoveMethod());
158            property.setRemoveIndexedMethod(
159                parseResultsList.getRemoveIndexedMethod());
160            property.setSizeMethod(parseResultsList.getSizeMethod());
161            return property;
162        }
163        /**
164         * Parser for the <code>getFooMap()</code> method:
165         * <ul>
166         *  <li>Return type not void</li>
167         *  <li>Name starts with "get" followed by capitalized property name</li>
168         *  <li>No parameters</li>
169         * </ul>
170         * 
171         * We don't check if the parameter is a List here. If it is not,
172         * we want to recognize the method and them mark the corresponding
173         * property as NotAProperty.
174         */
175        public static class ReadAccessorMethodParser extends AccessorMethodParser {
176            protected boolean testReturnType(Class returnType) {
177                return !returnType.equals(Void.TYPE);
178            }
179            protected String requiredPrefix() {
180                return "get";
181            }
182            protected int requiredParameterCount() {
183                return 0;
184            }
185            protected Class getValueType(Method method) {
186                return method.getReturnType();
187            }
188        }
189                
190        /**
191         * Parser for the <code>setFooList(List)</code> method:
192         * <ul>
193         *  <li>Return type void</li>
194         *  <li>Name starts with "set" followed by capitalized property name</li>
195         *  <li>One parameter</li>
196         * </ul>
197         * 
198         * We don't check if the parameter is a List here. If it is not,
199         * we want to recognize the method and them mark the corresponding
200         * property as NotAProperty.
201         */      
202        public static class WriteAccessorMethodParser extends AccessorMethodParser {
203            protected boolean testReturnType(Class returnType) {
204                return returnType.equals(Void.TYPE);
205            }
206            protected int requiredParameterCount() {
207                return 1;
208            }
209            protected String requiredPrefix() {
210                return "set";
211            }
212            protected Class getValueType(Method method) {
213                return method.getParameterTypes()[0];
214            }
215        }
216                
217        /**
218         * Parser for the <code>getFoo(int)</code> method:
219         * <ul>
220         *  <li>Return type not void</li>
221         *  <li>Name starts with "get" followed by capitalized singular
222         *      form of the property name</li>
223         *  <li>One integer parameter</li>
224         * </ul>
225         */            
226        public static class GetAccessorMethodParser extends AccessorMethodParser {
227            protected boolean testReturnType(Class returnType) {
228                return !returnType.equals(Void.TYPE);
229            }
230            protected String requiredPrefix() {
231                return "get";
232            }
233            protected int requiredParameterCount() {
234                return 1;
235            }
236            protected boolean testParameterType(int index, Class parameterType) {
237                return parameterType.equals(Integer.TYPE);
238            }
239            protected Class getValueType(Method method) {
240                return method.getReturnType();
241            }
242        }
243                 
244        /**
245         * Parser for the <code>setFoo(index, value)</code> method:
246         * <ul>
247         *  <li>Return type void</li>
248         *  <li>Name starts with "set" followed by capitalized singular
249         *      form of the property name</li>
250         *  <li>Two parameters, first integer</li>
251         * </ul>
252         */                        
253        public static class SetAccessorMethodParser extends AccessorMethodParser {
254            protected String requiredPrefix() {
255                return "set";
256            }
257            protected int requiredParameterCount() {
258                return 2;
259            }
260            protected boolean testParameterType(int index, Class parameterType) {
261                if (index == 0) {            
262                    return parameterType.equals(Integer.TYPE);
263                }
264                return true;
265            }
266            protected Class getValueType(Method method) {
267                return method.getParameterTypes()[1];
268            }
269        }
270    
271    }