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.Map;
020    
021    import org.apache.commons.clazz.reflect.ReflectedClazz;
022    
023    /**
024     * A ReflectedPropertyIntrospector that discovers scalar properties.
025     * 
026     * @author <a href="mailto:dmitri@apache.org">Dmitri Plotnikov</a>
027     * @version $Id: ReflectedScalarPropertyIntrospector.java 155436 2005-02-26 13:17:48Z dirkv $
028     */
029    public class ReflectedScalarPropertyIntrospector 
030                extends ReflectedPropertyIntrospectorSupport 
031    {
032        protected static final AccessorMethodParser READ_METHOD_PARSER =
033            new JBReadAccessorMethodParser();
034    
035        protected static final AccessorMethodParser WRITE_METHOD_PARSER =
036            new WriteAccessorMethodParser();
037       
038        public void introspectProperties(
039                ReflectedClazz clazz,
040                Class javaClass,
041                Map parseResultMap)
042        {
043            Method methods[] = javaClass.getMethods();
044            ReflectedScalarPropertyParseResults parseResults;
045            AccessorMethodParseResults results;
046            for (int i = 0; i < methods.length; i++) {
047                Method method = methods[i];
048                if (method.getDeclaringClass().equals(Object.class)) {
049                    continue;
050                }
051                
052                results = getReadAccessorMethodParser().parse(method);
053                if (results != null) {
054                    parseResults =
055                        getParseResults(
056                            clazz,
057                            parseResultMap,
058                            results.getPropertyName());
059                    parseResults.setReadMethodParseResults(results);
060                    continue;
061                }
062                
063                results = getWriteAccessorMethodParser().parse(method);
064                if (results != null) {
065                    parseResults =
066                        getParseResults(
067                            clazz,
068                            parseResultMap,
069                            results.getPropertyName());
070                    parseResults.setWriteMethodParseResults(results);
071                    continue;
072                }    
073            }
074        }
075     
076        /**
077         * Override to return an alternative parser for the read accessor method.
078         */
079        protected AccessorMethodParser getReadAccessorMethodParser() {
080            return READ_METHOD_PARSER;
081        }
082        
083        protected AccessorMethodParser getWriteAccessorMethodParser() {
084            return WRITE_METHOD_PARSER;
085        }    
086    
087        /**
088         * Finds a ReflectedScalarPropertyParseResults for the given
089         * propertyName or creates a new one and puts it in the map.
090         */
091        protected ReflectedScalarPropertyParseResults getParseResults(
092                ReflectedClazz clazz,
093                Map parseResultMap,
094                String propertyName) 
095        {
096            ReflectedScalarPropertyParseResults parseResults =
097                (ReflectedScalarPropertyParseResults) parseResultMap.get(
098                    propertyName);
099            if (parseResults == null) {
100                parseResults =
101                    new ReflectedScalarPropertyParseResults(clazz, propertyName);
102                parseResultMap.put(propertyName, parseResults);
103            }
104            return parseResults;
105        }
106        
107        /**
108         * Creates a new ReflectedAccessorPairProperty based on parse results. 
109         */
110        protected ReflectedAccessorPairProperty createProperty(
111            ReflectedClazz clazz,
112            ReflectedPropertyParseResults parseResults)
113        {
114            ReflectedScalarProperty property =
115                new ReflectedScalarProperty(clazz, parseResults.getPropertyName());
116    
117            property.setType(parseResults.getPropertyType());
118            property.setReadMethod(parseResults.getReadMethod());
119            property.setWriteMethod(parseResults.getWriteMethod());
120            return property;
121        }
122    
123        /**
124         * Parser for the <code>getFoo()</code> method:
125         * <ul>
126         *  <li>Return type not void</li>
127         *  <li>Name starts with "get" followed by capitalized property name.
128         *  <li>No parameters</li>
129         * </ul>
130         */
131        public static class ReadAccessorMethodParser extends AccessorMethodParser {
132            protected boolean testReturnType(Class returnType) {
133                return !returnType.equals(Void.TYPE);
134            }
135            protected int requiredParameterCount() {
136                return 0;
137            }
138            protected String requiredPrefix() {
139                return "get";
140            }
141            protected Class getValueType(Method method) {
142                return method.getReturnType();
143            }
144        }
145                       
146        /**
147         * Parser for the <code>getFoo()</code> method:
148         * <ul>
149         *  <li>Return type not void</li>
150         *  <li>Name starts with "get" followed by capitalized property name.
151         *      If the property is boolean the name may also start with "is".</li>
152         *  <li>No parameters</li>
153         * </ul>
154         */
155        public static class JBReadAccessorMethodParser 
156                extends ReadAccessorMethodParser
157        {               
158            protected String getPropertyName(Method method) {
159                String name = method.getName();
160                String propertyName = null;
161                if (name.startsWith("get")) {
162                    if (name.length() <= 3) {
163                        return null;
164                    }
165                    if (!testFirstCharacterOfPropertyName(name.charAt(3))) {
166                        return null;
167                    }
168                    propertyName = decapitalize(name.substring(3));
169                }
170                else if (method.getReturnType().equals(Boolean.TYPE)
171                        && name.startsWith("is")) {
172                    if (name.length() <= 2) {
173                        return null;
174                    }
175                    if (!testFirstCharacterOfPropertyName(name.charAt(2))) {
176                        return null;
177                    }
178                    propertyName = decapitalize(name.substring(2));
179                }
180                return propertyName;
181            }
182        };
183                
184        /**
185         * Parser for the <code>setFoo(value)</code> method:
186         * <ul>
187         *  <li>Return type void</li>
188         *  <li>Name starts with "set" followed by capitalized property name</li>
189         *  <li>One parameter</li>
190         * </ul>
191         */            
192        public static class WriteAccessorMethodParser extends AccessorMethodParser {
193            protected int requiredParameterCount() {
194                return 1;
195            }
196            protected String requiredPrefix() {
197                return "set";
198            }
199            protected boolean testReturnType(Class returnType) {
200                return returnType.equals(Void.TYPE);
201            }
202            protected Class getValueType(Method method) {
203                return method.getParameterTypes()[0];
204            }
205        }
206    
207    }