SimpleMapImpl.java
001 /*
002  *  SimpleMapImpl.java
003  *
004  *  Copyright (c) 2001, The University of Sheffield.
005  *
006  *  This file is part of GATE (see http://gate.ac.uk/), and is free
007  *  software, licenced under the GNU Library General Public License,
008  *  Version 2, June 1991 (in the distribution as file licence.html,
009  *  and also available at http://gate.ac.uk/gate/licence.html).
010  *
011  *  D.Ognyanoff, 5/Nov/2001
012  *
013  *  $Id: SimpleMapImpl.java 13144 2010-10-14 14:32:07Z valyt $
014  */
015 
016 package gate.util;
017 
018 import java.io.IOException;
019 import java.io.ObjectInputStream;
020 import java.io.Serializable;
021 import java.util.*;
022 import java.util.concurrent.ConcurrentHashMap;
023 
024 /**
025  * Implements Map interface in using less memory. Very simple but usefull
026  * for small number of items on it.
027  */
028 
029 class SimpleMapImpl implements Map<Object, Object>,
030       java.lang.Cloneable, java.io.Serializable {
031   
032   /**
033    * Special marker class used to represent null in the keys array.
034    */
035   private static class NullKey implements Serializable {
036     private static final long serialVersionUID = 6391916290867211345L;
037 
038     private NullKey() {
039     }
040   }
041 
042   /**
043    * The capacity of the map
044    */
045   int capacity = 3;
046 
047   /**
048    * The current number of elements of the map
049    */
050   int count = 0;
051 
052   /**
053    * Array keeping the keys of the entries in the map. It is "synchrnized"
054    * with the values array - the Nth position in both arrays correspond
055    * to one and the same entry
056    */
057   Object theKeys[];
058 
059   /**
060    * Array keeping the values of the entries in the map. It is "synchrnized"
061    * with the keys array - the Nth position in both arrays correspond
062    * to one and the same entry
063    */
064   Object theValues[];
065   
066   /** Freeze the serialization UID. */
067   static final long serialVersionUID = -6747241616127229116L;
068 
069   /** the Object instance that will represent the NULL keys in the map */
070   transient static Object nullKey = new NullKey();
071 
072   /** the static 'all keys' collection */
073   transient public static ConcurrentHashMap theKeysHere;
074 
075   /** additional static members initialization */
076   static {
077     theKeysHere = new ConcurrentHashMap();
078     theKeysHere.put(nullKey, nullKey);
079   // static code
080 
081   /**
082    * Constructor
083    */
084   public SimpleMapImpl() {
085     theKeys = new Object[capacity];
086     theValues = new Object[capacity];
087   // SimpleMapImpl()
088 
089   /**
090    * return the number of elements in the map
091    */
092   public int size() {
093     return count;
094   // size()
095 
096   /**
097    * return true if there are no elements in the map
098    */
099   public boolean isEmpty() {
100     return (count == 0);
101   // isEmpty()
102 
103   /**
104    * Not supported. This method is here only to conform the Map interface
105    */
106   public Collection values() {
107     throw new UnsupportedOperationException(
108       "SimpleMapImpl.values() not implemented!");
109   // values()
110 
111   /**
112    * return the set of the keys in the map. The changes in the set DO NOT
113    * affect the map.
114    */
115   public Set keySet()
116   {
117     HashSet s = new HashSet(size());
118     Object k;
119     for (int i = 0; i < count; i++) {
120       k = theKeys[i];
121       if (k == nullKey)
122            s.add(null);
123         else
124            s.add(k);
125     //for
126     return s;
127   // keySet()
128 
129   /**
130    * clear the map
131    */
132   public void clear()
133   {
134     for (int i = 0; i < count; i++) {
135       theKeys[inull;
136       theValues[inull;
137     // for
138     count = 0;
139   // clear
140 
141   /**
142    * return true if the key is in the map
143    */
144   public boolean containsKey(Object key) {
145     return (getPostionByKey(key!= -1);
146   }// containsKey
147 
148   /**
149    * return true if the map contains that value
150    */
151   public boolean containsValue(Object value) {
152     return (getPostionByValue(value!= -1);
153   }// containsValue
154 
155   /**
156    * return the value associated with the key. If the key is
157    * not in the map returns null.
158    */
159   public Object get(Object key) {
160     int pos = getPostionByKey(key);
161     return (pos == -1null : theValues[pos];
162   // get
163 
164   /**
165    * put a value in the map using the given key. If the key exist in the map
166    * the value is replaced and the old one is returned.
167    */
168   public Object put(Object key, Object value) {
169     Object gKey;
170     if (key == null) {
171       key = nullKey;
172       gKey = nullKey;
173     else
174       gKey = theKeysHere.putIfAbsent(key, key);
175     // if the key is already in the 'all keys' map - try to find it in that instance
176     // comparing by reference
177     if (gKey != null) {
178       for (int i = 0; i < count; i++) {
179         if (gKey == theKeys[i]) {
180           // we found the reference - return the value
181           Object oldVal = theValues[i];
182           theValues[i= value;
183           return oldVal;
184         }
185       // for
186     else {// if(gKey != null)
187       // no, the key is not in the 'all keys' map - put it there
188       gKey = key;
189     }
190     // enlarge the containers if necessary
191     if (count == capacity)
192       increaseCapacity();
193 
194     // put the key and value to the map
195     theKeys[count= gKey;
196     theValues[count= value;
197     count++;
198     return null;
199   // put
200 
201   /**
202    * remove value from the map using it's key.
203    */
204   public Object remove(Object key) {
205     int pos = getPostionByKey(key);
206     if (pos == -1)
207         return null;
208 
209     // save the value to return it at the end
210     Object oldVal = theValues[pos];
211     count--;
212     // move the last element key and value removing the element
213     if (count != 0) {
214         theKeys[pos= theKeys[count];
215         theValues[pos= theValues[count];
216     }
217     // clear the last position
218     theKeys[countnull;
219     theValues[countnull;
220 
221     // return the value
222     return oldVal;
223   // remove
224 
225   /**
226    * put all the elements from a map
227    */
228   public void putAll(Map t)
229   {
230     if (t == null) {
231       throw new UnsupportedOperationException(
232       "SimpleMapImpl.putAll argument is null");
233     // if (t == null)
234 
235     if (instanceof SimpleMapImpl) {
236       SimpleMapImpl sfm = (SimpleMapImpl)t;
237       Object key;
238       for (int i = 0; i < sfm.count; i++) {
239         key = sfm.theKeys[i];
240         put(key, sfm.theValues[i]);
241       //for
242     else // if (t instanceof SimpleMapImpl)
243       Iterator entries = t.entrySet().iterator();
244       Map.Entry e;
245       while (entries.hasNext()) {
246         e = (Map.Entry)entries.next();
247         put(e.getKey(), e.getValue());
248       // while
249     // if(t instanceof SimpleMapImpl)
250   // putAll
251 
252   /**
253    * return positive value as index of the key in the map.
254    * Negative value means that the key is not present in the map
255    */
256   private int getPostionByKey(Object key) {
257     if (key == null)
258       key = nullKey;
259     // check the 'all keys' map for the very first key occurence
260     key = theKeysHere.get(key);
261     if (key == null)
262       return -1;
263 
264     for (int i = 0; i < count; i++) {
265       if (key == theKeys[i])
266         return i;
267     // for
268     return -1;
269   // getPostionByKey
270 
271   /**
272    * return the index of the key in the map comparing them by reference only.
273    * This method is used in subsume check to speed it up.
274    */
275   protected int getSubsumeKey(Object key) {
276     for (int i = 0; i < count; i++) {
277       if (key == theKeys[i])
278         return i;
279     // for
280     return -1;
281   // getPostionByKey
282 
283   /**
284    * return positive value as index of the value in the map.
285    */
286   private int getPostionByValue(Object value) {
287     Object av;
288     for (int i = 0; i < count; i++) {
289       av = theValues[i];
290       if (value == null) {
291         if (av == null)
292           return i;
293       else {//if (value == null)
294         if (value.equals(av))
295           return i;
296       //if (value == null)
297     // for
298 
299     return -1;
300   // getPostionByValue
301 
302   // Modification Operations
303   private void increaseCapacity() {
304     int oldCapacity = capacity;
305     capacity *= 2;
306     Object oldKeys[] = theKeys;
307     theKeys = new Object[capacity];
308 
309     Object oldValues[] = theValues;
310     theValues = new Object[capacity];
311 
312     System.arraycopy(oldKeys, 0, theKeys, 0, oldCapacity);
313     System.arraycopy(oldValues, 0, theValues, 0, oldCapacity);
314   // increaseCapacity
315 
316   /**
317    * Auxiliary classes needed for the support of entrySet() method
318    */
319   private static class Entry implements Map.Entry {
320     int hash;
321     Object key;
322     Object value;
323 
324     Entry(int hash, Object key, Object value) {
325       this.hash = hash;
326       this.key = key;
327       this.value = value;
328     }
329 
330     protected Object clone() {
331       return new Entry(hash, key, value);
332     }
333 
334     public Object getKey() {
335       return key;
336     }
337 
338     public Object getValue() {
339       return value;
340     }
341 
342     public Object setValue(Object value) {
343       Object oldValue = this.value;
344       this.value = value;
345       return oldValue;
346     }
347 
348     public boolean equals(Object o) {
349       if (!(instanceof Map.Entry))
350         return false;
351       Map.Entry e = (Map.Entry)o;
352 
353       return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
354         (value==null ? e.getValue()==null : value.equals(e.getValue()));
355     }
356 
357     public int hashCode() {
358       return hash ^ (key==null : key.hashCode());
359     }
360 
361     public String toString() {
362       return key+"="+value;
363     }
364   // Entry
365 
366 
367   public Set entrySet() {
368     HashSet s = new HashSet(size());
369     Object v, k;
370     for (int i = 0; i < count; i++) {
371       k = theKeys[i];
372       s.add(new Entry(k.hashCode()((k==nullKey)?null:k), theValues[i]));
373     //for
374     return s;
375   // entrySet
376 
377   // Comparison and hashing
378   public boolean equals(Object o) {
379     if (!(instanceof Map)) {
380       return false;
381     }
382 
383     Map m = (Map)o;
384     if (m.size() != count) {
385       return false;
386     }
387 
388     Object v, k;
389     for (int i = 0; i < count; i++) {
390       k = theKeys[i];
391       v = m.get(k);
392       if (v==null) {
393         if (theValues[i]!=null)
394           return false;
395       }
396       else if (!v.equals(theValues[i])){
397         return false;
398       }
399     // for
400 
401     return true;
402   // equals
403 
404   /**
405    * return the hashCode for the map
406    */
407   public int hashCode() {
408     int h = 0;
409     Iterator i = entrySet().iterator();
410     while (i.hasNext())
411       h += i.next().hashCode();
412     return h;
413   // hashCode
414 
415   /**
416    * Create a copy of the map including the data.
417    */
418   public Object clone() {
419     SimpleMapImpl newMap;
420     try {
421       newMap = (SimpleMapImpl)super.clone();
422     catch (CloneNotSupportedException e) {
423       throw(new InternalError(e.toString()));
424     }
425 
426     newMap.count = count;
427     newMap.theKeys = new Object[capacity];
428     System.arraycopy(theKeys, 0, newMap.theKeys, 0, capacity);
429 
430     newMap.theValues = new Object[capacity];
431     System.arraycopy(theValues, 0, newMap.theValues, 0, capacity);
432 
433     return newMap;
434   // clone
435 
436   public String toString() {
437     int max = size() 1;
438     StringBuffer buf = new StringBuffer();
439     Iterator i = entrySet().iterator();
440 
441     buf.append("{");
442     for (int j = 0; j <= max; j++) {
443       Entry e = (Entry) (i.next());
444       buf.append(e.getKey() "=" + e.getValue());
445       if (j < max)
446         buf.append(", ");
447     }
448     buf.append("}");
449     return buf.toString();
450   // toString
451 
452   /**
453    * readObject - calls the default readObject() and then initialises the
454    * transient data
455    *
456    @serialData Read serializable fields. No optional data read.
457    */
458   private void readObject(ObjectInputStream s)
459       throws IOException, ClassNotFoundException {
460     s.defaultReadObject();
461     if (theKeysHere == null) {
462       synchronized(SimpleMapImpl.class) {
463         theKeysHere = new ConcurrentHashMap();
464         theKeysHere.put(nullKey, nullKey);
465       }
466     }
467     for (int i = 0; i < theKeys.length; i++) {
468       if(theKeys[iinstanceof NullKey) {
469         theKeys[i= nullKey;
470       }
471       else if(theKeys[i!= null) {
472         // check if the key is in the 'all keys' map, adding it if not
473         Object o = theKeysHere.putIfAbsent(theKeys[i], theKeys[i]);
474         if (o != null// yes - so reuse the reference
475           theKeys[i= o;
476       }
477     }//for
478   }//readObject
479 //SimpleMapImpl