1
2 package st.ata.util;
3
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.io.Serializable;
7 import java.util.Arrays;
8 import java.util.Date;
9 import java.util.Hashtable;
10 import java.util.Iterator;
11 import java.util.NoSuchElementException;
12
13
14
15
16 /*** Implementation of {@link AList} using simple hashtable. */
17 @SuppressWarnings({"unchecked"})
18 public class HashtableAList implements MutableAList, Serializable {
19 private static final long serialVersionUID = 3670660167336648644L;
20
21 private final Hashtable mTable = new Hashtable();
22
23 private static class DateArray {
24 public Date[] values;
25 public DateArray(Date[] v) { values = v; }
26 public boolean equals(Object obj) {
27 if (! (obj instanceof DateArray)) return false;
28 return Arrays.equals(values, ((DateArray)obj).values);
29 }
30 }
31
32
33 /*** Remove all key-value mappings. */
34 public void clear() {
35 close();
36 mTable.clear();
37 }
38
39 public boolean containsKey(String key) {
40 return mTable.containsKey(key);
41 }
42
43 /***
44 * Deep Clone.
45 *
46 * Limited implementation
47 * @return The cloned object.
48 */
49 public Object clone() {
50 HashtableAList copy = new HashtableAList();
51 String[] keys = getKeyArray();
52 for (int i=0; i<keys.length; i++) {
53 Object me=getObject(keys[i]);
54 if (me instanceof AList)
55 copy.putObject(keys[i], ((AList)me).clone());
56 else if (me instanceof AList[]) {
57 AList[] from = (AList[])me;
58 int count=from.length;
59 for (int j=0; j<from.length; j++) {
60 if (from[j]==null) {
61 count--;
62 }
63 }
64
65 AList[] copyAList = new AList[count];
66 for (int j=0; j<count; j++) {
67 if (from[j]==null) continue;
68 copyAList[j]=(AList)from[j].clone();
69 }
70 copy.putObject(keys[i], copyAList);
71 } else if (me instanceof String[]) {
72 String[] from = (String[])me;
73 String[] copyA = new String[from.length];
74 for (int j=0; j<from.length; j++)
75 copyA[j]=from[j];
76 copy.putObject(keys[i], copyA);
77 }
78 else if (me instanceof Long) {
79 copy.putObject(keys[i], new Long(((Long)me).longValue()));
80 } else if (me instanceof String) {
81 copy.putObject(keys[i], me);
82 } else
83 X.noimpl();
84 }
85 return copy;
86 }
87
88 /***
89 * Shallow copy of fields of <code>other</code> into <code>this</code>.
90 * @param other AList to copy from.
91 */
92 public void copyFrom(AList other) {
93 Iterator keys = other.getKeys();
94 while (keys.hasNext()) {
95 String key = (String)keys.next();
96 switch (other.getType(key)) {
97 case T_ALIST:
98 putAList(key, other.getAList(key));
99 break;
100 case T_DATE:
101 putDate(key, other.getDate(key));
102 break;
103 case T_INT:
104 putInt(key, other.getInt(key));
105 break;
106 case T_LONG:
107 putLong(key, other.getLong(key));
108 break;
109 case T_STRING:
110 putString(key, other.getString(key));
111 break;
112 case T_INPUTSTREAM:
113 putInputStream(key, other.getInputStream(key));
114 break;
115 case F_ARRAY | T_ALIST:
116 putAListArray(key, other.getAListArray(key));
117 break;
118 case F_ARRAY | T_DATE:
119 putDateArray(key, other.getDateArray(key));
120 break;
121 case F_ARRAY | T_INT:
122 putIntArray(key, other.getIntArray(key));
123 break;
124 case F_ARRAY | T_LONG:
125 putLongArray(key, other.getLongArray(key));
126 break;
127 case F_ARRAY | T_STRING:
128 putStringArray(key, other.getStringArray(key));
129 break;
130 case F_ARRAY_ARRAY | T_STRING:
131 putStringArrayArray(key, other.getStringArrayArray(key));
132 break;
133 case F_ARRAY | T_INPUTSTREAM:
134 putInputStreamArray(key, other.getInputStreamArray(key));
135 break;
136 default:
137 X.fail("Unexpected case");
138 }
139 }
140 }
141
142 public void copyKeysFrom(Iterator keys, AList other) {
143 copyKeysFrom(keys, other, true);
144 }
145
146 public void copyKeysFrom(Iterator keys, AList other, boolean clobber) {
147 for (; keys.hasNext();) {
148 String key = (String)keys.next();
149 if (!clobber && containsKey(key))
150 continue;
151 Object value = other.getObject(key);
152
153
154 if(value!=null) {
155 putObject(key,value);
156 }
157 }
158 }
159
160 public Object getObject(String key) {
161 return mTable.get(key);
162 }
163
164 public void putObject(String key, Object val) {
165 mTable.put(key, val);
166 }
167 public void remove(String key) {
168 mTable.remove(key);
169 }
170 public Iterator getKeys() {
171 return mTable.keySet().iterator();
172 }
173
174 public String[] getKeyArray() {
175 int i = 0;
176 String keys[] = new String[mTable.size()];
177 for(Iterator it = getKeys(); it.hasNext(); ++i)
178 keys[i] = (String)it.next();
179 return keys;
180 }
181
182 public int getInt(String key) {
183 Integer v = (Integer)mTable.get(key);
184 if (v == null) throw new NoSuchElementException(key);
185 return v.intValue();
186 }
187
188 public long getLong(String key) {
189 Long v = (Long)mTable.get(key);
190 if (v == null) throw new NoSuchElementException(key);
191 return v.longValue();
192 }
193
194 public String getString(String key) {
195 String v = (String)mTable.get(key);
196 if (v == null) throw new NoSuchElementException(key);
197 return v;
198 }
199
200 public AList getAList(String key) {
201 AList a = (AList) mTable.get(key);
202 if (a == null) throw new NoSuchElementException(key);
203 return a;
204 }
205
206 public Date getDate(String key) {
207 Date v = (Date)mTable.get(key);
208 if (v == null) throw new NoSuchElementException(key);
209 return v;
210 }
211
212 public InputStream getInputStream(String key) {
213 InputStream v = (InputStream)mTable.get(key);
214 if (v == null) throw new NoSuchElementException(key);
215 return v;
216 }
217
218 public int[] getIntArray(String key) {
219 int[] a = (int[]) mTable.get(key);
220 if (a == null) throw new NoSuchElementException(key);
221 return a;
222 }
223
224 public long[] getLongArray(String key) {
225 long[] a = (long[]) mTable.get(key);
226 if (a == null) throw new NoSuchElementException(key);
227 return a;
228 }
229
230 public String[] getStringArray(String key) {
231 String[] a = (String[]) mTable.get(key);
232 if (a == null) throw new NoSuchElementException(key);
233 return a;
234 }
235
236 public AList[] getAListArray(String key) {
237 AList[] a = (AList[]) mTable.get(key);
238 if (a == null) throw new NoSuchElementException(key);
239 return a;
240 }
241
242 public Date[] getDateArray(String key) {
243 DateArray a = (DateArray) mTable.get(key);
244 if (a == null) throw new NoSuchElementException(key);
245 return a.values;
246 }
247
248 public InputStream[] getInputStreamArray(String key) {
249 InputStream v[] = (InputStream [])mTable.get(key);
250 if (v == null) throw new NoSuchElementException(key);
251 return v;
252 }
253
254 public String[][] getStringArrayArray(String key) {
255 String[][] a = (String[][]) mTable.get(key);
256 if (a == null) throw new NoSuchElementException(key);
257 return a;
258 }
259
260
261 public void putInt(String key, int value) {
262 mTable.put(key, new Integer(value));
263 }
264
265 public void putLong(String key, long value) {
266 mTable.put(key, new Long(value));
267 }
268
269 public void putString(String key, String value) {
270 mTable.put(key, value);
271 }
272
273 public void putAList(String key, AList value) {
274 mTable.put(key, value);
275 }
276
277 public void putDate(String key, Date value) {
278 mTable.put(key, value);
279 }
280
281 public void putInputStream(String key, InputStream value) {
282 mTable.put(key, value);
283 }
284
285 public void putIntArray(String key, int[] value) {
286 mTable.put(key, value);
287 }
288
289 public void putLongArray(String key, long[] value) {
290 mTable.put(key, value);
291 }
292
293 public void putStringArray(String key, String[] value) {
294 mTable.put(key, value);
295 }
296
297 public void putAListArray(String key, AList[] value) {
298 mTable.put(key, value);
299 }
300
301 public void putDateArray(String key, Date[] value) {
302 mTable.put(key, new DateArray(value));
303 }
304
305 public void putInputStreamArray(String key, InputStream[] value) {
306 mTable.put(key, value);
307 }
308
309 public void putStringArrayArray(String key, String[][] value) {
310 mTable.put(key, value);
311 }
312
313
314 /*** Deep equals. Arrays need to have same values in same order to
315 * be considered equal.
316 * @param obj
317 * @return True if equals.
318 */
319 public boolean equals(Object obj) {
320 if (! (obj instanceof HashtableAList)) return false;
321 HashtableAList o = (HashtableAList)obj;
322 for (Iterator i = o.getKeys(); i.hasNext(); ) {
323 if (mTable.get(i.next()) == null) return false;
324 }
325 for (Iterator i = getKeys(); i.hasNext(); ) {
326 Object k = i.next();
327 Object v1 = mTable.get(k);
328 Object v2 = o.mTable.get(k);
329 if (! v1.equals(v2)) {
330 if (v1 instanceof AList[]) {
331 if (! (v2 instanceof AList[])) return false;
332 if (! Arrays.equals((Object[])v1, (Object[])v2))
333 return false;
334 } else if (v1 instanceof int[]) {
335 if (! (v2 instanceof int[])) return false;
336 if (! Arrays.equals((int[])v1, (int[])v2)) return false;
337 } else if (v1 instanceof long[]) {
338 if (! (v2 instanceof long[])) return false;
339 if (! Arrays.equals((long[])v1, (long[])v2)) return false;
340 } else if (v1 instanceof String[]) {
341 if (! (v2 instanceof String[])) return false;
342 if (! Arrays.equals((String[])v1, (String[])v2))
343 return false;
344 } else return false;
345 }
346 }
347 return true;
348 }
349
350 public int getType(String key) {
351 Object o = mTable.get(key);
352 if (o == null) return T_UNDEFINED;
353 else if (o instanceof AList) return T_ALIST;
354 else if (o instanceof Date) return T_DATE;
355 else if (o instanceof Integer) return T_INT;
356 else if (o instanceof Long) return T_LONG;
357 else if (o instanceof String) return T_STRING;
358 else if (o instanceof InputStream) return T_INPUTSTREAM;
359 else if (o instanceof AList[]) return T_ALIST | F_ARRAY;
360 else if (o instanceof DateArray) return T_DATE | F_ARRAY;
361 else if (o instanceof int[]) return T_INT | F_ARRAY;
362 else if (o instanceof long[]) return T_LONG | F_ARRAY;
363 else if (o instanceof String[]) return T_STRING | F_ARRAY;
364 else if (o instanceof InputStream[]) return T_INPUTSTREAM | F_ARRAY;
365 else if (o instanceof String[][]) return T_STRING | F_ARRAY_ARRAY;
366 else if (o instanceof Object[]) return T_OBJECT | F_ARRAY;
367 else if (o instanceof Object) return T_OBJECT;
368 else X.fail("Should not get here " + o);
369 return -1;
370 }
371
372 /*** Useful for creating test-tables for debugging. The object
373 should be one of an {@link AList}, {@link Date}, {@link
374 Integer}, {@link Long}, {@link String}, {@link AList}[],
375 {@link Date}[], <code>int[]</code>, <code>long[]</code>,
376 <code>{@link String}[]</code>, <code>ZE[]</code>
377 or <code>ZE[][]</code>. In the case of <code>ZE[]</code>,
378 the entry is treated as an {@link AList}. Similaryly
379 if the entry is <code>ZE[][]</code> it is treated as
380 {@link AList}[]. */
381 public static class ZE {
382 public final String key;
383 public final Object val;
384 public ZE(String k, Object v) { key = k; val = v; }
385 }
386
387 public void zInsert(ZE[] entries) {
388 for (int i = 0; i < entries.length; i++) {
389 zInsert(entries[i]);
390 }
391 }
392
393 public void zInsert(ZE entry) {
394 if (entry.val instanceof Date[]) {
395 mTable.put(entry.key, new DateArray((Date[])entry.val));
396 } else if (entry.val instanceof ZE[]) {
397 HashtableAList v = new HashtableAList();
398 v.zInsert((ZE[])entry.val);
399 mTable.put(entry.key, v);
400 } else if (entry.val instanceof ZE[][]) {
401 AList v[] = new AList[((ZE[][])entry.val).length];
402 for(int j = 0; j < v.length; ++j) {
403 HashtableAList h = new HashtableAList();
404 h.zInsert(((ZE[][])entry.val)[j]);
405 v[j] = h;
406 }
407 mTable.put(entry.key, v);
408 } else {
409 mTable.put(entry.key, entry.val);
410 }
411 }
412
413 public void close() {
414 String[] keys = getKeyArray();
415 try {
416 for (int i = 0; i < keys.length; i++) {
417 if (getType(keys[i]) == T_INPUTSTREAM) {
418 getInputStream(keys[i]).close();
419 } else if (getType(keys[i]) == (T_INPUTSTREAM | F_ARRAY)) {
420 InputStream[] ins = getInputStreamArray(keys[i]);
421 for (int j = 0; j < ins.length; j++) {
422 ins[j].close();
423 }
424 } else if (getType(keys[i]) == T_ALIST) {
425 getAList(keys[i]).close();
426 } else if (getType(keys[i]) == (T_ALIST | F_ARRAY)) {
427 AList[] als = getAListArray(keys[i]);
428 for (int j = 0; j < als.length; j++) {
429 als[j].close();
430 }
431 }
432 }
433 } catch (IOException e) {
434 throw X.toRTE(e);
435 }
436 }
437
438 public AList newAList() {
439 return new HashtableAList();
440 }
441
442 public String toString() {
443 return mTable.toString();
444 }
445
446 /***
447 * Enhance given object's default String display for appearing
448 * nested in a pretty AList String.
449 *
450 * @param obj Object to prettify
451 * @return prettified String
452 */
453 protected String prettyString(Object obj) {
454 if (obj instanceof AList) return ((AList)obj).toPrettyString();
455 else if (obj instanceof AList[]) return prettyString((AList[])obj);
456 else return "<"+obj+">";
457 }
458
459
460
461
462 public String toPrettyString() {
463 StringBuilder builder = new StringBuilder();
464 builder.append("{ ");
465 boolean needsComma = false;
466 for( String key : getKeyArray()) {
467 if(needsComma) {
468 builder.append(", ");
469 }
470 builder.append(key);
471 builder.append(": ");
472 builder.append(prettyString(mTable.get(key)));
473 needsComma = true;
474 }
475 builder.append(" }");
476 return builder.toString();
477 }
478
479 /***
480 * Provide a slightly-improved String of AList[]
481 *
482 * @param alists
483 * @return prettified (in square brackets) of AList[]
484 */
485 protected String prettyString(AList[] alists) {
486 StringBuilder builder = new StringBuilder();
487 builder.append("[ ");
488 boolean needsComma = false;
489 for( AList alist : alists) {
490 if(alist==null) continue;
491 if(needsComma) {
492 builder.append(", ");
493 }
494 builder.append(alist.toPrettyString());
495 needsComma = true;
496 }
497 builder.append(" ]");
498 return builder.toString();
499 }
500 }