|
|||||||||||||||||||
Source file | Conditionals | Statements | Methods | TOTAL | |||||||||||||||
Accessor.java | 69,7% | 71,8% | 86,2% | 72,9% |
|
1 | /* | |
2 | * Copyright (c) 2001-2004, | |
3 | * RedVerst Group, ISP RAS http://www.ispras.ru | |
4 | * All rights reserved. | |
5 | * | |
6 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions are met: | |
8 | * | |
9 | * 1. Redistributions of source code must retain the above copyright notice, this | |
10 | * list of conditions and the following disclaimer. | |
11 | * | |
12 | * 2. Redistributions in binary form must reproduce the above copyright notice, | |
13 | * this list of conditions and the following disclaimer in the documentation | |
14 | * and/or other materials provided with the distribution. | |
15 | * | |
16 | * 3. The names "ATP", "TreeDL", "RedVerst", "ISP RAS" | |
17 | * may not be used to endorse or promote products derived from this software | |
18 | * without specific prior written permission. | |
19 | * | |
20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
22 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | |
24 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
26 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |
27 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
29 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
30 | */ | |
31 | ||
32 | package com.unitesk.atp.dynattrs; | |
33 | ||
34 | import java.lang.reflect.Array; | |
35 | import java.lang.reflect.Method; | |
36 | import java.util.HashMap; | |
37 | import java.util.List; | |
38 | import java.util.Map; | |
39 | import java.util.Set; | |
40 | ||
41 | /** | |
42 | * This is a collection of static methods to work with | |
43 | * different implementations of attributes. | |
44 | * | |
45 | * @author <A href="mailto:demakov@ispras.ru">Alexey Demakov</A> | |
46 | * @author <A href="mailto:monakhov@ispras.ru">Alexander Monakhov</A> | |
47 | * @version $Id: Accessor.java,v 1.8 2006/02/09 06:30:07 all-x Exp $ | |
48 | */ | |
49 | public class Accessor | |
50 | { | |
51 | //-------------------------------------------------------------------------- | |
52 | // access to properties using JavaBeans naming conventions | |
53 | ||
54 | /** | |
55 | * Converts the first char of a string to upper case. | |
56 | * | |
57 | * @param s The string to convert. | |
58 | * @return The parameter string with the first character converted | |
59 | * to uppercase if possible. | |
60 | * @throws NullPointerException | |
61 | * when <code>s == null</code>. | |
62 | */ | |
63 | 1 | public static String capitalize( String s ) |
64 | { | |
65 | 1 | if( s.length() == 0 ) |
66 | { | |
67 | 0 | return s; |
68 | } | |
69 | ||
70 | 1 | char chars[] = s.toCharArray(); |
71 | 1 | chars[0] = Character.toUpperCase( chars[0] ); |
72 | 1 | return new String( chars ); |
73 | } | |
74 | ||
75 | /** | |
76 | * Converts the first char of a string to lower case. | |
77 | * | |
78 | * @param s The string to convert. | |
79 | * @return The parameter string with the first character converted | |
80 | * to lowercase if possible. | |
81 | * @throws NullPointerException | |
82 | * when <code>s == null</code>. | |
83 | */ | |
84 | 45 | public static String decapitalize( String s ) |
85 | { | |
86 | 45 | if( s.length() == 0 ) |
87 | { | |
88 | 0 | return s; |
89 | } | |
90 | ||
91 | 45 | char chars[] = s.toCharArray(); |
92 | 45 | chars[0] = Character.toLowerCase( chars[0] ); |
93 | 45 | return new String( chars ); |
94 | } | |
95 | ||
96 | /** | |
97 | * Checks existence of bean property of the specified object. | |
98 | * | |
99 | * @param bean The specified object. | |
100 | * @param name The specified bean property name. | |
101 | * @return <code>true</code> if there are bean property read method. | |
102 | * @throws NullPointerException | |
103 | * If <code>bean</code> is <code>null</code>. | |
104 | * @throws AttributeException | |
105 | * Some possible reasons: | |
106 | * <UL> | |
107 | * <LI><CODE>name == null</CODE></LI> | |
108 | * </UL> | |
109 | * (analyze exception parameters). | |
110 | * @see #getBeanPropertyReadMethod(Object,String) | |
111 | */ | |
112 | 6 | public static boolean hasBeanProperty( Object bean, String name ) |
113 | { | |
114 | 6 | return getBeanPropertyReadMethod( bean, name ) != null; |
115 | } | |
116 | ||
117 | /** | |
118 | * Checks writability of bean property of the specified object. | |
119 | * | |
120 | * @param bean The specified object. | |
121 | * @param name The specified bean property name. | |
122 | * @return <code>true</code> if there are bean property write method. | |
123 | * @throws NullPointerException | |
124 | * If <code>bean</code> is <code>null</code>. | |
125 | * @throws AttributeException | |
126 | * Some possible reasons: | |
127 | * <UL> | |
128 | * <LI><CODE>name == null</CODE></LI> | |
129 | * </UL> | |
130 | * (analyze exception parameters). | |
131 | * @see #getBeanPropertyWriteMethod(Object,String) | |
132 | */ | |
133 | 5 | public static boolean isBeanPropertyWritable( Object bean, String name ) |
134 | { | |
135 | 5 | return getBeanPropertyWriteMethod( bean, name ) != null; |
136 | } | |
137 | ||
138 | /** | |
139 | * Returns a value of the specified bean property. | |
140 | * | |
141 | * @param bean The specified object. | |
142 | * @param name The specified bean property name. | |
143 | * @return The value of bean property of specified object. | |
144 | * @throws NullPointerException | |
145 | * If <code>bean</code> is <code>null</code>. | |
146 | * @throws AttributeException | |
147 | * Some possible reasons: | |
148 | * <UL> | |
149 | * <LI><CODE>name == null</CODE></LI> | |
150 | * <LI>there is no attribute with the specified name</LI> | |
151 | * <LI>there was an underlying exception</LI> | |
152 | * </UL> | |
153 | * (analyze exception parameters). | |
154 | * @see #hasBeanProperty(Object,String) | |
155 | */ | |
156 | 18 | public static Object getBeanProperty( Object bean, String name ) |
157 | { | |
158 | 18 | Method method = getBeanPropertyReadMethod( bean, name ); |
159 | ||
160 | 18 | if( method == null ) |
161 | { | |
162 | 1 | throw new AttributeException( bean, name ); |
163 | } | |
164 | ||
165 | 17 | try |
166 | { | |
167 | 17 | return method.invoke( bean, get_par ); |
168 | } | |
169 | catch( Exception e ) | |
170 | { | |
171 | 0 | throw new AttributeException( bean, name, null, e ); |
172 | } | |
173 | } | |
174 | ||
175 | /** | |
176 | * Checks if a value of the bean property can be indexed. | |
177 | * This method uses {@link #getBeanProperty(Object, String) getBeanProperty(bean,name)} | |
178 | * to get property value. | |
179 | * | |
180 | * @param bean The specified object. | |
181 | * @param name The specified bean property name. | |
182 | * @return <code>true</code> if value of bean property {@link #isIndexed(Object) is indexed}. | |
183 | * @throws NullPointerException | |
184 | * If <code>bean</code> is <code>null</code>. | |
185 | * @throws AttributeException | |
186 | * Some possible reasons: | |
187 | * <UL> | |
188 | * <LI><CODE>name == null</CODE></LI> | |
189 | * <LI>there is no attribute with the specified name</LI> | |
190 | * <LI>attribute value is null</LI> | |
191 | * <LI>there was an underlying exception</LI> | |
192 | * </UL> | |
193 | * (analyze exception parameters). | |
194 | * @throws AttributeException | |
195 | * If bean property with the specified name doesn't exist (cause == null), | |
196 | * or there was an underlying exception (see cause). | |
197 | */ | |
198 | 6 | public static boolean isBeanPropertyIndexed( Object bean, String name ) |
199 | { | |
200 | 6 | Object value = getBeanProperty( bean, name ); |
201 | ||
202 | 5 | try |
203 | { | |
204 | 5 | return isIndexed( value ); |
205 | } | |
206 | catch( Exception e ) | |
207 | { | |
208 | 0 | throw new AttributeException( bean, name, value, e ); |
209 | } | |
210 | } | |
211 | ||
212 | /** | |
213 | * Sets the new value of the specified bean property. | |
214 | * | |
215 | * @param bean The specified object. | |
216 | * @param name The specified bean property name. | |
217 | * @param value The new value of bean property. | |
218 | * @throws NullPointerException | |
219 | * If <code>bean</code> is <code>null</code>. | |
220 | * @throws AttributeException | |
221 | * Some possible reasons: | |
222 | * <UL> | |
223 | * <LI><CODE>name == null</CODE></LI> | |
224 | * <LI>there is no attribute with the specified name</LI> | |
225 | * <LI>there was an underlying exception</LI> | |
226 | * </UL> | |
227 | * (analyze exception parameters). | |
228 | */ | |
229 | 1 | public static void setBeanProperty( Object bean, String name, Object value ) |
230 | { | |
231 | 1 | Method method = getBeanPropertyWriteMethod( bean, name ); |
232 | ||
233 | 1 | if( method == null ) |
234 | { | |
235 | 0 | throw new AttributeException( bean, name, value ); |
236 | } | |
237 | ||
238 | 1 | set_par[0] = value; |
239 | 1 | try |
240 | { | |
241 | 1 | method.invoke( bean, set_par ); |
242 | } | |
243 | catch( Exception e ) | |
244 | { | |
245 | 0 | throw new AttributeException( bean, name, value, e ); |
246 | } | |
247 | } | |
248 | ||
249 | /** | |
250 | * Returns the set of names of bean properties of the specified object. | |
251 | * @param bean The specified object. | |
252 | * @return The set that contains names of all object's bean properties. | |
253 | * @throws NullPointerException | |
254 | * If bean is null. | |
255 | */ | |
256 | 6 | public static Set/*String*/ getBeanPropertyNames( Object bean ) |
257 | { | |
258 | 6 | return getBeanClassPropertyNames( bean.getClass() ); |
259 | } | |
260 | ||
261 | /** | |
262 | * Returns the set of names of bean properties for objects of the specified class. | |
263 | * @param cls The specified class. | |
264 | * @return The set that contains names of bean properties for objects of | |
265 | * the specified class. | |
266 | * @throws NullPointerException | |
267 | * If cls is null. | |
268 | */ | |
269 | 7 | public static Set/*String*/ getBeanClassPropertyNames( Class cls ) |
270 | { | |
271 | 7 | return getBeanClassPropertyDescriptors( cls ).keySet(); |
272 | } | |
273 | ||
274 | //-------------------------------------------------------------------------- | |
275 | // indexed values | |
276 | ||
277 | /** | |
278 | * Checks if value can be indexed. | |
279 | * A value is indexed if it is an array or instance of {@link List}. | |
280 | * <CODE>null</CODE> is not indexed value. | |
281 | * | |
282 | * @param value The specified value. | |
283 | * @return <code>true</code> if the specified value is array | |
284 | * or instance of {@link List}. | |
285 | */ | |
286 | 11 | public static boolean isIndexed( Object value ) |
287 | { | |
288 | 11 | return value != null && (value.getClass().isArray() || value instanceof List); |
289 | } | |
290 | ||
291 | /** | |
292 | * Returns the size of the indexed value. | |
293 | * | |
294 | * @param value The specified indexed value. | |
295 | * @return Size of the specified indexed value. | |
296 | * @throws NullPointerException | |
297 | * If the specified value is <code>null</code>. | |
298 | * @throws ClassCastException | |
299 | * If the specified value {@link #isIndexed(Object) is not indexed}. | |
300 | */ | |
301 | 5 | public static int sizeIndexed( Object value ) |
302 | { | |
303 | 5 | if( value.getClass().isArray() ) |
304 | { | |
305 | 3 | return Array.getLength( value ); |
306 | } | |
307 | 2 | return ((List)value).size(); |
308 | } | |
309 | ||
310 | /** | |
311 | * Returns element of the specified index value. | |
312 | * | |
313 | * @param value The specified indexed value. | |
314 | * @param index The specified index. | |
315 | * @return An element of array or list with the specified index. | |
316 | * @throws NullPointerException | |
317 | * If the specified value is <code>null</code>. | |
318 | * @throws ClassCastException | |
319 | * If the specified value {@link #isIndexed(Object) is not indexed}. | |
320 | * @throws IndexOutOfBoundsException | |
321 | * If the specified index is out of bounds of indexed value. | |
322 | */ | |
323 | 6 | public static Object getIndexed( Object value, int index ) |
324 | { | |
325 | 6 | if( value.getClass().isArray() ) |
326 | { | |
327 | 2 | return Array.get( value, index ); |
328 | } | |
329 | 4 | return ((List)value).get( index ); |
330 | } | |
331 | ||
332 | /** | |
333 | * Sets the new element's value of indexed value. | |
334 | * | |
335 | * @param value The specified indexed value. | |
336 | * @param index The specified index. | |
337 | * @param elem The new value of element. | |
338 | * @throws NullPointerException | |
339 | * If <code>value</code> is <code>null</code>. | |
340 | * @throws ClassCastException | |
341 | * If the specified value {@link #isIndexed(Object) is not indexed}. | |
342 | * @throws IndexOutOfBoundsException | |
343 | * If the specified index is out of bounds of indexed value. | |
344 | */ | |
345 | 1 | public static void setIndexed( Object value, int index, Object elem ) |
346 | { | |
347 | 1 | if( value.getClass().isArray() ) |
348 | { | |
349 | 0 | Array.set( value, index, elem ); |
350 | } else { | |
351 | 1 | ((List)value).set( index, elem ); |
352 | } | |
353 | } | |
354 | ||
355 | //-------------------------------------------------------------------------- | |
356 | // attributes for any object. if object is instance of Attributed, | |
357 | // interface Attributed is used else interface JavaBeans is used | |
358 | ||
359 | /** | |
360 | * Checks existence of an attribute with the specified name of the specified object. | |
361 | * | |
362 | * @param obj The specified object. | |
363 | * @param name The specified attribute name. | |
364 | * @return If <code>obj</code> implements {@link Attributed} interface use | |
365 | * {@link Attributed#hasAttribute(String) its method} to get attribute value. | |
366 | * Otherwise {@link #hasBeanProperty(Object,String) check existence of bean property}. | |
367 | * @throws NullPointerException | |
368 | * If <code>obj</code> is <code>null</code>. | |
369 | * @throws AttributeException | |
370 | * Some possible reasons: | |
371 | * <UL> | |
372 | * <LI><CODE>name == null</CODE></LI> | |
373 | * <LI>there was an underlying exception</LI> | |
374 | * </UL> | |
375 | * (analyze exception parameters). | |
376 | * @since 3.6.3 | |
377 | */ | |
378 | 0 | public static boolean hasAttribute( Object obj, String name ) |
379 | { | |
380 | 0 | if( obj instanceof Attributed ) |
381 | { | |
382 | 0 | return ((Attributed)obj).hasAttribute( name ); |
383 | } else { | |
384 | 0 | return hasBeanProperty( obj, name ); |
385 | } | |
386 | } | |
387 | ||
388 | /** | |
389 | * Returns value of attribute of the specified object. | |
390 | * | |
391 | * @param obj The specified object. | |
392 | * @param name The specified attribute name. | |
393 | * @return If <code>obj</code> implements {@link Attributed} interface use | |
394 | * {@link Attributed#getAttribute(String) its method} to get attribute value. | |
395 | * Otherwise return {@link #getBeanProperty(Object,String) bean property value}. | |
396 | * @throws NullPointerException | |
397 | * If <code>obj</code> is <code>null</code>. | |
398 | * @throws AttributeException | |
399 | * Some possible reasons: | |
400 | * <UL> | |
401 | * <LI><CODE>name == null</CODE></LI> | |
402 | * <LI>there is no attribute with the specified name</LI> | |
403 | * <LI>there was an underlying exception</LI> | |
404 | * </UL> | |
405 | * (analyze exception parameters). | |
406 | */ | |
407 | 4 | public static Object getAttribute( Object obj, String name ) |
408 | { | |
409 | 4 | if( obj instanceof Attributed ) |
410 | { | |
411 | 1 | return ((Attributed)obj).getAttribute( name ); |
412 | } else { | |
413 | 3 | return getBeanProperty( obj, name ); |
414 | } | |
415 | } | |
416 | ||
417 | /** | |
418 | * Returns size of indexed attribute of the specified object. | |
419 | * | |
420 | * @param obj The specified object. | |
421 | * @param name The specified attribute name. | |
422 | * @return If <code>obj</code> implements {@link Attributed} interface use | |
423 | * {@link Attributed#sizeAttribute(String) its method} to get attribute size. | |
424 | * Otherwise {@link #getAttribute(Object,String) get attribute value} | |
425 | * and return {@link #sizeIndexed(Object) its size}. | |
426 | * @throws NullPointerException | |
427 | * If <code>obj</code> is <code>null</code>. | |
428 | * @throws AttributeException | |
429 | * Some possible reasons: | |
430 | * <UL> | |
431 | * <LI><CODE>name == null</CODE></LI> | |
432 | * <LI>there is no attribute with the specified name</LI> | |
433 | * <LI>attribute value is not indexed</LI> | |
434 | * <LI>there was an underlying exception</LI> | |
435 | * </UL> | |
436 | * (analyze exception parameters). | |
437 | */ | |
438 | 1 | public static int sizeAttribute( Object obj, String name ) |
439 | { | |
440 | 1 | if( obj instanceof Attributed ) |
441 | { | |
442 | 1 | return ((Attributed)obj).sizeAttribute( name ); |
443 | } else { | |
444 | 0 | Object value = getAttribute( obj, name ); |
445 | 0 | try |
446 | { | |
447 | 0 | return sizeIndexed( value ); |
448 | } | |
449 | catch( Exception e ) | |
450 | { | |
451 | 0 | throw new AttributeException( obj, name, value, e ); |
452 | } | |
453 | } | |
454 | } | |
455 | ||
456 | /** | |
457 | * Returns element of indexed attribute by index. | |
458 | * | |
459 | * @param obj The specified object. | |
460 | * @param name The specified attribute name. | |
461 | * @param index The specified index. | |
462 | * @return If <code>obj</code> implements {@link Attributed} interface use | |
463 | * {@link Attributed#getAttribute(String,int) its method} to get | |
464 | * attribute element. | |
465 | * Otherwise {@link #getAttribute(Object,String) get attribute value} | |
466 | * and return {@link #getIndexed(Object,int) its element by index}. | |
467 | * @throws NullPointerException | |
468 | * If <code>obj</code> is <code>null</code>. | |
469 | * @throws AttributeException | |
470 | * Some possible reasons: | |
471 | * <UL> | |
472 | * <LI><CODE>name == null</CODE></LI> | |
473 | * <LI>there is no attribute with the specified name</LI> | |
474 | * <LI>attribute value is not indexed</LI> | |
475 | * <LI>there was an underlying exception</LI> | |
476 | * </UL> | |
477 | * (analyze exception parameters). | |
478 | */ | |
479 | 2 | public static Object getAttribute( Object obj, String name, int index ) |
480 | { | |
481 | 2 | if( obj instanceof Attributed ) |
482 | { | |
483 | 1 | return ((Attributed)obj).getAttribute( name, index ); |
484 | } else { | |
485 | 1 | Object value = getAttribute( obj, name ); |
486 | 1 | try |
487 | { | |
488 | 1 | return getIndexed( value, index ); |
489 | } | |
490 | catch( Exception e ) | |
491 | { | |
492 | 0 | throw new AttributeException( obj, name, value, e ); |
493 | } | |
494 | } | |
495 | } | |
496 | ||
497 | /** | |
498 | * Sets the new value of attribute. | |
499 | * If <code>obj</code> implements {@link Attributed} interface use | |
500 | * {@link Attributed#setAttribute(String,Object) its method} to set | |
501 | * attribute. | |
502 | * Otherwise tries {@link #setBeanProperty(Object, String, Object) to set bean property} | |
503 | * | |
504 | * @param obj The specified object. | |
505 | * @param name The specified attribute name. | |
506 | * @param value The new value of attribute. | |
507 | * @throws AttributeException | |
508 | * Some possible reasons: | |
509 | * <UL> | |
510 | * <LI><CODE>name == null</CODE></LI> | |
511 | * <LI>there is no attribute with the specified name</LI> | |
512 | * <LI>attribute is not writable</LI> | |
513 | * <LI>attribute element type is not compatible with <CODE>elem</CODE></LI> | |
514 | * <LI>there was an underlying exception</LI> | |
515 | * </UL> | |
516 | * (analyze exception parameters). | |
517 | * @since 3.5.3 | |
518 | */ | |
519 | 0 | public static void setAttribute( Object obj |
520 | , String name | |
521 | , Object value | |
522 | ) | |
523 | { | |
524 | 0 | if( obj instanceof Attributed ) |
525 | { | |
526 | 0 | ((Attributed)obj).setAttribute( name, value); |
527 | } else { | |
528 | 0 | setBeanProperty( obj, name, value ); |
529 | } | |
530 | } | |
531 | ||
532 | /** | |
533 | * Sets the new value of element of indexed attribute by index. | |
534 | * If <code>obj</code> implements {@link Attributed} interface use | |
535 | * {@link Attributed#setAttribute(String,int,Object) its method} to set | |
536 | * attribute element. | |
537 | * Otherwise {@link #getAttribute(Object,String) get attribute value} | |
538 | * and {@link #setIndexed(Object,int,Object) set its element by index}. | |
539 | * | |
540 | * @param obj The specified object. | |
541 | * @param name The specified attribute name. | |
542 | * @param index The specified index. | |
543 | * @param elem The new value of element. | |
544 | * @throws AttributeException | |
545 | * Some possible reasons: | |
546 | * <UL> | |
547 | * <LI><CODE>name == null</CODE></LI> | |
548 | * <LI>there is no attribute with the specified name</LI> | |
549 | * <LI>attribute is not writable</LI> | |
550 | * <LI>attribute is not indexed</LI> | |
551 | * <LI>attribute index is out of bounds</LI> | |
552 | * <LI>attribute element type is not compatible with <CODE>elem</CODE></LI> | |
553 | * <LI>there was an underlying exception</LI> | |
554 | * </UL> | |
555 | * (analyze exception parameters). | |
556 | */ | |
557 | 0 | public static void setAttribute( Object obj |
558 | , String name | |
559 | , int index | |
560 | , Object elem | |
561 | ) | |
562 | { | |
563 | 0 | if( obj instanceof Attributed ) |
564 | { | |
565 | 0 | ((Attributed)obj).setAttribute( name, index, elem); |
566 | } else { | |
567 | 0 | Object value = getAttribute( obj, name ); |
568 | 0 | try |
569 | { | |
570 | 0 | setIndexed( value, index, elem ); |
571 | } | |
572 | catch( Exception e ) | |
573 | { | |
574 | 0 | throw new AttributeException( obj, name, value, e ); |
575 | } | |
576 | } | |
577 | } | |
578 | ||
579 | /** | |
580 | * Returns the set of attribute names the specified object. | |
581 | * @param obj The specified object. | |
582 | * @return If <code>obj</code> implements {@link Attributed} interface use | |
583 | * {@link Attributed#getAttributeNames() its method} to get attribute names. | |
584 | * Otherwise {@link #getBeanPropertyNames(Object) get bean property names}. | |
585 | */ | |
586 | 0 | public static Set/*String*/ getAttributeNames( Object obj ) |
587 | { | |
588 | 0 | if( obj instanceof Attributed ) |
589 | { | |
590 | 0 | return ((Attributed)obj).getAttributeNames(); |
591 | } else { | |
592 | 0 | return getBeanPropertyNames( obj ); |
593 | } | |
594 | } | |
595 | ||
596 | /** | |
597 | * Checks whether the given name is a valid identifier. | |
598 | * This method uses {@link Character#isUnicodeIdentifierStart(char)} | |
599 | * and {@link Character#isUnicodeIdentifierPart(char)} methods. | |
600 | * | |
601 | * @param name the name to check | |
602 | * @return <code>true</code> if the given name is a valid identifier, | |
603 | * <code>false</code> otherwise. | |
604 | */ | |
605 | 3 | public static boolean isID( String name ) |
606 | { | |
607 | 3 | int l = name.length(); |
608 | ||
609 | 3 | if( l == 0 ) |
610 | { | |
611 | 0 | return false; |
612 | } | |
613 | ||
614 | 3 | char[] chars = name.toCharArray(); |
615 | ||
616 | 3 | if( !Character.isUnicodeIdentifierStart( chars[0] ) ) |
617 | { | |
618 | 2 | return false; |
619 | } | |
620 | ||
621 | 1 | for( int i = 1; i < chars.length; i++ ) |
622 | { | |
623 | 2 | if( !Character.isUnicodeIdentifierPart( chars[i] ) ) |
624 | { | |
625 | 0 | return false; |
626 | } | |
627 | } | |
628 | 1 | return true; |
629 | } | |
630 | ||
631 | /** | |
632 | * Возвращает значение, на которое указывает заданная цепочка атрибутов для данного объекта. | |
633 | * Цепочка атрибутов имеет следующий вид: | |
634 | * <P><BLOCKQUOTE><code> | |
635 | * path ::= attribute ( "." attribute )* ;<BR> | |
636 | * attribute ::= <attr_name:ID> ( "[" index "]" )? ;<BR> | |
637 | * index ::= <index_var:ID> | number ;<BR> | |
638 | * number ::= ( "-" )? ( <DIGIT> )+ ;<BR> | |
639 | * </BLOCKQUOTE></code> | |
640 | * <P>Каждый элемент цепочки является либо именем атрибута, либо именем списочного атрибута | |
641 | * с указанным в скобках индексом элемента. Индекс может быть задан именем переменной | |
642 | * <code>index_var</code>, значение которой берется из <code>variable_map</code> как значение | |
643 | * соответствующего атрибута, или целочисленным литералом <code>number</code>. | |
644 | * Отрицательному индексу -i соответствует элемент списка с индексом L-i, где L - длина списка. | |
645 | * <P>Значение, на которое указывает цепочка атрибутов, определяется рекурсивно: | |
646 | * <UL> | |
647 | * <LI>Для цепочки длины нуль это значение самого объекта.</LI> | |
648 | * <LI>Для цепочки длины k рекурсивно находим объект, на который указывает цепочка длины k-1. | |
649 | * Результатом является значение k-го элемента цепочки, применённого к этого объекту.</LI> | |
650 | * </UL> | |
651 | * | |
652 | * @param obj The specified object. | |
653 | * @param path The chain of attribute names. | |
654 | * @param variableMap Defined variables. | |
655 | * @return The value pointed by path of attributes. | |
656 | * @see #getAttribute(Object,String) | |
657 | * @see #getAttribute(Object, String, int) | |
658 | */ | |
659 | 2 | public static Object getAttribute( Object obj |
660 | , String path | |
661 | , Attributed variableMap | |
662 | ) | |
663 | { | |
664 | 2 | int from = 0; |
665 | 2 | int to = 0; |
666 | ||
667 | 2 | Object nextobj; |
668 | 2 | String attrname; |
669 | 2 | int realindex = -1; |
670 | ||
671 | 2 | while( true ) |
672 | { | |
673 | 3 | to = path.indexOf( '.', from ); |
674 | 3 | if( to == -1 ) |
675 | { | |
676 | 2 | to = path.length(); |
677 | } | |
678 | ||
679 | 3 | int bracket = path.indexOf ( '[', from ); |
680 | 3 | if( bracket >= 0 && bracket < to ) |
681 | { | |
682 | ||
683 | 1 | attrname = path.substring( from, bracket ); |
684 | 1 | if( path.charAt( to - 1 ) != ']' ) |
685 | { | |
686 | 0 | throw new AttributePathException( AttributePathException.NO_CLOSING_BRACKET |
687 | , obj | |
688 | , path | |
689 | , bracket | |
690 | , null | |
691 | ); | |
692 | } | |
693 | ||
694 | 1 | String index_string = path.substring( bracket + 1, to - 1 ); |
695 | ||
696 | 1 | if( isID( index_string ) ) // if NAME |
697 | { | |
698 | 0 | Object mapped_index; |
699 | 0 | try |
700 | { | |
701 | 0 | mapped_index = variableMap.getAttribute( index_string ); |
702 | } | |
703 | catch( AttributeException e ) | |
704 | { | |
705 | 0 | throw new AttributePathException |
706 | ( AttributePathException.ATTRIBUTED_EXCEPTION | |
707 | , obj | |
708 | , path | |
709 | , bracket + 1 | |
710 | , e | |
711 | ); | |
712 | } | |
713 | 0 | try |
714 | { | |
715 | 0 | realindex = ((Integer)mapped_index).intValue(); |
716 | } | |
717 | catch( ClassCastException e ) | |
718 | { | |
719 | 0 | throw new AttributePathException( AttributePathException.INDEX_VALUE |
720 | , obj | |
721 | , path | |
722 | , bracket + 1 | |
723 | , e | |
724 | ); | |
725 | } | |
726 | } else { | |
727 | // if DIGITAL INDEX | |
728 | 1 | try |
729 | { | |
730 | 1 | realindex = Integer.parseInt( index_string ); |
731 | } | |
732 | catch ( NumberFormatException e ) | |
733 | { | |
734 | 0 | throw new AttributePathException( AttributePathException.INDEX_VALUE |
735 | , obj | |
736 | , path | |
737 | , bracket + 1 | |
738 | , e | |
739 | ); | |
740 | } | |
741 | 1 | if( realindex < 0 ) |
742 | { | |
743 | 0 | try |
744 | { | |
745 | 0 | realindex = sizeAttribute( obj, attrname ) + realindex; |
746 | } | |
747 | catch( AttributeException e ) | |
748 | { | |
749 | 0 | throw new AttributePathException |
750 | ( AttributePathException.ATTRIBUTED_EXCEPTION | |
751 | , obj | |
752 | , path | |
753 | , from | |
754 | , e | |
755 | ); | |
756 | } | |
757 | } | |
758 | } | |
759 | 1 | try |
760 | { | |
761 | 1 | nextobj = getAttribute( obj, attrname, realindex ); |
762 | } | |
763 | catch( AttributeException e ) | |
764 | { | |
765 | 0 | throw new AttributePathException( AttributePathException.ATTRIBUTED_EXCEPTION |
766 | , obj | |
767 | , path | |
768 | , from | |
769 | , e | |
770 | ); | |
771 | } | |
772 | } else { | |
773 | 2 | attrname = path.substring( from, to ); |
774 | 2 | try |
775 | { | |
776 | 2 | nextobj = getAttribute( obj, attrname ); |
777 | } | |
778 | catch( AttributeException e ) | |
779 | { | |
780 | 0 | throw new AttributePathException( AttributePathException.ATTRIBUTED_EXCEPTION |
781 | , obj | |
782 | , path | |
783 | , from | |
784 | , e | |
785 | ); | |
786 | } | |
787 | } | |
788 | ||
789 | 3 | if( to != path.length() ) |
790 | { | |
791 | 1 | from = to + 1; |
792 | 1 | realindex = -1; |
793 | 1 | obj = nextobj; |
794 | } else { | |
795 | 2 | return nextobj; |
796 | } | |
797 | } | |
798 | } | |
799 | ||
800 | //-------------------------------------------------------------------------- | |
801 | // auxilary methods | |
802 | ||
803 | private static class PropertyDescriptor | |
804 | { | |
805 | private Method read_method; | |
806 | private Method write_method; | |
807 | ||
808 | 44 | public PropertyDescriptor( Method read_method, Method write_method ) |
809 | { | |
810 | 44 | this.read_method = read_method; |
811 | 44 | this.write_method = write_method; |
812 | } | |
813 | ||
814 | 21 | public Method getReadMethod() |
815 | { | |
816 | 21 | return read_method; |
817 | } | |
818 | ||
819 | 5 | public Method getWriteMethod() |
820 | { | |
821 | 5 | return write_method; |
822 | } | |
823 | } | |
824 | ||
825 | 36 | private static Map/*Class,Map(String,PropertyDescriptor)*/ |
826 | getBeanClassPropertyDescriptors( Class cls ) | |
827 | { | |
828 | 36 | if( class_to_properties_descriptor.containsKey( cls ) ) |
829 | { | |
830 | 32 | return (Map/*Class,Map(String,PropertyDescriptor)*/) |
831 | class_to_properties_descriptor.get( cls ); | |
832 | } | |
833 | ||
834 | 4 | Method[] methods = cls.getMethods(); |
835 | 4 | Map/*String,PropertyDescriptor*/ res |
836 | = new HashMap/*String,PropertyDescriptor*/(); | |
837 | ||
838 | 4 | for( int i = 0; i < methods.length; i++ ) |
839 | { | |
840 | 186 | if( methods[i].getParameterTypes().length == 0 |
841 | && !methods[i].getReturnType().equals( void.class ) | |
842 | ) | |
843 | { | |
844 | 72 | String method_name = methods[i].getName(); |
845 | ||
846 | // if( methods[i].getReturnType().equals( boolean.class ) | |
847 | // && method_name.startsWith( "is" ) | |
848 | // && method_name.length() > 2 | |
849 | // ) | |
850 | // { | |
851 | // String capitalized_property_name | |
852 | // = method_name.substring( 2 ); | |
853 | // Method read_method = methods[i]; | |
854 | // | |
855 | // res.put( decapitalize( capitalized_property_name ) | |
856 | // , new PropertyDescriptor | |
857 | // ( read_method | |
858 | // , findWriteMethod( cls | |
859 | // , read_method | |
860 | // , capitalized_property_name | |
861 | // ) | |
862 | // ) | |
863 | // ); | |
864 | // } else | |
865 | 72 | if( method_name.startsWith( "get" ) |
866 | && method_name.length() > "get".length() | |
867 | ) | |
868 | { | |
869 | 44 | String capitalized_property_name |
870 | = method_name.substring( "get".length() ); | |
871 | 44 | Method read_method = methods[i]; |
872 | 44 | read_method.setAccessible( true ); |
873 | ||
874 | 44 | res.put( decapitalize( capitalized_property_name ) |
875 | , new PropertyDescriptor | |
876 | ( read_method | |
877 | , findWriteMethod( cls | |
878 | , read_method | |
879 | , capitalized_property_name | |
880 | ) | |
881 | ) | |
882 | ); | |
883 | } | |
884 | } | |
885 | } | |
886 | ||
887 | 4 | class_to_properties_descriptor.put( cls, res ); |
888 | ||
889 | 4 | return res; |
890 | } | |
891 | ||
892 | 44 | private static Method findWriteMethod( Class cls |
893 | , Method read_method | |
894 | , String capitalizedPropertyName | |
895 | ) | |
896 | { | |
897 | 44 | try |
898 | { | |
899 | 44 | set_type[0] = read_method.getReturnType(); |
900 | 44 | Method write_method = cls.getMethod( "set" + capitalizedPropertyName |
901 | , set_type | |
902 | ); | |
903 | 4 | if( write_method != null ) write_method.setAccessible( true ); |
904 | 4 | return write_method; |
905 | } | |
906 | catch( Exception e ) | |
907 | { | |
908 | 40 | return null; |
909 | } | |
910 | } | |
911 | ||
912 | /** | |
913 | * Returns read method for the specified property of the specified bean. | |
914 | * @param bean Object to get read method from. | |
915 | * @param name Property name. | |
916 | * @return Read method or <code>null</code> if object has | |
917 | * no property with such name. | |
918 | * @throws NullPointerException | |
919 | * If <code>bean</code> is <code>null</code>. | |
920 | * @throws AttributeException | |
921 | * Some possible reasons: | |
922 | * <UL> | |
923 | * <LI><CODE>name == null</CODE></LI> | |
924 | * </UL> | |
925 | * (analyze exception parameters). | |
926 | */ | |
927 | 24 | protected static Method getBeanPropertyReadMethod( Object bean |
928 | , String name | |
929 | ) | |
930 | { | |
931 | 24 | if( name == null ) |
932 | { | |
933 | 1 | throw new AttributeException( bean, name ); |
934 | } | |
935 | 23 | PropertyDescriptor pd |
936 | = (PropertyDescriptor) | |
937 | getBeanClassPropertyDescriptors( bean.getClass() ) | |
938 | .get( name ); | |
939 | ||
940 | 23 | return pd != null ? pd.getReadMethod() : null; |
941 | } | |
942 | ||
943 | /** | |
944 | * Returns write method for the specified property of the specified bean. | |
945 | * @param bean Object to get write method from. | |
946 | * @param name Property name. | |
947 | * @return Write method or <code>null</code> if object has | |
948 | * no property with such name. | |
949 | * @throws NullPointerException | |
950 | * If <code>bean</code> is <code>null</code>. | |
951 | * @throws AttributeException | |
952 | * Some possible reasons: | |
953 | * <UL> | |
954 | * <LI><CODE>name == null</CODE></LI> | |
955 | * </UL> | |
956 | * (analyze exception parameters). | |
957 | */ | |
958 | 6 | protected static Method getBeanPropertyWriteMethod( Object bean |
959 | , String name | |
960 | ) | |
961 | { | |
962 | 6 | if( name == null ) |
963 | { | |
964 | 0 | throw new AttributeException( bean, name ); |
965 | } | |
966 | 6 | PropertyDescriptor pd |
967 | = (PropertyDescriptor) | |
968 | getBeanClassPropertyDescriptors( bean.getClass() ) | |
969 | .get( name ); | |
970 | ||
971 | 6 | return pd != null ? pd.getWriteMethod() : null; |
972 | } | |
973 | ||
974 | private static Class[] set_type = new Class[1]; | |
975 | ||
976 | private static Object[] get_par = new Object[0]; | |
977 | private static Object[] set_par = new Object[1]; | |
978 | ||
979 | private static Map/*Class,Map(String,PropertyDescriptor)*/ | |
980 | class_to_properties_descriptor | |
981 | = new HashMap/*Class,Map(String,PropertyDescriptor)*/(); | |
982 | } |
|