001: package st.ata.util;
002:
003: import java.io.IOException;
004: import java.io.InputStream;
005: import java.io.Serializable;
006: import java.util.Arrays;
007: import java.util.Date;
008: import java.util.Hashtable;
009: import java.util.Iterator;
010: import java.util.NoSuchElementException;
011:
012: // Tested by TestHashtableAList
013:
014: /** Implementation of {@link AList} using simple hashtable. */
015: @SuppressWarnings({"unchecked"})
016: public class HashtableAList implements MutableAList, Serializable {
017: private static final long serialVersionUID = 3670660167336648644L;
018:
019: private final Hashtable mTable = new Hashtable();
020:
021: private static class DateArray {
022: public Date[] values;
023:
024: public DateArray(Date[] v) {
025: values = v;
026: }
027:
028: public boolean equals(Object obj) {
029: if (!(obj instanceof DateArray))
030: return false;
031: return Arrays.equals(values, ((DateArray) obj).values);
032: }
033: }
034:
035: /** Remove all key-value mappings. */
036: public void clear() {
037: close();
038: mTable.clear();
039: }
040:
041: public boolean containsKey(String key) {
042: return mTable.containsKey(key);
043: }
044:
045: /**
046: * Deep Clone.
047: *
048: * Limited implementation
049: * @return The cloned object.
050: */
051: public Object clone() {
052: HashtableAList copy = new HashtableAList();
053: String[] keys = getKeyArray();
054: for (int i = 0; i < keys.length; i++) {
055: Object me = getObject(keys[i]);
056: if (me instanceof AList)
057: copy.putObject(keys[i], ((AList) me).clone());
058: else if (me instanceof AList[]) {
059: AList[] from = (AList[]) me;
060: int count = from.length;
061: for (int j = 0; j < from.length; j++) {
062: if (from[j] == null) {
063: count--;
064: }
065: }
066:
067: AList[] copyAList = new AList[count];
068: for (int j = 0; j < count; j++) {
069: if (from[j] == null)
070: continue;
071: copyAList[j] = (AList) from[j].clone();
072: }
073: copy.putObject(keys[i], copyAList);
074: } else if (me instanceof String[]) {
075: String[] from = (String[]) me;
076: String[] copyA = new String[from.length];
077: for (int j = 0; j < from.length; j++)
078: copyA[j] = from[j];
079: copy.putObject(keys[i], copyA);
080: } else if (me instanceof Long) {
081: copy.putObject(keys[i], new Long(((Long) me)
082: .longValue()));
083: } else if (me instanceof String) {
084: copy.putObject(keys[i], me);
085: } else
086: X.noimpl();
087: }
088: return copy;
089: }
090:
091: /**
092: * Shallow copy of fields of <code>other</code> into <code>this</code>.
093: * @param other AList to copy from.
094: */
095: public void copyFrom(AList other) {
096: Iterator keys = other.getKeys();
097: while (keys.hasNext()) {
098: String key = (String) keys.next();
099: switch (other.getType(key)) {
100: case T_ALIST:
101: putAList(key, other.getAList(key));
102: break;
103: case T_DATE:
104: putDate(key, other.getDate(key));
105: break;
106: case T_INT:
107: putInt(key, other.getInt(key));
108: break;
109: case T_LONG:
110: putLong(key, other.getLong(key));
111: break;
112: case T_STRING:
113: putString(key, other.getString(key));
114: break;
115: case T_INPUTSTREAM:
116: putInputStream(key, other.getInputStream(key));
117: break;
118: case F_ARRAY | T_ALIST:
119: putAListArray(key, other.getAListArray(key));
120: break;
121: case F_ARRAY | T_DATE:
122: putDateArray(key, other.getDateArray(key));
123: break;
124: case F_ARRAY | T_INT:
125: putIntArray(key, other.getIntArray(key));
126: break;
127: case F_ARRAY | T_LONG:
128: putLongArray(key, other.getLongArray(key));
129: break;
130: case F_ARRAY | T_STRING:
131: putStringArray(key, other.getStringArray(key));
132: break;
133: case F_ARRAY_ARRAY | T_STRING:
134: putStringArrayArray(key, other.getStringArrayArray(key));
135: break;
136: case F_ARRAY | T_INPUTSTREAM:
137: putInputStreamArray(key, other.getInputStreamArray(key));
138: break;
139: default:
140: X.fail("Unexpected case");
141: }
142: }
143: }
144:
145: public void copyKeysFrom(Iterator keys, AList other) {
146: for (; keys.hasNext();) {
147: String key = (String) keys.next();
148: Object value = other.getObject(key);
149: // TODO: consider shallow or deep copy in some cases?
150: // perhaps controlled by a third parameter?
151: if (value != null) {
152: putObject(key, value);
153: }
154: }
155: }
156:
157: public Object getObject(String key) {
158: return mTable.get(key);
159: }
160:
161: public void putObject(String key, Object val) {
162: mTable.put(key, val);
163: }
164:
165: public void remove(String key) {
166: mTable.remove(key);
167: }
168:
169: public Iterator getKeys() {
170: return mTable.keySet().iterator();
171: }
172:
173: public String[] getKeyArray() {
174: int i = 0;
175: String keys[] = new String[mTable.size()];
176: for (Iterator it = getKeys(); it.hasNext(); ++i)
177: keys[i] = (String) it.next();
178: return keys;
179: }
180:
181: public int getInt(String key) {
182: Integer v = (Integer) mTable.get(key);
183: if (v == null)
184: 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)
191: throw new NoSuchElementException(key);
192: return v.longValue();
193: }
194:
195: public String getString(String key) {
196: String v = (String) mTable.get(key);
197: if (v == null)
198: throw new NoSuchElementException(key);
199: return v;
200: }
201:
202: public AList getAList(String key) {
203: AList a = (AList) mTable.get(key);
204: if (a == null)
205: throw new NoSuchElementException(key);
206: return a;
207: }
208:
209: public Date getDate(String key) {
210: Date v = (Date) mTable.get(key);
211: if (v == null)
212: throw new NoSuchElementException(key);
213: return v;
214: }
215:
216: public InputStream getInputStream(String key) {
217: InputStream v = (InputStream) mTable.get(key);
218: if (v == null)
219: throw new NoSuchElementException(key);
220: return v;
221: }
222:
223: public int[] getIntArray(String key) {
224: int[] a = (int[]) mTable.get(key);
225: if (a == null)
226: throw new NoSuchElementException(key);
227: return a;
228: }
229:
230: public long[] getLongArray(String key) {
231: long[] a = (long[]) mTable.get(key);
232: if (a == null)
233: throw new NoSuchElementException(key);
234: return a;
235: }
236:
237: public String[] getStringArray(String key) {
238: String[] a = (String[]) mTable.get(key);
239: if (a == null)
240: throw new NoSuchElementException(key);
241: return a;
242: }
243:
244: public AList[] getAListArray(String key) {
245: AList[] a = (AList[]) mTable.get(key);
246: if (a == null)
247: throw new NoSuchElementException(key);
248: return a;
249: }
250:
251: public Date[] getDateArray(String key) {
252: DateArray a = (DateArray) mTable.get(key);
253: if (a == null)
254: throw new NoSuchElementException(key);
255: return a.values;
256: }
257:
258: public InputStream[] getInputStreamArray(String key) {
259: InputStream v[] = (InputStream[]) mTable.get(key);
260: if (v == null)
261: throw new NoSuchElementException(key);
262: return v;
263: }
264:
265: public String[][] getStringArrayArray(String key) {
266: String[][] a = (String[][]) mTable.get(key);
267: if (a == null)
268: throw new NoSuchElementException(key);
269: return a;
270: }
271:
272: public void putInt(String key, int value) {
273: mTable.put(key, new Integer(value));
274: }
275:
276: public void putLong(String key, long value) {
277: mTable.put(key, new Long(value));
278: }
279:
280: public void putString(String key, String value) {
281: mTable.put(key, value);
282: }
283:
284: public void putAList(String key, AList value) {
285: mTable.put(key, value);
286: }
287:
288: public void putDate(String key, Date value) {
289: mTable.put(key, value);
290: }
291:
292: public void putInputStream(String key, InputStream value) {
293: mTable.put(key, value);
294: }
295:
296: public void putIntArray(String key, int[] value) {
297: mTable.put(key, value);
298: }
299:
300: public void putLongArray(String key, long[] value) {
301: mTable.put(key, value);
302: }
303:
304: public void putStringArray(String key, String[] value) {
305: mTable.put(key, value);
306: }
307:
308: public void putAListArray(String key, AList[] value) {
309: mTable.put(key, value);
310: }
311:
312: public void putDateArray(String key, Date[] value) {
313: mTable.put(key, new DateArray(value));
314: }
315:
316: public void putInputStreamArray(String key, InputStream[] value) {
317: mTable.put(key, value);
318: }
319:
320: public void putStringArrayArray(String key, String[][] value) {
321: mTable.put(key, value);
322: }
323:
324: /** Deep equals. Arrays need to have same values in same order to
325: * be considered equal.
326: * @param obj
327: * @return True if equals.
328: */
329: public boolean equals(Object obj) {
330: if (!(obj instanceof HashtableAList))
331: return false;
332: HashtableAList o = (HashtableAList) obj;
333: for (Iterator i = o.getKeys(); i.hasNext();) {
334: if (mTable.get(i.next()) == null)
335: return false;
336: }
337: for (Iterator i = getKeys(); i.hasNext();) {
338: Object k = i.next();
339: Object v1 = mTable.get(k);
340: Object v2 = o.mTable.get(k);
341: if (!v1.equals(v2)) {
342: if (v1 instanceof AList[]) {
343: if (!(v2 instanceof AList[]))
344: return false;
345: if (!Arrays.equals((Object[]) v1, (Object[]) v2))
346: return false;
347: } else if (v1 instanceof int[]) {
348: if (!(v2 instanceof int[]))
349: return false;
350: if (!Arrays.equals((int[]) v1, (int[]) v2))
351: return false;
352: } else if (v1 instanceof long[]) {
353: if (!(v2 instanceof long[]))
354: return false;
355: if (!Arrays.equals((long[]) v1, (long[]) v2))
356: return false;
357: } else if (v1 instanceof String[]) {
358: if (!(v2 instanceof String[]))
359: return false;
360: if (!Arrays.equals((String[]) v1, (String[]) v2))
361: return false;
362: } else
363: return false;
364: }
365: }
366: return true;
367: }
368:
369: public int getType(String key) {
370: Object o = mTable.get(key);
371: if (o == null)
372: return T_UNDEFINED;
373: else if (o instanceof AList)
374: return T_ALIST;
375: else if (o instanceof Date)
376: return T_DATE;
377: else if (o instanceof Integer)
378: return T_INT;
379: else if (o instanceof Long)
380: return T_LONG;
381: else if (o instanceof String)
382: return T_STRING;
383: else if (o instanceof InputStream)
384: return T_INPUTSTREAM;
385: else if (o instanceof AList[])
386: return T_ALIST | F_ARRAY;
387: else if (o instanceof DateArray)
388: return T_DATE | F_ARRAY;
389: else if (o instanceof int[])
390: return T_INT | F_ARRAY;
391: else if (o instanceof long[])
392: return T_LONG | F_ARRAY;
393: else if (o instanceof String[])
394: return T_STRING | F_ARRAY;
395: else if (o instanceof InputStream[])
396: return T_INPUTSTREAM | F_ARRAY;
397: else if (o instanceof String[][])
398: return T_STRING | F_ARRAY_ARRAY;
399: else if (o instanceof Object[])
400: return T_OBJECT | F_ARRAY;
401: else if (o instanceof Object)
402: return T_OBJECT;
403: else
404: X.fail("Should not get here " + o);
405: return -1;
406: }
407:
408: /** Useful for creating test-tables for debugging. The object
409: should be one of an {@link AList}, {@link Date}, {@link
410: Integer}, {@link Long}, {@link String}, {@link AList}[],
411: {@link Date}[], <code>int[]</code>, <code>long[]</code>,
412: <code>{@link String}[]</code>, <code>ZE[]</code>
413: or <code>ZE[][]</code>. In the case of <code>ZE[]</code>,
414: the entry is treated as an {@link AList}. Similaryly
415: if the entry is <code>ZE[][]</code> it is treated as
416: {@link AList}[]. */
417: public static class ZE {
418: public final String key;
419: public final Object val;
420:
421: public ZE(String k, Object v) {
422: key = k;
423: val = v;
424: }
425: }
426:
427: public void zInsert(ZE[] entries) {
428: for (int i = 0; i < entries.length; i++) {
429: zInsert(entries[i]);
430: }
431: }
432:
433: public void zInsert(ZE entry) {
434: if (entry.val instanceof Date[]) {
435: mTable.put(entry.key, new DateArray((Date[]) entry.val));
436: } else if (entry.val instanceof ZE[]) {
437: HashtableAList v = new HashtableAList();
438: v.zInsert((ZE[]) entry.val);
439: mTable.put(entry.key, v);
440: } else if (entry.val instanceof ZE[][]) {
441: AList v[] = new AList[((ZE[][]) entry.val).length];
442: for (int j = 0; j < v.length; ++j) {
443: HashtableAList h = new HashtableAList();
444: h.zInsert(((ZE[][]) entry.val)[j]);
445: v[j] = h;
446: }
447: mTable.put(entry.key, v);
448: } else {
449: mTable.put(entry.key, entry.val);
450: }
451: }
452:
453: public void close() {
454: String[] keys = getKeyArray();
455: try {
456: for (int i = 0; i < keys.length; i++) {
457: if (getType(keys[i]) == T_INPUTSTREAM) {
458: getInputStream(keys[i]).close();
459: } else if (getType(keys[i]) == (T_INPUTSTREAM | F_ARRAY)) {
460: InputStream[] ins = getInputStreamArray(keys[i]);
461: for (int j = 0; j < ins.length; j++) {
462: ins[j].close();
463: }
464: } else if (getType(keys[i]) == T_ALIST) {
465: getAList(keys[i]).close();
466: } else if (getType(keys[i]) == (T_ALIST | F_ARRAY)) {
467: AList[] als = getAListArray(keys[i]);
468: for (int j = 0; j < als.length; j++) {
469: als[j].close();
470: }
471: }
472: }
473: } catch (IOException e) {
474: throw X.toRTE(e);
475: }
476: }
477:
478: public AList newAList() {
479: return new HashtableAList();
480: }
481:
482: public String toString() {
483: return mTable.toString();
484: }
485:
486: /**
487: * Enhance given object's default String display for appearing
488: * nested in a pretty AList String.
489: *
490: * @param obj Object to prettify
491: * @return prettified String
492: */
493: protected String prettyString(Object obj) {
494: if (obj instanceof AList)
495: return ((AList) obj).toPrettyString();
496: else if (obj instanceof AList[])
497: return prettyString((AList[]) obj);
498: else
499: return "<" + obj + ">";
500: }
501:
502: /* (non-Javadoc)
503: * @see st.ata.util.AList#toPrettyString()
504: */
505: public String toPrettyString() {
506: StringBuilder builder = new StringBuilder();
507: builder.append("{ ");
508: boolean needsComma = false;
509: for (String key : getKeyArray()) {
510: if (needsComma) {
511: builder.append(", ");
512: }
513: builder.append(key);
514: builder.append(": ");
515: builder.append(prettyString(mTable.get(key)));
516: needsComma = true;
517: }
518: builder.append(" }");
519: return builder.toString();
520: }
521:
522: /**
523: * Provide a slightly-improved String of AList[]
524: *
525: * @param alists
526: * @return prettified (in square brackets) of AList[]
527: */
528: protected String prettyString(AList[] alists) {
529: StringBuilder builder = new StringBuilder();
530: builder.append("[ ");
531: boolean needsComma = false;
532: for (AList alist : alists) {
533: if (alist == null)
534: continue;
535: if (needsComma) {
536: builder.append(", ");
537: }
538: builder.append(alist.toPrettyString());
539: needsComma = true;
540: }
541: builder.append(" ]");
542: return builder.toString();
543: }
544: }
|