1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.commons.clazz.reflect.common;
17
18 import java.lang.reflect.Array;
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 import java.util.AbstractList;
22 import java.util.ArrayList;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.ListIterator;
26
27 import org.apache.commons.clazz.ClazzAccessException;
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 public class ReflectedList extends AbstractList {
51
52
53
54
55
56
57
58 private Object instance;
59 private ReflectedListProperty property;
60
61 private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
62
63
64
65
66 public ReflectedList(Object instance, ReflectedListProperty property) {
67 this.instance = instance;
68 this.property = property;
69 }
70
71 public Object getPropertyValue() {
72 Method readMethod = property.getReadMethod();
73 if (readMethod == null) {
74 throw new ClazzAccessException(
75 "Cannot read property "
76 + property.getName()
77 + ": no read method");
78 }
79 try {
80 return readMethod.invoke(instance, null);
81 }
82 catch (Exception ex) {
83 throw accessException("Cannot read property", readMethod, ex);
84 }
85 }
86
87 public void setPropertyValue(Object value) {
88 Method writeMethod = property.getWriteMethod();
89 if (writeMethod == null) {
90 throw new ClazzAccessException(
91 "Cannot set property: "
92 + property.getName()
93 + ": no set(array) method");
94 }
95
96 try {
97 writeMethod.invoke(instance, new Object[] { value });
98 }
99 catch (Exception ex) {
100 throw accessException("Cannot set property", writeMethod, ex);
101 }
102 }
103
104
105
106
107 public int size() {
108 Method sizeMethod = property.getSizeMethod();
109 if (sizeMethod != null) {
110 try {
111 Object value = sizeMethod.invoke(instance, null);
112 return ((Integer) value).intValue();
113 }
114 catch (Exception ex) {
115 throw accessException("Cannot get list size", sizeMethod, ex);
116 }
117 }
118 else {
119 Object list = getPropertyValue();
120 if (list == null) {
121 return 0;
122 }
123
124 if (list instanceof List) {
125 return ((List) list).size();
126 }
127
128 return Array.getLength(list);
129 }
130 }
131
132
133
134
135 public Object get(int index) {
136 Method getMethod = property.getGetMethod();
137 if (getMethod != null) {
138 Object value;
139 try {
140 value =
141 getMethod.invoke(
142 instance,
143 new Object[] { new Integer(index)});
144 }
145 catch (Throwable ex) {
146 throw accessException("Cannot get property", getMethod, ex);
147 }
148 return value;
149 }
150 else {
151 Object list = getPropertyValue();
152 if (list == null) {
153 return null;
154 }
155
156 if (list instanceof List) {
157 return ((List) list).get(index);
158 }
159
160 return Array.get(list, index);
161 }
162 }
163
164
165
166
167 public Iterator iterator() {
168 return new QuickList().iterator();
169 }
170
171
172
173
174 public ListIterator listIterator() {
175 return new QuickList().listIterator();
176 }
177
178
179
180
181 public ListIterator listIterator(int index) {
182 return new QuickList().listIterator(index);
183 }
184
185
186
187
188 public Object set(int index, Object element) {
189 modCount++;
190 Method setMethod = property.getSetMethod();
191 if (setMethod != null) {
192 Object oldValue = null;
193 try {
194 oldValue = get(index);
195 }
196 catch (Throwable t) {
197
198 }
199 try {
200 setMethod.invoke(
201 instance,
202 new Object[] { new Integer(index), element });
203 }
204 catch (Exception ex) {
205 throw accessException(
206 "Cannot set property element",
207 setMethod,
208 ex);
209 }
210 return oldValue;
211 }
212 else {
213 Object list = getPropertyValue();
214 if (list == null) {
215 return null;
216 }
217
218 if (list instanceof List) {
219 return ((List) list).set(index, element);
220 }
221
222 Object oldValue = Array.get(list, index);
223 Array.set(list, index, element);
224 return oldValue;
225 }
226 }
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252 public boolean add(Object element) {
253 modCount++;
254 if (property.getAddMethod() != null) {
255 Method addMethod = property.getAddMethod();
256 try {
257 addMethod.invoke(instance, new Object[] { element });
258 }
259 catch (Exception ex) {
260 throw accessException(
261 "Cannot add value to property",
262 addMethod,
263 ex);
264 }
265 }
266 else {
267 add(-1, element);
268 }
269 return true;
270 }
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295 public void add(int index, Object element) {
296 modCount++;
297 if (property.getAddIndexedMethod() != null) {
298 if (index == -1) {
299
300
301 index = size();
302 }
303 Method addIndexedMethod = property.getAddIndexedMethod();
304 try {
305 addIndexedMethod.invoke(
306 instance,
307 new Object[] { new Integer(index), element });
308 }
309 catch (Exception ex) {
310 throw accessException(
311 "Cannot add value to property",
312 addIndexedMethod,
313 ex);
314 }
315 return;
316 }
317
318 if (property.getAddMethod() != null && index == size()) {
319
320
321 add(element);
322 }
323 else if (property.getType().isArray()) {
324 addToArray(index, element);
325 }
326 else {
327 addToList(index, element);
328 }
329 }
330
331
332
333
334 private void addToArray(int index, Object element) {
335 Object newList;
336 Object list = getPropertyValue();
337 if (list == null) {
338 if (index != 0 && index != -1) {
339 throw new ArrayIndexOutOfBoundsException(
340 "Size: 0; Index: " + index);
341 }
342 Class contentType = property.getContentType();
343 newList = Array.newInstance(contentType, 1);
344 Array.set(newList, 0, element);
345 }
346 else {
347 Class contentType = property.getContentType();
348 int size = Array.getLength(list);
349 if (index == -1) {
350 index = size;
351 }
352 if (index < 0 || index > size) {
353 throw new ArrayIndexOutOfBoundsException(
354 "Size: " + size + "; Index: " + index);
355 }
356 newList = Array.newInstance(contentType, size + 1);
357 System.arraycopy(list, 0, newList, 0, index);
358 Array.set(newList, index, element);
359 System.arraycopy(list, index, newList, index + 1, size - index);
360 }
361
362 setPropertyValue(newList);
363 }
364
365
366
367
368 private void addToList(int index, Object element) {
369 Object list = getPropertyValue();
370 if (list == null) {
371 List newList;
372 Class type = property.getType();
373 if (!type.isInterface()) {
374 try {
375 newList = (List) type.newInstance();
376 }
377 catch (Exception ex) {
378 throw new ClazzAccessException(
379 "Cannot add value to property : "
380 + property.getName()
381 + ": cannot create List of type "
382 + type.getName(),
383 ex);
384 }
385 }
386 else {
387 newList = new ArrayList();
388 }
389 newList.add(element);
390
391 setPropertyValue(newList);
392 }
393 else if (index == -1) {
394 ((List) list).add(element);
395 }
396 else {
397 ((List) list).add(index, element);
398 }
399 }
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416 public boolean remove(Object element) {
417 modCount++;
418 if (property.getRemoveMethod() != null) {
419 Method removeMethod = property.getRemoveMethod();
420 try {
421 removeMethod.invoke(instance, new Object[] { element });
422 }
423 catch (Exception ex) {
424 throw accessException(
425 "Cannot remove value from property",
426 removeMethod,
427 ex);
428 }
429 return true;
430 }
431 else {
432 return super.remove(element);
433 }
434 }
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460 public Object remove(int index) {
461 modCount++;
462
463 if (property.getRemoveIndexedMethod() != null) {
464 Object value = null;
465
466 try {
467 value = get(index);
468 }
469 catch (Throwable t) {
470
471 }
472
473 Method removeIndexedMethod = property.getRemoveIndexedMethod();
474 try {
475 removeIndexedMethod.invoke(
476 instance,
477 new Object[] { new Integer(index)});
478 }
479 catch (Exception ex) {
480 throw accessException(
481 "Cannot remove value from property",
482 removeIndexedMethod,
483 ex);
484 }
485
486 return value;
487 }
488
489 Object list = getPropertyValue();
490 if (list == null) {
491 throw new ArrayIndexOutOfBoundsException(
492 "Size: 0; Index: " + index);
493 }
494 else if (property.getType().isArray()) {
495 Object value;
496 Class contentType = property.getContentType();
497 int size = Array.getLength(list);
498 if (index < 0 || index >= size) {
499 throw new ArrayIndexOutOfBoundsException(
500 "Size: " + size + "; Index: " + index);
501 }
502 value = Array.get(list, index);
503
504 Object newList = Array.newInstance(contentType, size - 1);
505 System.arraycopy(list, 0, newList, 0, index);
506 System.arraycopy(list, index + 1, newList, index, size - index - 1);
507 setPropertyValue(newList);
508 return value;
509 }
510 else {
511 return ((List) list).remove(index);
512 }
513 }
514
515 private RuntimeException accessException(
516 String message,
517 Method method,
518 Throwable ex)
519 {
520 if (ex instanceof InvocationTargetException) {
521 ex = ((InvocationTargetException) ex).getTargetException();
522 }
523
524
525
526 if (ex instanceof RuntimeException) {
527 throw (RuntimeException) ex;
528 }
529 if (ex instanceof Error) {
530 throw (Error) ex;
531 }
532
533 throw new ClazzAccessException(
534 message
535 + ": "
536 + property.getName()
537 + ": cannot invoke method: "
538 + method.getName(),
539 ex);
540 }
541
542 private int getModCount() {
543 return modCount;
544 }
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559 private class QuickList extends AbstractList {
560 private Object list;
561 private int size;
562
563 public QuickList() {
564 update();
565 }
566
567 public void refresh() {
568
569
570 if (super.modCount != getModCount()) {
571 update();
572 }
573 }
574
575 public void update() {
576
577
578 super.modCount = getModCount();
579
580 if (property.getReadMethod() != null) {
581 list = getPropertyValue();
582 if (list == null) {
583 size = 0;
584 }
585 else if (list instanceof List) {
586 size = ((List) list).size();
587 }
588 else {
589 size = Array.getLength(list);
590 }
591 }
592 else {
593 list = NOT_ACCESSIBLE;
594 size = ReflectedList.this.size();
595 }
596 }
597
598 public int size() {
599 refresh();
600 return size;
601 }
602
603 public Object get(int index) {
604 refresh();
605 if (list != NOT_ACCESSIBLE) {
606 if (list == null) {
607 throw new IndexOutOfBoundsException(
608 "Size= 0; index=" + index);
609 }
610
611 if (list instanceof List) {
612 return ((List) list).get(index);
613 }
614
615 return Array.get(list, index);
616 }
617 else {
618 return ReflectedList.this.get(index);
619 }
620 }
621
622 public Object set(int index, Object value) {
623 return ReflectedList.this.set(index, value);
624 }
625
626 public void add(int index, Object value) {
627 ReflectedList.this.add(index, value);
628 }
629
630 public Object remove(int index) {
631 return ReflectedList.this.remove(index);
632 }
633
634 }
635
636 private static final Object NOT_ACCESSIBLE = new Object();
637 }