1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.functor.example;
18
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertNull;
21 import static org.junit.Assert.fail;
22
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.HashMap;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30
31 import org.apache.commons.functor.BinaryFunction;
32 import org.apache.commons.functor.BinaryProcedure;
33 import org.apache.commons.functor.Function;
34 import org.apache.commons.functor.Procedure;
35 import org.apache.commons.functor.UnaryFunction;
36 import org.apache.commons.functor.UnaryProcedure;
37 import org.apache.commons.functor.adapter.IgnoreLeftFunction;
38 import org.apache.commons.functor.core.Constant;
39 import org.apache.commons.functor.core.Identity;
40 import org.apache.commons.functor.core.IsInstance;
41 import org.apache.commons.functor.core.IsNull;
42 import org.apache.commons.functor.core.RightIdentity;
43 import org.apache.commons.functor.core.composite.Conditional;
44 import org.junit.Test;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64 @SuppressWarnings("unchecked")
65 public class FlexiMapExample {
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85 @Test
86 public void testBasicMap() {
87
88 Map map = makeBasicMap();
89 Object key = "key";
90 Object value = new Integer(3);
91 map.put(key,value);
92 assertEquals(value, map.get(key) );
93 }
94
95
96
97
98
99 @Test
100 public void testBasicMapReturnsNullForMissingKey() {
101 Map map = makeBasicMap();
102 assertNull( map.get("key") );
103 }
104
105
106
107
108
109 @Test
110 public void testBasicMapAllowsNull() {
111 Map map = makeBasicMap();
112 Object key = "key";
113 Object value = null;
114 map.put(key,value);
115 assertNull( map.get(key) );
116 }
117
118
119
120
121
122 @Test
123 public void testBasicMapAllowsMultipleTypes() {
124 Map map = makeBasicMap();
125 map.put("key-1","value-1");
126 map.put(new Integer(2),"value-2");
127 map.put("key-3",new Integer(3));
128 map.put(new Integer(4),new Integer(4));
129
130 assertEquals("value-1", map.get("key-1") );
131 assertEquals("value-2", map.get(new Integer(2)) );
132 assertEquals(new Integer(3), map.get("key-3") );
133 assertEquals(new Integer(4), map.get(new Integer(4)) );
134 }
135
136
137
138
139
140
141 @Test
142 public void testBasicMapStoresOnlyOneValuePerKey() {
143 Map map = makeBasicMap();
144
145 assertNull( map.put("key","value-1") );
146 assertEquals("value-1", map.get("key") );
147 assertEquals("value-1", map.put("key","value-2"));
148 assertEquals("value-2", map.get("key") );
149 }
150
151
152
153
154
155
156
157
158
159 @Test
160 public void testForbidNull() {
161 Map map = makeNullForbiddenMap();
162
163 map.put("key","value");
164 map.put("key2", new Integer(2) );
165 try {
166 map.put("key3",null);
167 fail("Expected NullPointerException");
168 } catch(NullPointerException e) {
169
170 }
171 }
172
173
174
175
176
177
178
179
180 @Test
181 public void testNullDefaultsToZero() {
182 Map map = makeDefaultValueForNullMap(new Integer(0));
183
184
185
186 assertEquals( new Integer(0), map.get("key") );
187
188
189
190 map.put("key", null);
191 assertEquals( new Integer(0), map.get("key") );
192 }
193
194
195
196
197
198 @Test
199 public void testIntegerValuesOnly() {
200 Map map = makeTypeConstrainedMap(Integer.class);
201 map.put("key", new Integer(2));
202 assertEquals( new Integer(2), map.get("key") );
203 try {
204 map.put("key2","value");
205 fail("Expected ClassCastException");
206 } catch(ClassCastException e) {
207
208 }
209 }
210
211
212
213
214
215
216
217
218
219
220 @Test
221 public void testMultiMap() {
222 Map map = makeMultiMap();
223
224 map.put("key", "value 1");
225
226 {
227 Collection result = (Collection)(map.get("key"));
228 assertEquals(1,result.size());
229 assertEquals("value 1", result.iterator().next());
230 }
231
232 map.put("key", "value 2");
233
234 {
235 Collection result = (Collection)(map.get("key"));
236 assertEquals(2,result.size());
237 Iterator iter = result.iterator();
238 assertEquals("value 1", iter.next());
239 assertEquals("value 2", iter.next());
240 }
241
242 map.put("key", "value 3");
243
244 {
245 Collection result = (Collection)(map.get("key"));
246 assertEquals(3,result.size());
247 Iterator iter = result.iterator();
248 assertEquals("value 1", iter.next());
249 assertEquals("value 2", iter.next());
250 assertEquals("value 3", iter.next());
251 }
252
253 }
254
255
256
257
258
259
260
261
262 @Test
263 public void testStringConcatMap() {
264 Map map = makeStringConcatMap();
265 map.put("key", "value 1");
266 assertEquals("value 1",map.get("key"));
267 map.put("key", "value 2");
268 assertEquals("value 1, value 2",map.get("key"));
269 map.put("key", "value 3");
270 assertEquals("value 1, value 2, value 3",map.get("key"));
271 }
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287 static class FlexiMap implements Map {
288
289
290
291
292
293
294
295 public FlexiMap(BinaryFunction putfn, BinaryFunction getfn) {
296 onPut = null == putfn ? RightIdentity.function() : putfn;
297 onGet = null == getfn ? RightIdentity.function() : getfn;
298 proxiedMap = new HashMap();
299 }
300
301
302
303
304
305
306
307
308 public Object get(Object key) {
309 return onGet.evaluate( key, proxiedMap.get(key) );
310 }
311
312
313
314
315
316
317
318
319
320 public Object put(Object key, Object value) {
321 Object oldvalue = proxiedMap.get(key);
322 proxiedMap.put(key, onPut.evaluate(oldvalue, value));
323 return onGet.evaluate(key,oldvalue);
324 }
325
326
327
328
329
330 public void clear() {
331 throw new UnsupportedOperationException("Left as an exercise for the reader.");
332 }
333
334 public boolean containsKey(Object key) {
335 throw new UnsupportedOperationException("Left as an exercise for the reader.");
336 }
337
338 public boolean containsValue(Object value) {
339 throw new UnsupportedOperationException("Left as an exercise for the reader.");
340 }
341
342 public Set entrySet() {
343 throw new UnsupportedOperationException("Left as an exercise for the reader.");
344 }
345
346 public boolean isEmpty() {
347 throw new UnsupportedOperationException("Left as an exercise for the reader.");
348 }
349
350 public Set keySet() {
351 throw new UnsupportedOperationException("Left as an exercise for the reader.");
352 }
353
354 public void putAll(Map t) {
355 throw new UnsupportedOperationException("Left as an exercise for the reader.");
356 }
357
358 public Object remove(Object key) {
359 throw new UnsupportedOperationException("Left as an exercise for the reader.");
360 }
361
362 public int size() {
363 throw new UnsupportedOperationException("Left as an exercise for the reader.");
364 }
365
366 public Collection values() {
367 throw new UnsupportedOperationException("Left as an exercise for the reader.");
368 }
369
370 private BinaryFunction onPut = null;
371 private BinaryFunction onGet = null;
372 private Map proxiedMap = null;
373 }
374
375
376
377
378
379
380
381
382
383
384
385
386 private Map makeBasicMap() {
387 return new HashMap();
388 }
389
390
391
392
393
394 private Map makeNullForbiddenMap() {
395 return new FlexiMap(
396
397
398
399 IgnoreLeftFunction.adapt(
400
401
402
403 Conditional.function(
404
405
406
407 IsNull.instance(),
408
409
410
411 throwNPE,
412
413
414
415 Identity.instance()
416 )
417 ),
418 null
419 );
420 }
421
422
423
424
425
426
427 private Map makeDefaultValueForNullMap(Object defaultValue) {
428 return new FlexiMap(
429 null,
430
431
432
433 IgnoreLeftFunction.adapt(
434
435
436
437 Conditional.function(
438
439
440
441 IsNull.instance(),
442
443
444
445 new Constant(defaultValue),
446
447
448
449 Identity.instance()
450 )
451 )
452 );
453 }
454
455
456
457
458
459 private Map makeTypeConstrainedMap(Class clazz) {
460 return new FlexiMap(
461
462
463
464 IgnoreLeftFunction.adapt(
465 Conditional.function(
466
467
468
469 IsInstance.of(clazz),
470
471
472
473 Identity.instance(),
474
475
476
477 throwCCE
478 )
479 ),
480 null
481 );
482 }
483
484
485
486
487
488
489 private Map makeMultiMap() {
490 return new FlexiMap(
491 new BinaryFunction() {
492 public Object evaluate(Object oldval, Object newval) {
493 List list = null;
494 if (null == oldval) {
495 list = new ArrayList();
496 } else {
497 list = (List) oldval;
498 }
499 list.add(newval);
500 return list;
501 }
502 },
503 null
504 );
505 }
506
507
508
509
510 private Map makeStringConcatMap() {
511 return new FlexiMap(
512
513
514
515
516 new BinaryFunction() {
517 public Object evaluate(Object oldval, Object newval) {
518 StringBuffer buf = null;
519 if (null == oldval) {
520 buf = new StringBuffer();
521 } else {
522 buf = (StringBuffer) oldval;
523 buf.append(", ");
524 }
525 buf.append(newval);
526 return buf;
527 }
528 },
529
530
531
532
533 new BinaryFunction() {
534 public Object evaluate(Object key, Object val) {
535 if (null == val) {
536 return null;
537 } else {
538 return ((StringBuffer) val).toString();
539 }
540 }
541 }
542 );
543 }
544
545
546
547
548
549
550
551
552 private abstract class UniversalFunctor implements
553 Procedure, UnaryProcedure, BinaryProcedure,
554 Function, UnaryFunction, BinaryFunction {
555 public abstract void run();
556
557 public void run(Object obj) {
558 run();
559 }
560 public void run(Object left, Object right) {
561 run();
562 }
563 public Object evaluate() {
564 run();
565 return null;
566 }
567 public Object evaluate(Object obj) {
568 run();
569 return null;
570 }
571 public Object evaluate(Object left, Object right) {
572 run();
573 return null;
574 }
575 }
576
577 private UniversalFunctor throwNPE = new UniversalFunctor() {
578 public void run() {
579 throw new NullPointerException();
580 }
581 };
582
583 private UniversalFunctor throwCCE = new UniversalFunctor() {
584 public void run() {
585 throw new ClassCastException();
586 }
587 };
588
589 }