View Javadoc

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  // Tested by TestHashtableAList
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);  // clobber
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             // TODO: consider shallow or deep copy in some cases?
153             // perhaps controlled by an additional parameter?
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     /* (non-Javadoc)
460      * @see st.ata.util.AList#toPrettyString()
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 }