1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.chain.impl;
18
19 import java.beans.IntrospectionException;
20 import java.beans.Introspector;
21 import java.beans.PropertyDescriptor;
22 import java.io.Serializable;
23 import java.lang.reflect.Method;
24 import java.util.AbstractCollection;
25 import java.util.AbstractSet;
26 import java.util.Collection;
27 import java.util.HashMap;
28 import java.util.Iterator;
29 import java.util.Map;
30 import java.util.Objects;
31 import java.util.Set;
32
33 import org.apache.commons.chain.Context;
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 public class ContextBase extends HashMap<String, Object> implements Context {
53 private static final long serialVersionUID = -2482145117370708259L;
54
55
56
57
58
59
60
61
62 private static final Object SINGLETON = new Serializable() {
63 private static final long serialVersionUID = -6023767081282668587L;
64
65 @Override
66 public boolean equals(Object object) {
67 return false;
68 }
69
70 @Override
71 public int hashCode() {
72 return super.hashCode();
73 }
74 };
75
76
77
78
79
80
81
82
83
84
85
86
87
88 private final transient PropertyDescriptor[] pds = getPropertyDescriptors();
89
90
91
92
93
94
95
96 private final transient Map<String, PropertyDescriptor> descriptors = getMapDescriptors();
97
98
99
100
101
102
103 public ContextBase() {
104 }
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119 public ContextBase(Map<String, Object> map) {
120 super(map);
121 putAll(map);
122 }
123
124
125
126
127
128
129
130 @Override
131 public void clear() {
132 if (descriptors == null) {
133 super.clear();
134 } else {
135 Iterator<String> keys = keySet().iterator();
136 while (keys.hasNext()) {
137 String key = keys.next();
138 if (!descriptors.containsKey(key)) {
139 keys.remove();
140 }
141 }
142 }
143 }
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158 @Override
159 public boolean containsValue(Object value) {
160 boolean b = super.containsValue(value);
161
162
163 if (descriptors == null) {
164 return b;
165 }
166
167
168 if (b) {
169 return true;
170 }
171
172
173 for (PropertyDescriptor pd : pds) {
174 if (pd.getReadMethod() != null) {
175 Object prop = readProperty(pd);
176 if (value == null) {
177 if (prop == null) {
178 return true;
179 }
180 } else if (value.equals(prop)) {
181 return true;
182 }
183 }
184 }
185 return false;
186 }
187
188
189
190
191
192
193
194
195
196
197 @Override
198 public Set<Map.Entry<String, Object>> entrySet() {
199 return new EntrySetImpl();
200 }
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220 @Override
221 public Object get(Object key) {
222
223 if (descriptors == null) {
224 return super.get(key);
225 }
226
227
228 if (key != null) {
229 final PropertyDescriptor descriptor = descriptors.get(key);
230 if (descriptor != null) {
231 if (descriptor.getReadMethod() != null) {
232 return readProperty(descriptor);
233 } else {
234 return null;
235 }
236 }
237 }
238
239
240 return super.get(key);
241 }
242
243
244
245
246
247
248
249
250
251 @Override
252 public boolean isEmpty() {
253
254 if (descriptors == null) {
255 return super.isEmpty();
256 }
257
258
259 return super.size() <= descriptors.size();
260 }
261
262
263
264
265
266
267
268
269
270
271 public Set<String> keySet() {
272 return super.keySet();
273 }
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289 @Override
290 public Object put(String key, Object value) {
291
292 if (descriptors == null) {
293 return super.put(key, value);
294 }
295
296
297 if (key != null) {
298 final PropertyDescriptor descriptor = descriptors.get(key);
299 if (descriptor != null) {
300 Object previous = null;
301 if (descriptor.getReadMethod() != null) {
302 previous = readProperty(descriptor);
303 }
304 writeProperty(descriptor, value);
305 return previous;
306 }
307 }
308
309
310 return super.put(key, value);
311 }
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326 @Override
327 public void putAll(Map<? extends String, ? extends Object> map) {
328 map.forEach(this::put);
329 }
330
331
332
333
334
335
336
337
338
339
340
341
342
343 @Override
344 public Object remove(Object key) {
345
346 if (descriptors == null) {
347 return super.remove(key);
348 }
349
350
351 if (key != null) {
352 PropertyDescriptor descriptor = descriptors.get(key);
353 if (descriptor != null) {
354 throw new UnsupportedOperationException("Local property '" + key + "' cannot be removed");
355 }
356 }
357
358
359 return super.remove(key);
360 }
361
362
363
364
365
366
367
368
369
370
371 @Override
372 public Collection<Object> values() {
373 return new ValuesImpl();
374 }
375
376
377
378
379
380
381
382
383
384 private Iterator<Map.Entry<String, Object>> entriesIterator() {
385 return new EntrySetIterator();
386 }
387
388
389
390
391
392
393
394
395
396
397 private Map.Entry<String, Object> entry(Object key) {
398 if (containsKey(key)) {
399 return new MapEntryImpl(key.toString(), get(key));
400 } else {
401 return null;
402 }
403 }
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418 private Object readProperty(PropertyDescriptor descriptor) {
419 try {
420 Method method = descriptor.getReadMethod();
421 if (method == null) {
422 throw new UnsupportedOperationException("Property '"
423 + descriptor.getName() + "' is not readable");
424 }
425 return method.invoke(this);
426 } catch (Exception e) {
427 throw new UnsupportedOperationException("Exception reading property '"
428 + descriptor.getName() + "': " + e.getMessage());
429 }
430 }
431
432
433
434
435
436
437
438
439
440
441
442
443
444 private boolean remove(Map.Entry<?, ?> entry) {
445 Map.Entry<String, Object> actual = entry(entry.getKey());
446 if (actual == null) {
447 return false;
448 } else if (entry.equals(actual)) {
449 remove(entry.getKey());
450 return true;
451 } else {
452 return false;
453 }
454 }
455
456
457
458
459
460
461
462
463 private Iterator<Object> valuesIterator() {
464 return new ValuesIterator();
465 }
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480 private void writeProperty(PropertyDescriptor descriptor, Object value) {
481 try {
482 Method method = descriptor.getWriteMethod();
483 if (method == null) {
484 throw new UnsupportedOperationException("Property '" + descriptor.getName()
485 + "' is not writeable");
486 }
487 method.invoke(this, value);
488 } catch (Exception e) {
489 throw new UnsupportedOperationException("Exception writing property '"
490 + descriptor.getName() + "': " + e.getMessage());
491 }
492 }
493
494
495
496
497
498
499
500 private PropertyDescriptor[] getPropertyDescriptors() {
501
502 try {
503 return Introspector.getBeanInfo(getClass()).getPropertyDescriptors();
504 } catch (IntrospectionException e) {
505 return new PropertyDescriptor[0];
506 }
507 }
508
509
510
511
512
513
514
515
516
517
518
519 private Map<String, PropertyDescriptor> getMapDescriptors() {
520 Map<String, PropertyDescriptor> ret = new HashMap<>();
521
522
523 for (PropertyDescriptor pd : pds) {
524 String name = pd.getName();
525
526
527 if (!("class".equals(name) || "empty".equals(name))) {
528 ret.put(name, pd);
529 super.put(name, SINGLETON);
530 }
531 }
532
533 return ret.isEmpty() ? null : ret;
534 }
535
536
537
538
539
540
541
542 private final class EntrySetImpl extends AbstractSet<Map.Entry<String, Object>> {
543
544 @Override
545 public void clear() {
546 ContextBase.this.clear();
547 }
548
549 @Override
550 public boolean contains(Object obj) {
551 if (!(obj instanceof Map.Entry)) {
552 return false;
553 }
554 Map.Entry<?, ?> entry = (Map.Entry<?, ?>) obj;
555 Map.Entry<String, Object> actual = ContextBase.this.entry(entry.getKey());
556 if (actual != null) {
557 return actual.equals(entry);
558 } else {
559 return false;
560 }
561 }
562
563 @Override
564 public boolean isEmpty() {
565 return ContextBase.this.isEmpty();
566 }
567
568 @Override
569 public Iterator<Map.Entry<String, Object>> iterator() {
570 return ContextBase.this.entriesIterator();
571 }
572
573 @Override
574 public boolean remove(Object obj) {
575 if (obj instanceof Map.Entry) {
576 return ContextBase.this.remove((Map.Entry<?, ?>) obj);
577 } else {
578 return false;
579 }
580 }
581
582 @Override
583 public int size() {
584 return ContextBase.this.size();
585 }
586 }
587
588
589
590
591
592 private final class EntrySetIterator implements Iterator<Map.Entry<String, Object>> {
593
594 private Map.Entry<String, Object> entry = null;
595 private Iterator<String> keys = ContextBase.this.keySet().iterator();
596
597 @Override
598 public boolean hasNext() {
599 return keys.hasNext();
600 }
601
602 @Override
603 public Map.Entry<String, Object> next() {
604 entry = ContextBase.this.entry(keys.next());
605 return entry;
606 }
607
608 @Override
609 public void remove() {
610 ContextBase.this.remove(entry);
611 }
612 }
613
614
615
616
617
618 private class MapEntryImpl implements Map.Entry<String, Object> {
619
620 private String key;
621 private Object value;
622
623 MapEntryImpl(String key, Object value) {
624 this.key = key;
625 this.value = value;
626 }
627
628 @Override
629 public boolean equals(Object obj) {
630 if (this == obj) {
631 return true;
632 }
633 if (obj == null || getClass() != obj.getClass()) {
634 return false;
635 }
636 MapEntryImpl other = (MapEntryImpl) obj;
637 if (!getEnclosingInstance().equals(other.getEnclosingInstance())) {
638 return false;
639 }
640 return Objects.equals(key, other.key) && Objects.equals(value, other.value);
641 }
642
643 @Override
644 public String getKey() {
645 return this.key;
646 }
647
648 @Override
649 public Object getValue() {
650 return this.value;
651 }
652
653 @Override
654 public int hashCode() {
655 return Objects.hashCode(key) ^ Objects.hashCode(value);
656 }
657
658 @Override
659 public Object setValue(Object value) {
660 Object previous = this.value;
661 ContextBase.this.put(this.key, value);
662 this.value = value;
663 return previous;
664 }
665
666 @Override
667 public String toString() {
668 return getKey() + "=" + getValue();
669 }
670
671 private ContextBase getEnclosingInstance() {
672 return ContextBase.this;
673 }
674 }
675
676
677
678
679
680 private final class ValuesImpl extends AbstractCollection<Object> {
681
682 @Override
683 public void clear() {
684 ContextBase.this.clear();
685 }
686
687 @Override
688 public boolean contains(Object obj) {
689 if (!(obj instanceof Map.Entry)) {
690 return false;
691 }
692 Map.Entry<?, ?> entry = (Map.Entry<?, ?>) obj;
693 return ContextBase.this.containsValue(entry.getValue());
694 }
695
696 @Override
697 public boolean isEmpty() {
698 return ContextBase.this.isEmpty();
699 }
700
701 @Override
702 public Iterator<Object> iterator() {
703 return ContextBase.this.valuesIterator();
704 }
705
706 @Override
707 public boolean remove(Object obj) {
708 if (obj instanceof Map.Entry) {
709 return ContextBase.this.remove((Map.Entry<?, ?>) obj);
710 } else {
711 return false;
712 }
713 }
714
715 @Override
716 public int size() {
717 return ContextBase.this.size();
718 }
719 }
720
721
722
723
724
725 private final class ValuesIterator implements Iterator<Object> {
726
727 private Map.Entry<String, Object> entry = null;
728 private Iterator<String> keys = ContextBase.this.keySet().iterator();
729
730 @Override
731 public boolean hasNext() {
732 return keys.hasNext();
733 }
734
735 @Override
736 public Object next() {
737 entry = ContextBase.this.entry(keys.next());
738 return entry.getValue();
739 }
740
741 @Override
742 public void remove() {
743 ContextBase.this.remove(entry);
744 }
745 }
746 }