try to fix some minor dropdown bugs
[potlatch2.git] / org / idmedia / as3commons / util / AbstractMap.as
1 /*
2  * Copyright the original author or authors.
3  * 
4  * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * 
8  *      http://www.mozilla.org/MPL/MPL-1.1.html
9  * 
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.idmedia.as3commons.util {
17   import org.idmedia.as3commons.lang.UnsupportedOperationException;
18   
19   /**
20    * This class provides a skeletal implementation of the <tt>Map</tt>
21    * interface, to minimize the effort required to implement this interface. <p>
22    *
23    * To implement an unmodifiable map, the programmer needs only to extend this
24    * class and provide an implementation for the <tt>entrySet</tt> method, which
25    * returns a set-view of the map's mappings.  Typically, the returned set
26    * will, in turn, be implemented atop <tt>AbstractSet</tt>.  This set should
27    * not support the <tt>add</tt> or <tt>remove</tt> methods, and its iterator
28    * should not support the <tt>remove</tt> method.<p>
29    *
30    * To implement a modifiable map, the programmer must additionally override
31    * this class's <tt>put</tt> method (which otherwise throws an
32    * <tt>UnsupportedOperationException</tt>), and the iterator returned by
33    * <tt>entrySet().iterator()</tt> must additionally implement its
34    * <tt>remove</tt> method.<p>
35    *
36    * The programmer should generally provide a void (no argument) and map
37    * constructor, as per the recommendation in the <tt>Map</tt> interface
38    * specification.<p>
39    *
40    * The documentation for each non-abstract methods in this class describes its
41    * implementation in detail.  Each of these methods may be overridden if the
42    * map being implemented admits a more efficient implementation.<p>
43    * 
44    * @author sleistner
45    */
46   public class AbstractMap implements Map {
47     
48     /**
49      * Associates the specified value with the specified key in this map
50      * (optional operation).  If the map previously contained a mapping for
51      * this key, the old value is replaced.<p>
52      *
53      * This implementation always throws an
54      * <tt>UnsupportedOperationException</tt>.
55      *
56      * @param key key with which the specified value is to be associated.
57      * @param value value to be associated with the specified key.
58      * 
59      * @return previous value associated with specified key, or <tt>null</tt>
60      *         if there was no mapping for key.  (A <tt>null</tt> return can
61      *         also indicate that the map previously associated <tt>null</tt>
62      *         with the specified key, if the implementation supports
63      *         <tt>null</tt> values.)
64      * 
65      * @throws UnsupportedOperationException if the <tt>put</tt> operation is
66      *            not supported by this map.
67      * 
68      * @throws IllegalArgumentException if some aspect of this key or value *
69      *            prevents it from being stored in this map.
70      * 
71      * @throws NullPointerException if this map does not permit <tt>null</tt>
72      *            keys or values, and the specified key or value is
73      *            <tt>null</tt>.
74      */
75     public function put(key:*, value:*):* {
76       throw new UnsupportedOperationException();
77     }
78     
79     /**
80      * Returns the value to which this map maps the specified key.  Returns
81      * <tt>null</tt> if the map contains no mapping for this key.  A return
82      * value of <tt>null</tt> does not <i>necessarily</i> indicate that the
83      * map contains no mapping for the key; it's also possible that the map
84      * explicitly maps the key to <tt>null</tt>.  The containsKey operation
85      * may be used to distinguish these two cases. <p>
86      *
87      * This implementation iterates over <tt>entrySet()</tt> searching for an
88      * entry with the specified key.  If such an entry is found, the entry's
89      * value is returned.  If the iteration terminates without finding such an
90      * entry, <tt>null</tt> is returned.  Note that this implementation
91      * requires linear time in the size of the map; many implementations will
92      * override this method.
93      *
94      * @param key key whose associated value is to be returned.
95      * @return the value to which this map maps the specified key.
96      * 
97      * @throws NullPointerException if the key is <tt>null</tt> and this map
98      *            does not permit <tt>null</tt> keys.
99      * 
100      * @see #containsKey(*)
101      */
102     public function get(key:*):* {
103       
104       if(key === undefined)
105         return null;
106       
107       var k:* = key; //|| null;
108       var i:Iterator = entrySet().iterator();
109       while(i.hasNext()) {
110         var e:Entry = i.next() as Entry;
111         if(k === e.getKey()) {
112           return e.getValue();
113         }
114       }
115       return null;
116     }
117     
118     /**
119      * Returns <tt>true</tt> if this map contains a mapping for the specified
120      * key. <p>
121      *
122      * This implementation iterates over <tt>entrySet()</tt> searching for an
123      * entry with the specified key.  If such an entry is found, <tt>true</tt>
124      * is returned.  If the iteration terminates without finding such an
125      * entry, <tt>false</tt> is returned.  Note that this implementation
126      * requires linear time in the size of the map; many implementations will
127      * override this method.
128      *
129      * @param key key whose presence in this map is to be tested.
130      * @return <tt>true</tt> if this map contains a mapping for the specified
131      *            key.
132      * 
133      * @throws NullPointerException if the key is <tt>null</tt> and this map
134      *            does not permit <tt>null</tt> keys.
135      */
136     public function containsKey(key:*):Boolean {
137       var k:* = key || null;
138       var i:Iterator = entrySet().iterator();
139       while(i.hasNext()) {
140         var e:Entry = i.next() as Entry;
141         if(k === e.getKey()) {
142           return true;
143         }
144       }
145       return false;
146     }
147     
148     /**
149      * Returns <tt>true</tt> if this map maps one or more keys to this value.
150      * More formally, returns <tt>true</tt> if and only if this map contains
151      * at least one mapping to a value <tt>v</tt> such that <tt>(value==null ?
152      * v==null : value.equals(v))</tt>.  This operation will probably require
153      * time linear in the map size for most implementations of map.<p>
154      *
155      * This implementation iterates over entrySet() searching for an entry
156      * with the specified value.  If such an entry is found, <tt>true</tt> is
157      * returned.  If the iteration terminates without finding such an entry,
158      * <tt>false</tt> is returned.  Note that this implementation requires
159      * linear time in the size of the map.
160      *
161      * @param value value whose presence in this map is to be tested.
162      * 
163      * @return <tt>true</tt> if this map maps one or more keys to this value.
164      */
165     public function containsValue(value:*):Boolean {
166       var v:* = value || null;
167       var i:Iterator = entrySet().iterator();
168       while(i.hasNext()) {
169         var e:Entry = i.next() as Entry;
170         if(v === e.getValue()) {
171           return true;
172         }
173       }
174       return false;
175     }
176     
177     /**
178      * Removes the mapping for this key from this map if present (optional
179      * operation). <p>
180      *
181      * This implementation iterates over <tt>entrySet()</tt> searching for an
182      * entry with the specified key.  If such an entry is found, its value is
183      * obtained with its <tt>getValue</tt> operation, the entry is removed
184      * from the Collection (and the backing map) with the iterator's
185      * <tt>remove</tt> operation, and the saved value is returned.  If the
186      * iteration terminates without finding such an entry, <tt>null</tt> is
187      * returned.  Note that this implementation requires linear time in the
188      * size of the map; many implementations will override this method.<p>
189      *
190      * Note that this implementation throws an
191      * <tt>UnsupportedOperationException</tt> if the <tt>entrySet</tt> iterator
192      * does not support the <tt>remove</tt> method and this map contains a
193      * mapping for the specified key.
194      *
195      * @param key key whose mapping is to be removed from the map.
196      * @return previous value associated with specified key, or <tt>null</tt>
197      *         if there was no entry for key.  (A <tt>null</tt> return can
198      *         also indicate that the map previously associated <tt>null</tt>
199      *         with the specified key, if the implementation supports
200      *         <tt>null</tt> values.)
201      * @throws UnsupportedOperationException if the <tt>remove</tt> operation
202      *            is not supported by this map.
203      */
204     public function remove(key:*):* {
205       var k:* = key || null;
206       var i:Iterator = entrySet().iterator();
207       var correctEntry:Entry = null;
208
209       while(correctEntry == null && i.hasNext()) {
210         var e:Entry = Entry(i.next());
211         if(key === e.getKey()) {
212           correctEntry = e;
213         }
214       }
215
216       var oldValue:* = null;
217       if(correctEntry != null) {
218         oldValue = correctEntry.getValue();
219         i.remove();
220       }
221
222       return oldValue;
223     }
224     
225     /**
226      * Removes all mappings from this map (optional operation). <p>
227      *
228      * This implementation calls <tt>entrySet().clear()</tt>.
229      *
230      * Note that this implementation throws an
231      * <tt>UnsupportedOperationException</tt> if the <tt>entrySet</tt>
232      * does not support the <tt>clear</tt> operation.
233      *
234      * @throws    UnsupportedOperationException clear is not supported
235      *            by this map.
236      */
237     public function clear():void {
238       entrySet().clear();
239     }
240     
241     /**
242      * Returns the number of key-value mappings in this map.  If the map
243      * contains more than <tt>Number.MAX_VALUE</tt> elements, returns
244      * <tt>Number.MAX_VALUE</tt>.<p>
245      *
246      * This implementation returns <tt>entrySet().size()</tt>.
247      *
248      * @return the number of key-value mappings in this map.
249      */
250     public function size():int {
251       return entrySet().size();
252     }
253     
254     private var v:Collection = null;
255     
256     /**
257      * Returns a collection view of the values contained in this map.  The
258      * collection is backed by the map, so changes to the map are reflected in
259      * the collection, and vice-versa.  (If the map is modified while an
260      * iteration over the collection is in progress, the results of the
261      * iteration are undefined.)  The collection supports element removal,
262      * which removes the corresponding entry from the map, via the
263      * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
264      * <tt>removeAll</tt>, <tt>retainAll</tt> and <tt>clear</tt> operations.
265      * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.<p>
266      *
267      * This implementation returns a collection that subclasses abstract
268      * collection.  The subclass's iterator method returns a "wrapper object"
269      * over this map's <tt>entrySet()</tt> iterator.  The size method
270      * delegates to this map's size method and the contains method delegates
271      * to this map's containsValue method.<p>
272      *
273      * The collection is created the first time this method is called, and
274      * returned in response to all subsequent calls.  No synchronization is
275      * performed, so there is a slight chance that multiple calls to this
276      * method will not all return the same Collection.
277      *
278      * @return a collection view of the values contained in this map.
279      */    
280     public function values():Collection {
281       if(v == null) {
282         v = new CollectionImpl(this);
283       }
284       return v;
285     }
286     
287     private var k:Set = null;
288     
289     /**
290      * Returns a Set view of the keys contained in this map.  The Set is
291      * backed by the map, so changes to the map are reflected in the Set,
292      * and vice-versa.  (If the map is modified while an iteration over
293      * the Set is in progress, the results of the iteration are undefined.)
294      * The Set supports element removal, which removes the corresponding entry
295      * from the map, via the Iterator.remove, Set.remove,  removeAll
296      * retainAll, and clear operations.  It does not support the add or
297      * addAll operations.<p>
298      *
299      * This implementation returns a Set that subclasses
300      * AbstractSet.  The subclass's iterator method returns a "wrapper
301      * object" over this map's entrySet() iterator.  The size method delegates
302      * to this map's size method and the contains method delegates to this
303      * map's containsKey method.<p>
304      *
305      * The Set is created the first time this method is called,
306      * and returned in response to all subsequent calls.  No synchronization
307      * is performed, so there is a slight chance that multiple calls to this
308      * method will not all return the same Set.
309      *
310      * @return a Set view of the keys contained in this map.
311      */
312     public function keySet():Set {
313       if(k == null ) {
314         k = new AbstractEntrySet(this);
315       }
316       return k;
317     }
318     
319     /**
320      * Returns a set view of the mappings contained in this map.  Each element
321      * in this set is a Map.Entry.  The set is backed by the map, so changes
322      * to the map are reflected in the set, and vice-versa.  (If the map is
323      * modified while an iteration over the set is in progress, the results of
324      * the iteration are undefined.)  The set supports element removal, which
325      * removes the corresponding entry from the map, via the
326      * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>, <tt>removeAll</tt>,
327      * <tt>retainAll</tt> and <tt>clear</tt> operations.  It does not support
328      * the <tt>add</tt> or <tt>addAll</tt> operations.
329      *
330      * @return a set view of the mappings contained in this map.
331      */
332     public function entrySet():Set {
333       throw new UnsupportedOperationException();
334     }
335     
336     /**
337      * Returns <tt>true</tt> if this map contains no key-value mappings. <p>
338      *
339      * This implementation returns <tt>size() == 0</tt>.
340      *
341      * @return <tt>true</tt> if this map contains no key-value mappings.
342      */
343     public function isEmpty():Boolean {
344       return size() == 0;
345     }
346     
347     /**
348      * Copies all of the mappings from the specified map to this map
349      * (optional operation).  These mappings will replace any mappings that
350      * this map had for any of the keys currently in the specified map.<p>
351      *
352      * This implementation iterates over the specified map's
353      * <tt>entrySet()</tt> collection, and calls this map's <tt>put</tt>
354      * operation once for each entry returned by the iteration.<p>
355      *
356      * Note that this implementation throws an
357      * <tt>UnsupportedOperationException</tt> if this map does not support
358      * the <tt>put</tt> operation and the specified map is nonempty.
359      *
360      * @param t mappings to be stored in this map.
361      * 
362      * @throws UnsupportedOperationException if the <tt>putAll</tt> operation
363      *            is not supported by this map.
364      * 
365      * @throws IllegalArgumentException if some aspect of a key or value in
366      *            the specified map prevents it from being stored in this map.
367      * @throws NullPointerException if the specified map is <tt>null</tt>, or if
368      *         this map does not permit <tt>null</tt> keys or values, and the
369      *         specified map contains <tt>null</tt> keys or values.
370      */
371     public function putAll(map:Map):void {
372       var i:Iterator = map.entrySet().iterator();
373       while(i.hasNext()) {
374         var e:Entry = i.next() as Entry;
375         put(e.getKey(), e.getValue());
376       }
377     }
378     
379     public function equals(object:*):Boolean {
380       return object === this;
381     }
382   }     
383 }
384
385 import org.idmedia.as3commons.util.AbstractCollection;
386 import org.idmedia.as3commons.util.AbstractSet;
387 import org.idmedia.as3commons.util.Entry;
388 import org.idmedia.as3commons.util.Iterator;
389 import org.idmedia.as3commons.util.Map;
390
391 internal class AbstractEntrySet extends AbstractSet {
392   
393   private var m:Map = null;
394   
395   function AbstractEntrySet(m:Map) {
396     this.m = m;
397   }
398   
399   override public function iterator():Iterator {
400     return new KeyIterator(m.entrySet().iterator());
401   }
402   
403   override public function size():int {
404     return m.size();
405   }
406   
407   override public function contains(value:*):Boolean {
408     return m.containsKey(value);        
409   }
410 }
411
412 internal final class CollectionImpl extends AbstractCollection {
413   
414   private var map:Map;
415   
416   function CollectionImpl(map:Map) {
417     this.map = map;
418   }
419   
420   override public function iterator():Iterator {
421     return new ValueIterator(map.entrySet().iterator());
422   }
423   
424   override public function size():int { 
425     return map.size();
426   }
427 }
428
429 internal class KeyIterator implements Iterator {
430   
431   protected var iter:Iterator;
432   
433   function KeyIterator(iter:Iterator) {
434     this.iter = iter;
435   }
436   
437   public function hasNext():Boolean {
438     return iter.hasNext();
439   }
440   
441   public function next():* {
442     return Entry(iter.next()).getKey();
443   }
444   
445   public function remove():void {
446     iter.remove();
447   }
448 }
449
450 internal final class ValueIterator extends KeyIterator {
451   
452   function ValueIterator(iter:Iterator) {
453     super(iter);
454   }
455   
456   override public function next():* {
457     return Entry(iter.next()).getValue();
458   }
459 }