Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
AccessorMethodParser |
|
| 3.6363636363636362;3.636 |
1 | /* | |
2 | * Copyright 2002-2004 The Apache Software Foundation | |
3 | * | |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.commons.clazz.reflect.common; | |
17 | ||
18 | import java.lang.reflect.Method; | |
19 | import java.lang.reflect.Modifier; | |
20 | ||
21 | /** | |
22 | * | |
23 | * @author <a href="mailto:dmitri@apache.org">Dmitri Plotnikov</a> | |
24 | * @version $Id: AccessorMethodParser.java 155436 2005-02-26 13:17:48Z dirkv $ | |
25 | */ | |
26 | 0 | public class AccessorMethodParser { |
27 | ||
28 | /** | |
29 | * If a method parsed by this parser must have a number or parameters, | |
30 | * override and return that number. | |
31 | */ | |
32 | protected int requiredParameterCount() { | |
33 | 0 | return -1; |
34 | } | |
35 | ||
36 | /** | |
37 | * If a method parsed by this parser must have a certain prefix, | |
38 | * override and return a non-null prefix string | |
39 | */ | |
40 | protected String requiredPrefix() { | |
41 | 0 | return null; |
42 | } | |
43 | ||
44 | /** | |
45 | * To check constraints on the return type of methods parsed | |
46 | * by this parser, override and perform the check. | |
47 | * | |
48 | * @param javaClass The return type of the method (never null) | |
49 | * @return boolean True if the return type passes the parser's constraints | |
50 | */ | |
51 | protected boolean testReturnType(Class javaClass) { | |
52 | 0 | return true; |
53 | } | |
54 | ||
55 | /** | |
56 | * To check constraints on the type of a parameter, override | |
57 | * and perform the check. | |
58 | * | |
59 | * @param javaClass The return type of the method (never null) | |
60 | * @return boolean True if the return type passes the parser's constraints | |
61 | */ | |
62 | protected boolean testParameterType(int index, Class parameterType) { | |
63 | 0 | return true; |
64 | } | |
65 | ||
66 | /** | |
67 | * If a method parsed by this parser must have a certain suffix, | |
68 | * override this method, check that it does and remove the | |
69 | * suffix. | |
70 | */ | |
71 | protected String testAndRemoveSuffix(String methodName) { | |
72 | 0 | return methodName; |
73 | } | |
74 | ||
75 | /** | |
76 | * Returns true if the character can be the first character of a Capitalized | |
77 | * property name. | |
78 | */ | |
79 | protected boolean testFirstCharacterOfPropertyName(char ch) { | |
80 | 0 | return Character.isUpperCase(ch); |
81 | } | |
82 | ||
83 | /** | |
84 | * Extract the value type from the method. Depending on the type | |
85 | * of method, it could be the return type or the type of a parameter. | |
86 | */ | |
87 | protected Class getValueType(Method method) { | |
88 | 0 | return null; |
89 | } | |
90 | ||
91 | /** | |
92 | * Extract the parameter type from the method, if it has one. | |
93 | * For example a mapped property "get" method might have | |
94 | * a "key" parameter. | |
95 | */ | |
96 | protected Class getParameterType(Method method) { | |
97 | 0 | return null; |
98 | } | |
99 | ||
100 | /** | |
101 | * Parses the supplied method according to the parser's configuration. | |
102 | * If the parse process fails, returns null. | |
103 | * | |
104 | * @param method | |
105 | * @return AccessorMethodParseResults | |
106 | */ | |
107 | public AccessorMethodParseResults parse(Method method) { | |
108 | 0 | int modifiers = method.getModifiers(); |
109 | ||
110 | // An accessor methods must be public and non-static | |
111 | 0 | if (!Modifier.isPublic(modifiers) || Modifier.isStatic(modifiers)) { |
112 | 0 | return null; |
113 | } | |
114 | ||
115 | 0 | Class returnType = method.getReturnType(); |
116 | 0 | if (returnType == null) { |
117 | 0 | returnType = Void.TYPE; |
118 | } | |
119 | 0 | if (!testReturnType(returnType)) { |
120 | 0 | return null; |
121 | } | |
122 | ||
123 | 0 | int reqParamCount = requiredParameterCount(); |
124 | 0 | if (reqParamCount != -1) { |
125 | 0 | Class paramTypes[] = method.getParameterTypes(); |
126 | 0 | if (paramTypes.length != reqParamCount) { |
127 | 0 | return null; |
128 | } | |
129 | ||
130 | 0 | for (int i = 0; i < paramTypes.length; i++) { |
131 | 0 | if (!testParameterType(i, paramTypes[i])) { |
132 | 0 | return null; |
133 | } | |
134 | } | |
135 | } | |
136 | ||
137 | 0 | String propertyName = getPropertyName(method); |
138 | 0 | if (propertyName == null) { |
139 | 0 | return null; |
140 | } | |
141 | ||
142 | 0 | return new AccessorMethodParseResults( |
143 | method, | |
144 | propertyName, | |
145 | getValueType(method), | |
146 | getParameterType(method)); | |
147 | } | |
148 | ||
149 | /** | |
150 | * Parse method name and return the corresponding property name. | |
151 | * Return null if the method name does not satisfy the parser's | |
152 | * grammar. | |
153 | * <p> | |
154 | * The default implementation of the method checks if the | |
155 | * method name starts with the specified prefix followed | |
156 | * by an optionally capitalized property name. | |
157 | * | |
158 | * @param methodName | |
159 | * @return String | |
160 | */ | |
161 | protected String getPropertyName(Method method) { | |
162 | 0 | String name = method.getName(); |
163 | 0 | name = testAndRemoveSuffix(name); |
164 | 0 | if (name == null) { |
165 | 0 | return null; |
166 | } | |
167 | ||
168 | 0 | String prefix = requiredPrefix(); |
169 | 0 | if (prefix == null) { |
170 | 0 | return name; |
171 | } | |
172 | ||
173 | 0 | int prefixLength = prefix.length(); |
174 | ||
175 | 0 | if (name.length() <= prefixLength) { |
176 | 0 | return null; |
177 | } | |
178 | ||
179 | 0 | if (!name.startsWith(prefix)) { |
180 | 0 | return null; |
181 | } | |
182 | ||
183 | 0 | if (!testFirstCharacterOfPropertyName(name.charAt(prefixLength))) { |
184 | 0 | return null; |
185 | } | |
186 | ||
187 | 0 | return decapitalize(name.substring(prefixLength)); |
188 | } | |
189 | ||
190 | /** | |
191 | * Changes the first character of the <code>string</code> | |
192 | * to lower case, unless the second character is | |
193 | * upper case. This is consistent with the JavaBean specification. | |
194 | * | |
195 | * @param candidate | |
196 | * @return String | |
197 | */ | |
198 | protected String decapitalize(String string) { | |
199 | 0 | char firstChar = string.charAt(0); |
200 | 0 | if (!Character.isUpperCase(firstChar)) { |
201 | 0 | return string; |
202 | } | |
203 | ||
204 | 0 | int len = string.length(); |
205 | 0 | if (len == 1) { |
206 | 0 | return String.valueOf(Character.toLowerCase(firstChar)); |
207 | } | |
208 | 0 | else if (Character.isLowerCase(string.charAt(1))) { |
209 | 0 | char buffer[] = new char[len]; |
210 | 0 | buffer[0] = Character.toLowerCase(firstChar); |
211 | 0 | string.getChars(1, len, buffer, 1); |
212 | 0 | return new String(buffer); |
213 | } | |
214 | ||
215 | 0 | return string; |
216 | } | |
217 | } |