Mercurial > hg > blitz_condensed
comparison src/com/go/trove/util/PropertyMap.java @ 0:3dc0c5604566
Initial checkin of blitz 2.0 fcs - no installer yet.
author | Dan Creswell <dan.creswell@gmail.com> |
---|---|
date | Sat, 21 Mar 2009 11:00:06 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:3dc0c5604566 |
---|---|
1 /* ==================================================================== | |
2 * Trove - Copyright (c) 1997-2000 Walt Disney Internet Group | |
3 * ==================================================================== | |
4 * The Tea Software License, Version 1.1 | |
5 * | |
6 * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved. | |
7 * | |
8 * Redistribution and use in source and binary forms, with or without | |
9 * modification, are permitted provided that the following conditions | |
10 * are met: | |
11 * | |
12 * 1. Redistributions of source code must retain the above copyright | |
13 * notice, this list of conditions and the following disclaimer. | |
14 * | |
15 * 2. Redistributions in binary form must reproduce the above copyright | |
16 * notice, this list of conditions and the following disclaimer in | |
17 * the documentation and/or other materials provided with the | |
18 * distribution. | |
19 * | |
20 * 3. The end-user documentation included with the redistribution, | |
21 * if any, must include the following acknowledgment: | |
22 * "This product includes software developed by the | |
23 * Walt Disney Internet Group (http://opensource.go.com/)." | |
24 * Alternately, this acknowledgment may appear in the software itself, | |
25 * if and wherever such third-party acknowledgments normally appear. | |
26 * | |
27 * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must | |
28 * not be used to endorse or promote products derived from this | |
29 * software without prior written permission. For written | |
30 * permission, please contact opensource@dig.com. | |
31 * | |
32 * 5. Products derived from this software may not be called "Tea", | |
33 * "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet", | |
34 * "Kettle", "Trove" or "BeanDoc" appear in their name, without prior | |
35 * written permission of the Walt Disney Internet Group. | |
36 * | |
37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |
38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
40 * DISCLAIMED. IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS | |
41 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
42 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
43 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
44 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
45 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
46 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
47 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
48 * ==================================================================== | |
49 * | |
50 * For more information about Tea, please see http://opensource.go.com/. | |
51 */ | |
52 | |
53 package com.go.trove.util; | |
54 | |
55 import java.util.*; | |
56 import java.io.Serializable; | |
57 | |
58 /****************************************************************************** | |
59 * A class that is similar to {@link java.util.Properties} but preserves | |
60 * original property order and supports a special {@link #subMap} view | |
61 * operation. PropertyMap also has more convenience methods for getting | |
62 * properties as certain types. | |
63 * | |
64 * @author Brian S O'Neill | |
65 * @version | |
66 * <!--$$Revision: 1.1 $--> 4 <!-- $$JustDate:--> 01/02/26 <!-- $--> | |
67 */ | |
68 public class PropertyMap extends AbstractMap { | |
69 public static final Class ELEMENT_TYPE = String.class; | |
70 | |
71 private static String internStr(String str) { | |
72 return (String)Utils.intern(str); | |
73 } | |
74 | |
75 private Map mMappings; | |
76 private String mSeparator; | |
77 private String mPrefix; | |
78 | |
79 private transient Set mSubMapKeySet; | |
80 private transient Set mEntrySet; | |
81 | |
82 /** | |
83 * Construct a PropetyMap using a dot (".") separator. | |
84 */ | |
85 public PropertyMap() { | |
86 this(null, "."); | |
87 } | |
88 | |
89 /** | |
90 * @param map Map of defaults. | |
91 */ | |
92 public PropertyMap(Map map) { | |
93 this(map, "."); | |
94 } | |
95 | |
96 /** | |
97 * @param separator Sub-key separator, i.e. ".". | |
98 */ | |
99 public PropertyMap(String separator) { | |
100 this(null, separator); | |
101 } | |
102 | |
103 /** | |
104 * @param map Optional map of defaults. | |
105 * @param separator Sub-key separator, i.e. ".". | |
106 * | |
107 * @see #putDefaults(Map) | |
108 */ | |
109 public PropertyMap(Map map, String separator) { | |
110 UsageMap usageMap = new UsageMap(); | |
111 usageMap.setReverseOrder(true); | |
112 mMappings = usageMap; | |
113 mSeparator = separator; | |
114 if (map != null) { | |
115 putAll(map); | |
116 } | |
117 } | |
118 | |
119 private PropertyMap(String prefix, PropertyMap source) { | |
120 mMappings = source; | |
121 mSeparator = source.getSeparator(); | |
122 mPrefix = prefix; | |
123 } | |
124 | |
125 public String getSeparator() { | |
126 return mSeparator; | |
127 } | |
128 | |
129 /** | |
130 * Returns a view of this map for keys that are the same as the given key, | |
131 * or start with it (and a separator). The names of the keys in the | |
132 * sub-map have their prefix truncated. | |
133 * | |
134 * <p>A sub-map of | |
135 * | |
136 * <pre> | |
137 * "x" = "a" | |
138 * "foo" = "b" | |
139 * "foo." = "c" | |
140 * "foo.bar" = "d" | |
141 * "foo.bar.splat" = "e" | |
142 * "foo..bar" = "f" | |
143 * ".foo" = "g" | |
144 * "" = "h" | |
145 * null = "i" | |
146 * </pre> | |
147 * | |
148 * using a key of "foo" results in | |
149 * | |
150 * <pre> | |
151 * null = "b" | |
152 * "" = "c" | |
153 * "bar" = "d" | |
154 * "bar.splat" = "e" | |
155 * ".bar" = "f" | |
156 * </pre> | |
157 * | |
158 * using a key of "x" results in | |
159 * | |
160 * <pre> | |
161 * null = "a" | |
162 * </pre> | |
163 * | |
164 * using a key of "" results in | |
165 * | |
166 * <pre> | |
167 * "foo" = "g" | |
168 * null = "h" | |
169 * </pre> | |
170 * | |
171 * and using a key of null results in the original map. | |
172 */ | |
173 public PropertyMap subMap(String key) { | |
174 if (key == null) { | |
175 return this; | |
176 } | |
177 else { | |
178 return new PropertyMap(key, this); | |
179 } | |
180 } | |
181 | |
182 /** | |
183 * Returns the key names of each sub-map in this PropertyMap. The returned | |
184 * set is unmodifiable. | |
185 */ | |
186 public Set subMapKeySet() { | |
187 if (mSubMapKeySet == null) { | |
188 mSubMapKeySet = new SubMapKeySet(); | |
189 } | |
190 return mSubMapKeySet; | |
191 } | |
192 | |
193 public int size() { | |
194 if (mPrefix == null) { | |
195 return mMappings.size(); | |
196 } | |
197 else { | |
198 return super.size(); | |
199 } | |
200 } | |
201 | |
202 public boolean isEmpty() { | |
203 if (mPrefix == null) { | |
204 return mMappings.isEmpty(); | |
205 } | |
206 else { | |
207 return super.isEmpty(); | |
208 } | |
209 } | |
210 | |
211 public boolean containsKey(Object key) { | |
212 if (key == null) { | |
213 return containsKey((String)null); | |
214 } | |
215 else { | |
216 return containsKey(key.toString()); | |
217 } | |
218 } | |
219 | |
220 public boolean containsKey(String key) { | |
221 if (mPrefix == null) { | |
222 return mMappings.containsKey(internStr(key)); | |
223 } | |
224 else { | |
225 return mMappings.containsKey(mPrefix + mSeparator + key); | |
226 } | |
227 } | |
228 | |
229 public Object get(Object key) { | |
230 if (key == null) { | |
231 return get((String)null); | |
232 } | |
233 else { | |
234 return get(key.toString()); | |
235 } | |
236 } | |
237 | |
238 public Object get(String key) { | |
239 if (mPrefix == null) { | |
240 return mMappings.get(internStr(key)); | |
241 } | |
242 else { | |
243 return mMappings.get(mPrefix + mSeparator + key); | |
244 } | |
245 } | |
246 | |
247 /** | |
248 * Returns null if the given key isn't in this PropertyMap. | |
249 * | |
250 * @param key Key of property to read | |
251 */ | |
252 public String getString(String key) { | |
253 Object value = get(key); | |
254 if (value == null) { | |
255 return null; | |
256 } | |
257 else { | |
258 return value.toString(); | |
259 } | |
260 } | |
261 | |
262 /** | |
263 * Returns the default value if the given key isn't in this PropertyMap. | |
264 * | |
265 * @param key Key of property to read | |
266 * @param def Default value | |
267 */ | |
268 public String getString(String key, String def) { | |
269 Object value = get(key); | |
270 if (value == null) { | |
271 return def; | |
272 } | |
273 else { | |
274 return value.toString(); | |
275 } | |
276 } | |
277 | |
278 /** | |
279 * Returns 0 if the given key isn't in this PropertyMap. | |
280 * | |
281 * @param key Key of property to read | |
282 */ | |
283 public int getInt(String key) throws NumberFormatException { | |
284 String value = getString(key); | |
285 if (value == null) { | |
286 return 0; | |
287 } | |
288 else { | |
289 return Integer.parseInt(value); | |
290 } | |
291 } | |
292 | |
293 /** | |
294 * Returns the default value if the given key isn't in this PropertyMap or | |
295 * isn't a valid integer. | |
296 * | |
297 * @param key Key of property to read | |
298 * @param def Default value | |
299 */ | |
300 public int getInt(String key, int def) { | |
301 String value = getString(key); | |
302 if (value == null) { | |
303 return def; | |
304 } | |
305 else { | |
306 try { | |
307 return Integer.parseInt(value); | |
308 } | |
309 catch (NumberFormatException e) { | |
310 } | |
311 return def; | |
312 } | |
313 } | |
314 | |
315 /** | |
316 * Returns null if the given key isn't in this PropertyMap or it isn't a | |
317 * valid integer. | |
318 * | |
319 * @param key Key of property to read | |
320 */ | |
321 public Integer getInteger(String key) { | |
322 return getInteger(key, null); | |
323 } | |
324 | |
325 /** | |
326 * Returns the default value if the given key isn't in this PropertyMap or | |
327 * it isn't a valid integer. | |
328 * | |
329 * @param key Key of property to read | |
330 * @param def Default value | |
331 */ | |
332 public Integer getInteger(String key, Integer def) { | |
333 String value = getString(key); | |
334 if (value == null) { | |
335 return def; | |
336 } | |
337 else { | |
338 try { | |
339 return Integer.valueOf(value); | |
340 } | |
341 catch (NumberFormatException e) { | |
342 } | |
343 return def; | |
344 } | |
345 } | |
346 | |
347 /** | |
348 * Returns null if the given key isn't in this PropertyMap. | |
349 * | |
350 * @param key Key of property to read | |
351 */ | |
352 public Number getNumber(String key) throws NumberFormatException { | |
353 String value = getString(key); | |
354 if (value == null) { | |
355 return null; | |
356 } | |
357 else { | |
358 try { | |
359 return Integer.valueOf(value); | |
360 } | |
361 catch (NumberFormatException e) { | |
362 } | |
363 try { | |
364 return Long.valueOf(value); | |
365 } | |
366 catch (NumberFormatException e) { | |
367 } | |
368 return Double.valueOf(value); | |
369 } | |
370 } | |
371 | |
372 /** | |
373 * Returns the default value if the given key isn't in this PropertyMap or | |
374 * isn't a valid number. | |
375 * | |
376 * @param key Key of property to read | |
377 * @param def Default value | |
378 */ | |
379 public Number getNumber(String key, Number def) { | |
380 String value = getString(key); | |
381 if (value == null) { | |
382 return def; | |
383 } | |
384 else { | |
385 try { | |
386 return Integer.valueOf(value); | |
387 } | |
388 catch (NumberFormatException e) { | |
389 } | |
390 try { | |
391 return Long.valueOf(value); | |
392 } | |
393 catch (NumberFormatException e) { | |
394 } | |
395 try { | |
396 return Double.valueOf(value); | |
397 } | |
398 catch (NumberFormatException e) { | |
399 } | |
400 return def; | |
401 } | |
402 } | |
403 | |
404 /** | |
405 * Returns true only if value is "true", ignoring case. | |
406 * | |
407 * @param key Key of property to read | |
408 */ | |
409 public boolean getBoolean(String key) { | |
410 return "true".equalsIgnoreCase(getString(key)); | |
411 } | |
412 | |
413 /** | |
414 * Returns the default value if the given key isn't in this PropertyMap or | |
415 * if the the value isn't equal to "true", ignoring case. | |
416 * | |
417 * @param key Key of property to read | |
418 * @param def Default value | |
419 */ | |
420 public boolean getBoolean(String key, boolean def) { | |
421 String value = getString(key); | |
422 if (value == null) { | |
423 return def; | |
424 } | |
425 else { | |
426 return "true".equalsIgnoreCase(value); | |
427 } | |
428 } | |
429 | |
430 public Set entrySet() { | |
431 if (mPrefix == null) { | |
432 return mMappings.entrySet(); | |
433 } | |
434 else { | |
435 if (mEntrySet == null) { | |
436 mEntrySet = new EntrySet(); | |
437 } | |
438 return mEntrySet; | |
439 } | |
440 } | |
441 | |
442 public Set keySet() { | |
443 if (mPrefix == null) { | |
444 return mMappings.keySet(); | |
445 } | |
446 else { | |
447 return super.keySet(); | |
448 } | |
449 } | |
450 | |
451 public Collection values() { | |
452 if (mPrefix == null) { | |
453 return mMappings.values(); | |
454 } | |
455 else { | |
456 return super.values(); | |
457 } | |
458 } | |
459 | |
460 /** | |
461 * The key is always converted to a String. | |
462 */ | |
463 public Object put(Object key, Object value) { | |
464 if (key == null) { | |
465 return put((String)null, value); | |
466 } | |
467 else { | |
468 return put(key.toString(), value); | |
469 } | |
470 } | |
471 | |
472 public Object put(String key, Object value) { | |
473 if (mPrefix == null) { | |
474 return mMappings.put(internStr(key), value); | |
475 } | |
476 else { | |
477 return mMappings.put(mPrefix + mSeparator + key, value); | |
478 } | |
479 } | |
480 | |
481 /** | |
482 * Copies the entries of the given map into this one only for keys that | |
483 * aren't contained in this map. Is equivalent to putAll if this map is | |
484 * empty. | |
485 */ | |
486 public void putDefaults(Map map) { | |
487 Iterator it = map.entrySet().iterator(); | |
488 while (it.hasNext()) { | |
489 Map.Entry entry = (Map.Entry)it.next(); | |
490 Object key = entry.getKey(); | |
491 if (!containsKey(key)) { | |
492 put(key, entry.getValue()); | |
493 } | |
494 } | |
495 } | |
496 | |
497 public Object remove(Object key) { | |
498 if (key == null) { | |
499 return remove((String)null); | |
500 } | |
501 else { | |
502 return remove(key.toString()); | |
503 } | |
504 } | |
505 | |
506 public Object remove(String key) { | |
507 if (mPrefix == null) { | |
508 return mMappings.remove(internStr(key)); | |
509 } | |
510 else { | |
511 return mMappings.remove(mPrefix + mSeparator + key); | |
512 } | |
513 } | |
514 | |
515 public void clear() { | |
516 if (mPrefix == null) { | |
517 mMappings.clear(); | |
518 } | |
519 else { | |
520 super.clear(); | |
521 } | |
522 } | |
523 | |
524 private class SubMapKeySet extends AbstractSet { | |
525 public int size() { | |
526 int size = 0; | |
527 Iterator it = iterator(); | |
528 while (it.hasNext()) { | |
529 it.next(); | |
530 size++; | |
531 } | |
532 return size; | |
533 } | |
534 | |
535 public Iterator iterator() { | |
536 return new Iterator() { | |
537 final Iterator mIterator = keySet().iterator(); | |
538 Set mSeen = new HashSet(); | |
539 String mNext; | |
540 | |
541 public boolean hasNext() { | |
542 if (mNext != null) { | |
543 return true; | |
544 } | |
545 | |
546 String sep = mSeparator; | |
547 | |
548 while (mIterator.hasNext()) { | |
549 String key = (String)mIterator.next(); | |
550 if (key != null) { | |
551 int index = key.indexOf(sep); | |
552 if (index >= 0) { | |
553 String subKey = key.substring(0, index); | |
554 if (!mSeen.contains(subKey)) { | |
555 mSeen.add(subKey); | |
556 mNext = subKey; | |
557 return true; | |
558 } | |
559 } | |
560 } | |
561 } | |
562 | |
563 return false; | |
564 } | |
565 | |
566 public Object next() { | |
567 if (!hasNext()) { | |
568 throw new NoSuchElementException(); | |
569 } | |
570 else { | |
571 Object next = mNext; | |
572 mNext = null; | |
573 return next; | |
574 } | |
575 } | |
576 | |
577 public void remove() { | |
578 throw new UnsupportedOperationException(); | |
579 } | |
580 }; | |
581 } | |
582 } | |
583 | |
584 private class EntrySet extends AbstractSet { | |
585 public int size() { | |
586 int size = 0; | |
587 Iterator it = iterator(); | |
588 while (it.hasNext()) { | |
589 it.next(); | |
590 size++; | |
591 } | |
592 return size; | |
593 } | |
594 | |
595 public boolean remove(Object obj) { | |
596 if (obj instanceof Map.Entry) { | |
597 Object key = ((Map.Entry)obj).getKey(); | |
598 Object value = ((Map.Entry)obj).getValue(); | |
599 | |
600 if (PropertyMap.this.containsKey(key)) { | |
601 Object v = PropertyMap.this.get(key); | |
602 if (v == null) { | |
603 if (value == null) { | |
604 PropertyMap.this.remove(key); | |
605 return true; | |
606 } | |
607 } | |
608 else if (v.equals(value)) { | |
609 PropertyMap.this.remove(key); | |
610 return true; | |
611 } | |
612 } | |
613 } | |
614 return false; | |
615 } | |
616 | |
617 public void clear() { | |
618 Iterator it = iterator(); | |
619 while (it.hasNext()) { | |
620 it.next(); | |
621 it.remove(); | |
622 } | |
623 } | |
624 | |
625 public Iterator iterator() { | |
626 return new Iterator() { | |
627 final Iterator mIterator = mMappings.entrySet().iterator(); | |
628 Map.Entry mNext; | |
629 | |
630 public boolean hasNext() { | |
631 if (mNext != null) { | |
632 return true; | |
633 } | |
634 | |
635 String prefix = mPrefix; | |
636 String sep = mSeparator; | |
637 | |
638 while (mIterator.hasNext()) { | |
639 final Map.Entry entry = (Map.Entry)mIterator.next(); | |
640 String key = (String)entry.getKey(); | |
641 | |
642 if (key != null && key.startsWith(prefix)) { | |
643 final String subKey; | |
644 if (key.length() == prefix.length()) { | |
645 subKey = null; | |
646 } | |
647 else { | |
648 if (key.startsWith(sep, prefix.length())) { | |
649 subKey = key.substring | |
650 (prefix.length() + sep.length()); | |
651 } | |
652 else { | |
653 continue; | |
654 } | |
655 } | |
656 | |
657 mNext = new AbstractMapEntry() { | |
658 public Object getKey() { | |
659 return subKey; | |
660 } | |
661 | |
662 public Object getValue() { | |
663 return entry.getValue(); | |
664 } | |
665 | |
666 public Object setValue(Object value) { | |
667 return put(subKey, value); | |
668 } | |
669 }; | |
670 | |
671 return true; | |
672 } | |
673 } | |
674 | |
675 return false; | |
676 } | |
677 | |
678 public Object next() { | |
679 if (!hasNext()) { | |
680 throw new NoSuchElementException(); | |
681 } | |
682 else { | |
683 Object next = mNext; | |
684 mNext = null; | |
685 return next; | |
686 } | |
687 } | |
688 | |
689 public void remove() { | |
690 mIterator.remove(); | |
691 } | |
692 }; | |
693 } | |
694 } | |
695 } |