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 }