1 /*
2 * Copyright (c) 2003, Henri Yandell
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or
6 * without modification, are permitted provided that the
7 * following conditions are met:
8 *
9 * + Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * + Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * + Neither the name of Genjava-Core nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32 package com.generationjava.collections;
33
34 import java.util.Collection;
35 import java.util.HashMap;
36 import java.util.Iterator;
37 import java.util.LinkedList;
38 import java.util.Map;
39 import java.util.Set;
40 import java.util.HashSet;
41
42 /***
43 * A Map in which multiple keys are used, ie) an Object array
44 * of keys.
45 *
46 * @author bayard@generationjava.com
47 * @date 2001-12-19
48 */
49 /// Pass in: { "key1", "key2", "key3" } and a value.
50 public class MultiKeyedMap implements Map {
51
52 // this is not lazy instantiation. One MUST be
53 // constructed in a constructor.
54 private Map myMap = null;
55
56 public MultiKeyedMap(Map m) {
57 myMap = m;
58 }
59
60 public MultiKeyedMap() {
61 this(new HashMap());
62 }
63
64 /* map interface */
65
66 // Removes all mappings from this map (optional operation) {.
67 public void clear() {
68 myMap.clear();
69 }
70
71 // Returns true if this map contains a mapping for the specified key.
72 public boolean containsKey(Object key) {
73 return get(key) != null;
74 }
75
76 // Returns true if this map maps one or more keys to the specified value.
77 public boolean containsValue(Object value) {
78 return this.containsValue(this.myMap, value);
79 }
80
81 private boolean containsValue(Map map, Object value) {
82 Iterator values = map.values().iterator();
83 while(values.hasNext()) {
84 Object obj = values.next();
85 if(obj instanceof Map) {
86 if(containsValue((Map)obj, value) ) {
87 return true;
88 }
89 }
90 if(value == obj) {
91 return true;
92 }
93 if(value.equals(obj)) {
94 return true;
95 }
96 }
97
98 return false;
99 }
100
101 // Returns a set view of the mappings contained in this map.
102 public Set entrySet() {
103 // FIX : Make this actually work.
104 return myMap.entrySet();
105 }
106
107 // Compares the specified object with this map for equality.
108 public boolean equals(Object o) {
109 return myMap.equals(o);
110 }
111
112 // Returns the value to which this map maps the specified key.
113 public Object get(Object key) {
114 if(key instanceof Object[]) {
115 Object[] keys = (Object[])key;
116 Map map = this.myMap;
117 int szLessOne = keys.length - 1;
118 for(int i=0; i<szLessOne; i++) {
119 Object value = map.get(keys[i]);
120 if(value instanceof Map) {
121 map = (Map)value;
122 } else {
123 return null;
124 }
125 }
126 return map.get(keys[szLessOne]);
127 } else {
128 return this.myMap.get(key);
129 }
130 }
131
132 // Returns the hash code value for this map.
133 public int hashCode() {
134 return myMap.hashCode();
135 }
136
137 // Returns true if this map contains no key-value mappings.
138 public boolean isEmpty() {
139 return myMap.isEmpty();
140 }
141
142 // Returns a set view of the keys contained in this map.
143 /// NOTE: If fred:foo is added, it will return that key as
144 /// the array [fred]. Arrays are the correct way to talk
145 /// to this class.
146 public Set keySet() {
147 Set set = new HashSet();
148 keySetRecurse(this.myMap, set, new LinkedList());
149 return set;
150 }
151
152 // SLOW :(
153 private void keySetRecurse(Map map, Set set, LinkedList head) {
154 Iterator keys = map.keySet().iterator();
155 while(keys.hasNext()) {
156 Object key = keys.next();
157 head.add(key);
158 Object value = map.get(key);
159 if(value instanceof Map) {
160 keySetRecurse((Map)value, set, head);
161 } else {
162 set.add(head.toArray());
163 }
164 head.removeLast();
165 }
166 }
167
168 // Put a value into the specified map (optional operation)
169 public Object put(Object key, Object value) {
170 if(key instanceof Object[]) {
171 Object[] keys = (Object[])key;
172 int szLessOne = keys.length-1;
173 Map map = this.myMap;
174 for(int i=0; i<szLessOne; i++) {
175 Object obj = map.get(keys[i]);
176 if(obj instanceof Map) {
177 map = (Map)obj;
178 } else {
179 Map oldmap = map;
180 map = CollectionsW.cloneNewEmptyMap(map);
181 oldmap.put(keys[i], map);
182 }
183 }
184 return map.put(keys[szLessOne], value);
185 } else {
186 return this.myMap.put(key, value);
187 }
188 }
189
190 // Copies all of the mappings from the specified map to this map (optional operation)
191 public void putAll(Map t) {
192 if(t != null) {
193 Set set = t.keySet();
194 if(set != null) {
195 Iterator iterator = set.iterator();
196 while(iterator.hasNext()) {
197 Object key = iterator.next();
198 put(key,t.get(key));
199 }
200 }
201 }
202 }
203
204 // Removes the mapping for this key from this map if present (optional operation)
205 public Object remove(Object key) {
206 if(key instanceof Object[]) {
207 Object[] keys = (Object[])key;
208 int szLessOne = keys.length-1;
209 Map map = this.myMap;
210 for(int i=0; i<szLessOne; i++) {
211 Object obj = map.get(keys[i]);
212 if(obj instanceof Map) {
213 map = (Map)obj;
214 } else {
215 return null;
216 }
217 }
218 return map.remove(keys[szLessOne]);
219 } else {
220 return this.myMap.remove(key);
221 }
222 }
223
224 // Returns the number of key-value mappings in this map.
225 public int size() {
226 // QUERY: This is okay I think. Should it return size of flattened???
227 return values().size();
228 }
229
230 // Returns a collection view of the values contained in this map.
231 public Collection values() {
232 Collection list = new LinkedList();
233 valuesRecurse( this.myMap, list );
234 return list;
235 }
236
237 private void valuesRecurse(Map map, Collection list) {
238 Iterator iterator = map.keySet().iterator();
239 while(iterator.hasNext()) {
240 Object key = iterator.next();
241 Object value = map.get(key);
242 if(value instanceof Map) {
243 valuesRecurse((Map)value, list);
244 } else {
245 list.add(value);
246 }
247 }
248 }
249
250 public String toString() {
251 return myMap.toString();
252 }
253
254 }