1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.commons.clazz.reflect.extended;
17
18 import java.lang.reflect.Array;
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 import java.util.AbstractMap;
22 import java.util.AbstractSet;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.Iterator;
29 import java.util.Map;
30 import java.util.Set;
31
32 import org.apache.commons.clazz.ClazzAccessException;
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 public class ReflectedMap extends AbstractMap {
55 private Object instance;
56 private ReflectedMappedProperty property;
57 private int modCount = 0;
58
59
60
61
62 public ReflectedMap(
63 Object instance,
64 ReflectedMappedProperty property)
65 {
66 this.instance = instance;
67 this.property = property;
68 }
69
70 public Map getPropertyValue() {
71 Method readMethod = property.getReadMethod();
72 if (readMethod == null) {
73 throw new ClazzAccessException(
74 "Cannot read property "
75 + property.getName()
76 + ": no read method");
77 }
78 try {
79 return (Map) readMethod.invoke(instance, null);
80 }
81 catch (Exception ex) {
82 throw accessException("Cannot read property", readMethod, ex);
83 }
84 }
85
86 public void setPropertyValue(Map value) {
87 Method writeMethod = property.getWriteMethod();
88 if (writeMethod == null) {
89 throw new ClazzAccessException(
90 "Cannot set property: "
91 + property.getName()
92 + ": no set(array) method");
93 }
94
95 try {
96 writeMethod.invoke(instance, new Object[] { value });
97 }
98 catch (Exception ex) {
99 throw accessException("Cannot set property", writeMethod, ex);
100 }
101 }
102
103 public Set getPropertyKeySet() {
104 Method keySetMethod = property.getKeySetMethod();
105 if (keySetMethod != null) {
106 Set set;
107 try {
108 Object value = keySetMethod.invoke(instance, null);
109 if (value == null) {
110 set = Collections.EMPTY_SET;
111 }
112 else if (value instanceof Set) {
113 set = (Set) value;
114 }
115 else if (value instanceof Collection) {
116 set = new ConcurrentChangeSafeSet((Collection) value);
117 }
118 else {
119 set =
120 new ConcurrentChangeSafeSet(
121 Arrays.asList((Object[]) value));
122 }
123 }
124 catch (Exception ex) {
125 throw new ClazzAccessException(
126 "Cannot get key set: "
127 + property.getName()
128 + ": cannot invoke method: "
129 + keySetMethod.getName(),
130 ex);
131 }
132 return set;
133 }
134 else {
135 Map map = getPropertyValue();
136 if (map == null) {
137 return Collections.EMPTY_SET;
138 }
139 return map.keySet();
140 }
141 }
142
143
144
145
146
147
148
149 public Object get(Object key) {
150 Method getMethod = property.getGetMethod();
151 if (getMethod != null) {
152 Object value;
153 try {
154 value = getMethod.invoke(instance, new Object[]{key});
155 }
156 catch (Exception ex) {
157 throw new ClazzAccessException(
158 "Cannot get property : "
159 + property.getName()
160 + ": cannot invoke method: "
161 + getMethod.getName(),
162 ex);
163 }
164 return value;
165 }
166 else {
167 Map map = getPropertyValue();
168 if (map == null) {
169 return null;
170 }
171 return map.get(key);
172 }
173 }
174
175
176
177
178 public int size() {
179 Method keySetMethod = property.getKeySetMethod();
180 if (keySetMethod != null) {
181 try {
182 Object value = keySetMethod.invoke(instance, null);
183 if (value == null) {
184 return 0;
185 }
186 else if (value instanceof Collection) {
187 return ((Collection) value).size();
188 }
189 else {
190 return Array.getLength(value);
191 }
192 }
193 catch (Exception ex) {
194 throw accessException("Cannot get key set", keySetMethod, ex);
195 }
196 }
197 else {
198 Map map = getPropertyValue();
199 if (map == null) {
200 return 0;
201 }
202 return map.size();
203 }
204 }
205
206
207
208
209 public boolean isEmpty() {
210 return size() == 0;
211 }
212
213
214
215
216 public Set keySet() {
217 return new EntrySet(KEYS);
218 }
219
220
221
222
223 public Set entrySet() {
224 return new EntrySet(ENTRIES);
225 }
226
227
228
229
230 public Object put(Object key, Object value) {
231 Method putMethod = property.getPutMethod();
232 if (putMethod != null) {
233 Object oldValue = null;
234 try {
235 oldValue = get(key);
236 }
237 catch (Throwable t) {
238
239 }
240
241 try {
242 putMethod.invoke(instance, new Object[]{key, value});
243 }
244 catch (Exception ex) {
245 throw new ClazzAccessException(
246 "Cannot set property : "
247 + property.getName()
248 + ": cannot invoke method: "
249 + putMethod.getName(),
250 ex);
251 }
252 return oldValue;
253 }
254 else {
255 Map map = getPropertyValue();
256 if (map == null) {
257 map = new HashMap();
258 setPropertyValue(map);
259 }
260 return map.put(key, value);
261 }
262 }
263
264
265
266
267 public Object remove(Object key) {
268 Method removeMethod = property.getRemoveMethod();
269 if (removeMethod != null) {
270 Object oldValue = null;
271 try {
272 oldValue = get(key);
273 }
274 catch (Throwable t) {
275
276 }
277
278 try {
279 removeMethod.invoke(instance, new Object[]{key});
280 }
281 catch (Exception ex) {
282 throw new ClazzAccessException(
283 "Cannot set property : "
284 + property.getName()
285 + ": cannot invoke method: "
286 + removeMethod.getName(),
287 ex);
288 }
289 return oldValue;
290 }
291 else {
292 Map map = getPropertyValue();
293 if (map != null) {
294 return map.remove(key);
295 }
296 return null;
297 }
298 }
299
300 private RuntimeException accessException(
301 String message,
302 Method method,
303 Throwable ex)
304 {
305 if (ex instanceof InvocationTargetException) {
306 ex = ((InvocationTargetException) ex).getTargetException();
307 }
308
309
310
311 if (ex instanceof RuntimeException) {
312 throw (RuntimeException) ex;
313 }
314 if (ex instanceof Error) {
315 throw (Error) ex;
316 }
317
318 throw new ClazzAccessException(
319 message
320 + ": "
321 + property.getName()
322 + ": cannot invoke method: "
323 + method.getName(),
324 ex);
325 }
326
327 private static final int ENTRIES = 0;
328 private static final int KEYS = 1;
329
330
331
332
333
334 private class EntrySet extends AbstractSet {
335 private int type;
336 private int modCount = -1;
337 private Set keySet;
338 private int size;
339
340 public EntrySet(int type) {
341 this.type = type;
342 update();
343 }
344
345 public void refresh() {
346
347
348 if (modCount != ReflectedMap.this.modCount) {
349 update();
350 }
351 }
352
353 public void update() {
354
355
356 modCount = ReflectedMap.this.modCount;
357
358 keySet = ReflectedMap.this.getPropertyKeySet();
359 size = keySet.size();
360 }
361
362 public int size() {
363 refresh();
364 return size;
365 }
366
367 public Iterator iterator() {
368 refresh();
369 return new EntryIterator(keySet, type);
370 }
371
372 public boolean remove(Object object) {
373 refresh();
374 Object key =
375 (type == KEYS ? object : ((Map.Entry) object).getKey());
376
377 boolean exists = true;
378 try {
379 exists = keySet.contains(key);
380 }
381 catch (Throwable t) {
382
383 }
384 if (exists) {
385 ReflectedMap.this.remove(key);
386 }
387 return exists;
388 }
389 }
390
391
392
393
394
395 private class EntryIterator implements Iterator {
396 private int type;
397 private Set keySet;
398 private Iterator keyIterator;
399 private Object lastReturned = UNINITIALIZED;
400
401 public EntryIterator(Set keySet, int type) {
402 this.type = type;
403 this.keySet = keySet;
404 this.keyIterator = keySet.iterator();
405 }
406
407
408
409
410 public boolean hasNext() {
411 return keyIterator.hasNext();
412 }
413
414
415
416
417 public Object next() {
418 lastReturned = keyIterator.next();
419 if (type == KEYS) {
420 return lastReturned;
421 }
422 else {
423 return new Entry(lastReturned);
424 }
425 }
426
427
428
429
430 public void remove() {
431 if (lastReturned == UNINITIALIZED) {
432 throw new IllegalStateException();
433 }
434 ensureConcurrentChangeSafety();
435 ReflectedMap.this.remove(lastReturned);
436 }
437
438
439
440
441
442
443
444
445
446
447 private void ensureConcurrentChangeSafety() {
448 if (!(keySet instanceof ConcurrentChangeSafeSet)) {
449 keySet = new ConcurrentChangeSafeSet(keySet);
450 keyIterator = keySet.iterator();
451 while (keyIterator.hasNext()) {
452 Object key = keyIterator.next();
453 if ((key == null && lastReturned == null)
454 || (key != null && key.equals(lastReturned))) {
455 return;
456 }
457 }
458 throw new IllegalStateException(
459 "The second iteration over the key set"
460 + " did not produce the same elements");
461 }
462 }
463 }
464
465 private static final Object UNINITIALIZED = new Object();
466
467
468
469
470
471 private class Entry implements Map.Entry {
472 private Object key;
473
474 public Entry(Object key) {
475 this.key = key;
476 }
477
478
479
480
481 public Object getKey() {
482 return key;
483 }
484
485
486
487 public Object getValue() {
488 return ReflectedMap.this.get(key);
489 }
490
491
492
493
494 public Object setValue(Object value) {
495 return ReflectedMap.this.put(key, value);
496 }
497
498 public boolean equals(Object o) {
499 if (!(o instanceof Map.Entry)) {
500 return false;
501 }
502
503 Map.Entry e = (Map.Entry) o;
504 return (key == null ? e.getKey() == null : key.equals(e.getKey()));
505 }
506
507 public int hashCode() {
508 return key == null ? 0 : key.hashCode();
509 }
510
511 public String toString() {
512 return key + "=" + getValue();
513 }
514 }
515
516
517
518
519
520
521 private static class ConcurrentChangeSafeSet extends HashSet {
522 public ConcurrentChangeSafeSet(Collection collection) {
523 super(collection);
524 }
525 }
526 }