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 for (; keys.hasNext();) {
144 String key = (String)keys.next();
145 Object value = other.getObject(key);
146
147
148 if(value!=null) {
149 putObject(key,value);
150 }
151 }
152 }
153
154 public Object getObject(String key) {
155 return mTable.get(key);
156 }
157
158 public void putObject(String key, Object val) {
159 mTable.put(key, val);
160 }
161 public void remove(String key) {
162 mTable.remove(key);
163 }
164 public Iterator getKeys() {
165 return mTable.keySet().iterator();
166 }
167
168 public String[] getKeyArray() {
169 int i = 0;
170 String keys[] = new String[mTable.size()];
171 for(Iterator it = getKeys(); it.hasNext(); ++i)
172 keys[i] = (String)it.next();
173 return keys;
174 }
175
176 public int getInt(String key) {
177 Integer v = (Integer)mTable.get(key);
178 if (v == null) throw new NoSuchElementException(key);
179 return v.intValue();
180 }
181
182 public long getLong(String key) {
183 Long v = (Long)mTable.get(key);
184 if (v == null) throw new NoSuchElementException(key);
185 return v.longValue();
186 }
187
188 public String getString(String key) {
189 String v = (String)mTable.get(key);
190 if (v == null) throw new NoSuchElementException(key);
191 return v;
192 }
193
194 public AList getAList(String key) {
195 AList a = (AList) mTable.get(key);
196 if (a == null) throw new NoSuchElementException(key);
197 return a;
198 }
199
200 public Date getDate(String key) {
201 Date v = (Date)mTable.get(key);
202 if (v == null) throw new NoSuchElementException(key);
203 return v;
204 }
205
206 public InputStream getInputStream(String key) {
207 InputStream v = (InputStream)mTable.get(key);
208 if (v == null) throw new NoSuchElementException(key);
209 return v;
210 }
211
212 public int[] getIntArray(String key) {
213 int[] a = (int[]) mTable.get(key);
214 if (a == null) throw new NoSuchElementException(key);
215 return a;
216 }
217
218 public long[] getLongArray(String key) {
219 long[] a = (long[]) mTable.get(key);
220 if (a == null) throw new NoSuchElementException(key);
221 return a;
222 }
223
224 public String[] getStringArray(String key) {
225 String[] a = (String[]) mTable.get(key);
226 if (a == null) throw new NoSuchElementException(key);
227 return a;
228 }
229
230 public AList[] getAListArray(String key) {
231 AList[] a = (AList[]) mTable.get(key);
232 if (a == null) throw new NoSuchElementException(key);
233 return a;
234 }
235
236 public Date[] getDateArray(String key) {
237 DateArray a = (DateArray) mTable.get(key);
238 if (a == null) throw new NoSuchElementException(key);
239 return a.values;
240 }
241
242 public InputStream[] getInputStreamArray(String key) {
243 InputStream v[] = (InputStream [])mTable.get(key);
244 if (v == null) throw new NoSuchElementException(key);
245 return v;
246 }
247
248 public String[][] getStringArrayArray(String key) {
249 String[][] a = (String[][]) mTable.get(key);
250 if (a == null) throw new NoSuchElementException(key);
251 return a;
252 }
253
254
255 public void putInt(String key, int value) {
256 mTable.put(key, new Integer(value));
257 }
258
259 public void putLong(String key, long value) {
260 mTable.put(key, new Long(value));
261 }
262
263 public void putString(String key, String value) {
264 mTable.put(key, value);
265 }
266
267 public void putAList(String key, AList value) {
268 mTable.put(key, value);
269 }
270
271 public void putDate(String key, Date value) {
272 mTable.put(key, value);
273 }
274
275 public void putInputStream(String key, InputStream value) {
276 mTable.put(key, value);
277 }
278
279 public void putIntArray(String key, int[] value) {
280 mTable.put(key, value);
281 }
282
283 public void putLongArray(String key, long[] value) {
284 mTable.put(key, value);
285 }
286
287 public void putStringArray(String key, String[] value) {
288 mTable.put(key, value);
289 }
290
291 public void putAListArray(String key, AList[] value) {
292 mTable.put(key, value);
293 }
294
295 public void putDateArray(String key, Date[] value) {
296 mTable.put(key, new DateArray(value));
297 }
298
299 public void putInputStreamArray(String key, InputStream[] value) {
300 mTable.put(key, value);
301 }
302
303 public void putStringArrayArray(String key, String[][] value) {
304 mTable.put(key, value);
305 }
306
307
308 /*** Deep equals. Arrays need to have same values in same order to
309 * be considered equal.
310 * @param obj
311 * @return True if equals.
312 */
313 public boolean equals(Object obj) {
314 if (! (obj instanceof HashtableAList)) return false;
315 HashtableAList o = (HashtableAList)obj;
316 for (Iterator i = o.getKeys(); i.hasNext(); ) {
317 if (mTable.get(i.next()) == null) return false;
318 }
319 for (Iterator i = getKeys(); i.hasNext(); ) {
320 Object k = i.next();
321 Object v1 = mTable.get(k);
322 Object v2 = o.mTable.get(k);
323 if (! v1.equals(v2)) {
324 if (v1 instanceof AList[]) {
325 if (! (v2 instanceof AList[])) return false;
326 if (! Arrays.equals((Object[])v1, (Object[])v2))
327 return false;
328 } else if (v1 instanceof int[]) {
329 if (! (v2 instanceof int[])) return false;
330 if (! Arrays.equals((int[])v1, (int[])v2)) return false;
331 } else if (v1 instanceof long[]) {
332 if (! (v2 instanceof long[])) return false;
333 if (! Arrays.equals((long[])v1, (long[])v2)) return false;
334 } else if (v1 instanceof String[]) {
335 if (! (v2 instanceof String[])) return false;
336 if (! Arrays.equals((String[])v1, (String[])v2))
337 return false;
338 } else return false;
339 }
340 }
341 return true;
342 }
343
344 public int getType(String key) {
345 Object o = mTable.get(key);
346 if (o == null) return T_UNDEFINED;
347 else if (o instanceof AList) return T_ALIST;
348 else if (o instanceof Date) return T_DATE;
349 else if (o instanceof Integer) return T_INT;
350 else if (o instanceof Long) return T_LONG;
351 else if (o instanceof String) return T_STRING;
352 else if (o instanceof InputStream) return T_INPUTSTREAM;
353 else if (o instanceof AList[]) return T_ALIST | F_ARRAY;
354 else if (o instanceof DateArray) return T_DATE | F_ARRAY;
355 else if (o instanceof int[]) return T_INT | F_ARRAY;
356 else if (o instanceof long[]) return T_LONG | F_ARRAY;
357 else if (o instanceof String[]) return T_STRING | F_ARRAY;
358 else if (o instanceof InputStream[]) return T_INPUTSTREAM | F_ARRAY;
359 else if (o instanceof String[][]) return T_STRING | F_ARRAY_ARRAY;
360 else if (o instanceof Object[]) return T_OBJECT | F_ARRAY;
361 else if (o instanceof Object) return T_OBJECT;
362 else X.fail("Should not get here " + o);
363 return -1;
364 }
365
366 /*** Useful for creating test-tables for debugging. The object
367 should be one of an {@link AList}, {@link Date}, {@link
368 Integer}, {@link Long}, {@link String}, {@link AList}[],
369 {@link Date}[], <code>int[]</code>, <code>long[]</code>,
370 <code>{@link String}[]</code>, <code>ZE[]</code>
371 or <code>ZE[][]</code>. In the case of <code>ZE[]</code>,
372 the entry is treated as an {@link AList}. Similaryly
373 if the entry is <code>ZE[][]</code> it is treated as
374 {@link AList}[]. */
375 public static class ZE {
376 public final String key;
377 public final Object val;
378 public ZE(String k, Object v) { key = k; val = v; }
379 }
380
381 public void zInsert(ZE[] entries) {
382 for (int i = 0; i < entries.length; i++) {
383 zInsert(entries[i]);
384 }
385 }
386
387 public void zInsert(ZE entry) {
388 if (entry.val instanceof Date[]) {
389 mTable.put(entry.key, new DateArray((Date[])entry.val));
390 } else if (entry.val instanceof ZE[]) {
391 HashtableAList v = new HashtableAList();
392 v.zInsert((ZE[])entry.val);
393 mTable.put(entry.key, v);
394 } else if (entry.val instanceof ZE[][]) {
395 AList v[] = new AList[((ZE[][])entry.val).length];
396 for(int j = 0; j < v.length; ++j) {
397 HashtableAList h = new HashtableAList();
398 h.zInsert(((ZE[][])entry.val)[j]);
399 v[j] = h;
400 }
401 mTable.put(entry.key, v);
402 } else {
403 mTable.put(entry.key, entry.val);
404 }
405 }
406
407 public void close() {
408 String[] keys = getKeyArray();
409 try {
410 for (int i = 0; i < keys.length; i++) {
411 if (getType(keys[i]) == T_INPUTSTREAM) {
412 getInputStream(keys[i]).close();
413 } else if (getType(keys[i]) == (T_INPUTSTREAM | F_ARRAY)) {
414 InputStream[] ins = getInputStreamArray(keys[i]);
415 for (int j = 0; j < ins.length; j++) {
416 ins[j].close();
417 }
418 } else if (getType(keys[i]) == T_ALIST) {
419 getAList(keys[i]).close();
420 } else if (getType(keys[i]) == (T_ALIST | F_ARRAY)) {
421 AList[] als = getAListArray(keys[i]);
422 for (int j = 0; j < als.length; j++) {
423 als[j].close();
424 }
425 }
426 }
427 } catch (IOException e) {
428 throw X.toRTE(e);
429 }
430 }
431
432 public AList newAList() {
433 return new HashtableAList();
434 }
435
436 public String toString() {
437 return mTable.toString();
438 }
439
440 /***
441 * Enhance given object's default String display for appearing
442 * nested in a pretty AList String.
443 *
444 * @param obj Object to prettify
445 * @return prettified String
446 */
447 protected String prettyString(Object obj) {
448 if (obj instanceof AList) return ((AList)obj).toPrettyString();
449 else if (obj instanceof AList[]) return prettyString((AList[])obj);
450 else return "<"+obj+">";
451 }
452
453
454
455
456 public String toPrettyString() {
457 StringBuilder builder = new StringBuilder();
458 builder.append("{ ");
459 boolean needsComma = false;
460 for( String key : getKeyArray()) {
461 if(needsComma) {
462 builder.append(", ");
463 }
464 builder.append(key);
465 builder.append(": ");
466 builder.append(prettyString(mTable.get(key)));
467 needsComma = true;
468 }
469 builder.append(" }");
470 return builder.toString();
471 }
472
473 /***
474 * Provide a slightly-improved String of AList[]
475 *
476 * @param alists
477 * @return prettified (in square brackets) of AList[]
478 */
479 protected String prettyString(AList[] alists) {
480 StringBuilder builder = new StringBuilder();
481 builder.append("[ ");
482 boolean needsComma = false;
483 for( AList alist : alists) {
484 if(alist==null) continue;
485 if(needsComma) {
486 builder.append(", ");
487 }
488 builder.append(alist.toPrettyString());
489 needsComma = true;
490 }
491 builder.append(" ]");
492 return builder.toString();
493 }
494 }