Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
DefaultResolver |
|
| 9.444444444444445;9.444 |
1 | /* | |
2 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
3 | * contributor license agreements. See the NOTICE file distributed with | |
4 | * this work for additional information regarding copyright ownership. | |
5 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
6 | * (the "License"); you may not use this file except in compliance with | |
7 | * the License. You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, software | |
12 | * distributed under the License is distributed on an "AS IS" BASIS, | |
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | * See the License for the specific language governing permissions and | |
15 | * limitations under the License. | |
16 | */ | |
17 | package org.apache.commons.beanutils.expression; | |
18 | ||
19 | /** | |
20 | * Default Property Name Expression {@link Resolver} Implementation. | |
21 | * <p> | |
22 | * This class assists in resolving property names in the following five formats, | |
23 | * with the layout of an identifying String in parentheses: | |
24 | * <ul> | |
25 | * <li><strong>Simple (<code>name</code>)</strong> - The specified | |
26 | * <code>name</code> identifies an individual property of a particular | |
27 | * JavaBean. The name of the actual getter or setter method to be used | |
28 | * is determined using standard JavaBeans instrospection, so that (unless | |
29 | * overridden by a <code>BeanInfo</code> class, a property named "xyz" | |
30 | * will have a getter method named <code>getXyz()</code> or (for boolean | |
31 | * properties only) <code>isXyz()</code>, and a setter method named | |
32 | * <code>setXyz()</code>.</li> | |
33 | * <li><strong>Nested (<code>name1.name2.name3</code>)</strong> The first | |
34 | * name element is used to select a property getter, as for simple | |
35 | * references above. The object returned for this property is then | |
36 | * consulted, using the same approach, for a property getter for a | |
37 | * property named <code>name2</code>, and so on. The property value that | |
38 | * is ultimately retrieved or modified is the one identified by the | |
39 | * last name element.</li> | |
40 | * <li><strong>Indexed (<code>name[index]</code>)</strong> - The underlying | |
41 | * property value is assumed to be an array, or this JavaBean is assumed | |
42 | * to have indexed property getter and setter methods. The appropriate | |
43 | * (zero-relative) entry in the array is selected. <code>List</code> | |
44 | * objects are now also supported for read/write. You simply need to define | |
45 | * a getter that returns the <code>List</code></li> | |
46 | * <li><strong>Mapped (<code>name(key)</code>)</strong> - The JavaBean | |
47 | * is assumed to have an property getter and setter methods with an | |
48 | * additional attribute of type <code>java.lang.String</code>.</li> | |
49 | * <li><strong>Combined (<code>name1.name2[index].name3(key)</code>)</strong> - | |
50 | * Combining mapped, nested, and indexed references is also | |
51 | * supported.</li> | |
52 | * </ul> | |
53 | * | |
54 | * @version $Revision: 473888 $ $Date: 2006-11-12 01:21:24 -0500 (Sun, 12 Nov 2006) $ | |
55 | * @since 1.8.0 | |
56 | */ | |
57 | public class DefaultResolver implements Resolver { | |
58 | ||
59 | private static final char NESTED = '.'; | |
60 | private static final char MAPPED_START = '('; | |
61 | private static final char MAPPED_END = ')'; | |
62 | private static final char INDEXED_START = '['; | |
63 | private static final char INDEXED_END = ']'; | |
64 | ||
65 | /** | |
66 | * Default Constructor. | |
67 | */ | |
68 | 200 | public DefaultResolver() { |
69 | 200 | } |
70 | ||
71 | /** | |
72 | * Return the index value from the property expression or -1. | |
73 | * | |
74 | * @param expression The property expression | |
75 | * @return The index value or -1 if the property is not indexed | |
76 | * @throws IllegalArgumentException If the indexed property is illegally | |
77 | * formed or has an invalid (non-numeric) value. | |
78 | */ | |
79 | public int getIndex(String expression) { | |
80 | 700 | if (expression == null || expression.length() == 0) { |
81 | 2 | return -1; |
82 | } | |
83 | 8332 | for (int i = 0; i < expression.length(); i++) { |
84 | 7900 | char c = expression.charAt(i); |
85 | 7900 | if (c == NESTED || c == MAPPED_START) { |
86 | 24 | return -1; |
87 | 7876 | } else if (c == INDEXED_START) { |
88 | 242 | int end = expression.indexOf(INDEXED_END, i); |
89 | 242 | if (end < 0) { |
90 | 1 | throw new IllegalArgumentException("Missing End Delimiter"); |
91 | } | |
92 | 241 | String value = expression.substring(i + 1, end); |
93 | 241 | if (value.length() == 0) { |
94 | 1 | throw new IllegalArgumentException("No Index Value"); |
95 | } | |
96 | 240 | int index = 0; |
97 | try { | |
98 | 240 | index = Integer.parseInt(value, 10); |
99 | 1 | } catch (Exception e) { |
100 | 1 | throw new IllegalArgumentException("Invalid index value '" |
101 | + value + "'"); | |
102 | 239 | } |
103 | 239 | return index; |
104 | } | |
105 | } | |
106 | 432 | return -1; |
107 | } | |
108 | ||
109 | /** | |
110 | * Return the map key from the property expression or <code>null</code>. | |
111 | * | |
112 | * @param expression The property expression | |
113 | * @return The index value | |
114 | * @throws IllegalArgumentException If the mapped property is illegally formed. | |
115 | */ | |
116 | public String getKey(String expression) { | |
117 | 589 | if (expression == null || expression.length() == 0) { |
118 | 2 | return null; |
119 | } | |
120 | 7325 | for (int i = 0; i < expression.length(); i++) { |
121 | 6897 | char c = expression.charAt(i); |
122 | 6897 | if (c == NESTED || c == INDEXED_START) { |
123 | 49 | return null; |
124 | 6848 | } else if (c == MAPPED_START) { |
125 | 110 | int end = expression.indexOf(MAPPED_END, i); |
126 | 110 | if (end < 0) { |
127 | 1 | throw new IllegalArgumentException("Missing End Delimiter"); |
128 | } | |
129 | 109 | return expression.substring(i + 1, end); |
130 | } | |
131 | } | |
132 | 428 | return null; |
133 | } | |
134 | ||
135 | /** | |
136 | * Return the property name from the property expression. | |
137 | * | |
138 | * @param expression The property expression | |
139 | * @return The property name | |
140 | */ | |
141 | public String getProperty(String expression) { | |
142 | 3303 | if (expression == null || expression.length() == 0) { |
143 | 8 | return expression; |
144 | } | |
145 | 42191 | for (int i = 0; i < expression.length(); i++) { |
146 | 39292 | char c = expression.charAt(i); |
147 | 39292 | if (c == NESTED) { |
148 | 4 | return expression.substring(0, i); |
149 | 39288 | } else if (c == MAPPED_START || c == INDEXED_START) { |
150 | 392 | return expression.substring(0, i); |
151 | } | |
152 | } | |
153 | 2899 | return expression; |
154 | } | |
155 | ||
156 | /** | |
157 | * Indicates whether or not the expression | |
158 | * contains nested property expressions or not. | |
159 | * | |
160 | * @param expression The property expression | |
161 | * @return The next property expression | |
162 | */ | |
163 | public boolean hasNested(String expression) { | |
164 | 5021 | if (expression == null || expression.length() == 0) { |
165 | 6 | return false; |
166 | } else { | |
167 | 5015 | return (remove(expression) != null); |
168 | } | |
169 | } | |
170 | ||
171 | /** | |
172 | * Indicate whether the expression is for an indexed property or not. | |
173 | * | |
174 | * @param expression The property expression | |
175 | * @return <code>true</code> if the expresion is indexed, | |
176 | * otherwise <code>false</code> | |
177 | */ | |
178 | public boolean isIndexed(String expression) { | |
179 | 1876 | if (expression == null || expression.length() == 0) { |
180 | 2 | return false; |
181 | } | |
182 | 22801 | for (int i = 0; i < expression.length(); i++) { |
183 | 21050 | char c = expression.charAt(i); |
184 | 21050 | if (c == NESTED || c == MAPPED_START) { |
185 | 11 | return false; |
186 | 21039 | } else if (c == INDEXED_START) { |
187 | 112 | return true; |
188 | } | |
189 | } | |
190 | 1751 | return false; |
191 | } | |
192 | ||
193 | /** | |
194 | * Indicate whether the expression is for a mapped property or not. | |
195 | * | |
196 | * @param expression The property expression | |
197 | * @return <code>true</code> if the expresion is mapped, | |
198 | * otherwise <code>false</code> | |
199 | */ | |
200 | public boolean isMapped(String expression) { | |
201 | 1964 | if (expression == null || expression.length() == 0) { |
202 | 2 | return false; |
203 | } | |
204 | 23780 | for (int i = 0; i < expression.length(); i++) { |
205 | 22010 | char c = expression.charAt(i); |
206 | 22010 | if (c == NESTED || c == INDEXED_START) { |
207 | 112 | return false; |
208 | 21898 | } else if (c == MAPPED_START) { |
209 | 80 | return true; |
210 | } | |
211 | } | |
212 | 1770 | return false; |
213 | } | |
214 | ||
215 | /** | |
216 | * Extract the next property expression from the | |
217 | * current expression. | |
218 | * | |
219 | * @param expression The property expression | |
220 | * @return The next property expression | |
221 | */ | |
222 | public String next(String expression) { | |
223 | 5467 | if (expression == null || expression.length() == 0) { |
224 | 0 | return null; |
225 | } | |
226 | 5467 | boolean indexed = false; |
227 | 5467 | boolean mapped = false; |
228 | 70148 | for (int i = 0; i < expression.length(); i++) { |
229 | 65586 | char c = expression.charAt(i); |
230 | 65586 | if (indexed) { |
231 | 495 | if (c == INDEXED_END) { |
232 | 243 | return expression.substring(0, i + 1); |
233 | } | |
234 | 65091 | } else if (mapped) { |
235 | 1686 | if (c == MAPPED_END) { |
236 | 149 | return expression.substring(0, i + 1); |
237 | } | |
238 | } else { | |
239 | 63405 | if (c == NESTED) { |
240 | 513 | return expression.substring(0, i); |
241 | 62892 | } else if (c == MAPPED_START) { |
242 | 149 | mapped = true; |
243 | 62743 | } else if (c == INDEXED_START) { |
244 | 243 | indexed = true; |
245 | } | |
246 | } | |
247 | } | |
248 | 4562 | return expression; |
249 | } | |
250 | ||
251 | /** | |
252 | * Remove the last property expresson from the | |
253 | * current expression. | |
254 | * | |
255 | * @param expression The property expression | |
256 | * @return The new expression value, with first property | |
257 | * expression removed - null if there are no more expressions | |
258 | */ | |
259 | public String remove(String expression) { | |
260 | 5236 | if (expression == null || expression.length() == 0) { |
261 | 0 | return null; |
262 | } | |
263 | 5236 | String property = next(expression); |
264 | 5236 | if (expression.length() == property.length()) { |
265 | 4795 | return null; |
266 | } | |
267 | 441 | int start = property.length(); |
268 | 441 | if (expression.charAt(start) == NESTED) { |
269 | 363 | start++; |
270 | } | |
271 | 441 | return expression.substring(start); |
272 | } | |
273 | } |