1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 06:21:24 +0000 (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 public DefaultResolver() {
69 }
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 if (expression == null || expression.length() == 0) {
81 return -1;
82 }
83 for (int i = 0; i < expression.length(); i++) {
84 char c = expression.charAt(i);
85 if (c == NESTED || c == MAPPED_START) {
86 return -1;
87 } else if (c == INDEXED_START) {
88 int end = expression.indexOf(INDEXED_END, i);
89 if (end < 0) {
90 throw new IllegalArgumentException("Missing End Delimiter");
91 }
92 String value = expression.substring(i + 1, end);
93 if (value.length() == 0) {
94 throw new IllegalArgumentException("No Index Value");
95 }
96 int index = 0;
97 try {
98 index = Integer.parseInt(value, 10);
99 } catch (Exception e) {
100 throw new IllegalArgumentException("Invalid index value '"
101 + value + "'");
102 }
103 return index;
104 }
105 }
106 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 if (expression == null || expression.length() == 0) {
118 return null;
119 }
120 for (int i = 0; i < expression.length(); i++) {
121 char c = expression.charAt(i);
122 if (c == NESTED || c == INDEXED_START) {
123 return null;
124 } else if (c == MAPPED_START) {
125 int end = expression.indexOf(MAPPED_END, i);
126 if (end < 0) {
127 throw new IllegalArgumentException("Missing End Delimiter");
128 }
129 return expression.substring(i + 1, end);
130 }
131 }
132 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 if (expression == null || expression.length() == 0) {
143 return expression;
144 }
145 for (int i = 0; i < expression.length(); i++) {
146 char c = expression.charAt(i);
147 if (c == NESTED) {
148 return expression.substring(0, i);
149 } else if (c == MAPPED_START || c == INDEXED_START) {
150 return expression.substring(0, i);
151 }
152 }
153 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 if (expression == null || expression.length() == 0) {
165 return false;
166 } else {
167 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 if (expression == null || expression.length() == 0) {
180 return false;
181 }
182 for (int i = 0; i < expression.length(); i++) {
183 char c = expression.charAt(i);
184 if (c == NESTED || c == MAPPED_START) {
185 return false;
186 } else if (c == INDEXED_START) {
187 return true;
188 }
189 }
190 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 if (expression == null || expression.length() == 0) {
202 return false;
203 }
204 for (int i = 0; i < expression.length(); i++) {
205 char c = expression.charAt(i);
206 if (c == NESTED || c == INDEXED_START) {
207 return false;
208 } else if (c == MAPPED_START) {
209 return true;
210 }
211 }
212 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 if (expression == null || expression.length() == 0) {
224 return null;
225 }
226 boolean indexed = false;
227 boolean mapped = false;
228 for (int i = 0; i < expression.length(); i++) {
229 char c = expression.charAt(i);
230 if (indexed) {
231 if (c == INDEXED_END) {
232 return expression.substring(0, i + 1);
233 }
234 } else if (mapped) {
235 if (c == MAPPED_END) {
236 return expression.substring(0, i + 1);
237 }
238 } else {
239 if (c == NESTED) {
240 return expression.substring(0, i);
241 } else if (c == MAPPED_START) {
242 mapped = true;
243 } else if (c == INDEXED_START) {
244 indexed = true;
245 }
246 }
247 }
248 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 if (expression == null || expression.length() == 0) {
261 return null;
262 }
263 String property = next(expression);
264 if (expression.length() == property.length()) {
265 return null;
266 }
267 int start = property.length();
268 if (expression.charAt(start) == NESTED) {
269 start++;
270 }
271 return expression.substring(start);
272 }
273 }