KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > collections > map > ListOrderedMap


1 /*
2  * Copyright 2003-2004 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
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.apache.commons.collections.map;
17
18 import java.io.IOException JavaDoc;
19 import java.io.ObjectInputStream JavaDoc;
20 import java.io.ObjectOutputStream JavaDoc;
21 import java.io.Serializable JavaDoc;
22 import java.util.AbstractCollection JavaDoc;
23 import java.util.AbstractSet JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.Collection JavaDoc;
26 import java.util.HashMap JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.List JavaDoc;
29 import java.util.ListIterator JavaDoc;
30 import java.util.Map JavaDoc;
31 import java.util.NoSuchElementException JavaDoc;
32 import java.util.Set JavaDoc;
33
34 import org.apache.commons.collections.MapIterator;
35 import org.apache.commons.collections.OrderedMap;
36 import org.apache.commons.collections.OrderedMapIterator;
37 import org.apache.commons.collections.ResettableIterator;
38 import org.apache.commons.collections.iterators.AbstractIteratorDecorator;
39 import org.apache.commons.collections.keyvalue.AbstractMapEntry;
40 import org.apache.commons.collections.list.UnmodifiableList;
41
42 /**
43  * Decorates a <code>Map</code> to ensure that the order of addition is retained
44  * using a <code>List</code> to maintain order.
45  * <p>
46  * The order will be used via the iterators and toArray methods on the views.
47  * The order is also returned by the <code>MapIterator</code>.
48  * The <code>orderedMapIterator()</code> method accesses an iterator that can
49  * iterate both forwards and backwards through the map.
50  * In addition, non-interface methods are provided to access the map by index.
51  * <p>
52  * If an object is added to the Map for a second time, it will remain in the
53  * original position in the iteration.
54  * <p>
55  * This class is Serializable from Commons Collections 3.1.
56  *
57  * @since Commons Collections 3.0
58  * @version $Revision: 1.16 $ $Date: 2004/06/07 21:51:39 $
59  *
60  * @author Henri Yandell
61  * @author Stephen Colebourne
62  */

63 public class ListOrderedMap
64         extends AbstractMapDecorator
65         implements OrderedMap, Serializable JavaDoc {
66
67     /** Serialization version */
68     private static final long serialVersionUID = 2728177751851003750L;
69
70     /** Internal list to hold the sequence of objects */
71     protected final List JavaDoc insertOrder = new ArrayList JavaDoc();
72
73     /**
74      * Factory method to create an ordered map.
75      * <p>
76      * An <code>ArrayList</code> is used to retain order.
77      *
78      * @param map the map to decorate, must not be null
79      * @throws IllegalArgumentException if map is null
80      */

81     public static OrderedMap decorate(Map JavaDoc map) {
82         return new ListOrderedMap(map);
83     }
84
85     //-----------------------------------------------------------------------
86
/**
87      * Constructs a new empty <code>ListOrderedMap</code> that decorates
88      * a <code>HashMap</code>.
89      *
90      * @since Commons Collections 3.1
91      */

92     public ListOrderedMap() {
93         this(new HashMap JavaDoc());
94     }
95
96     /**
97      * Constructor that wraps (not copies).
98      *
99      * @param map the map to decorate, must not be null
100      * @throws IllegalArgumentException if map is null
101      */

102     protected ListOrderedMap(Map JavaDoc map) {
103         super(map);
104         insertOrder.addAll(getMap().keySet());
105     }
106
107     //-----------------------------------------------------------------------
108
/**
109      * Write the map out using a custom routine.
110      *
111      * @param out the output stream
112      * @throws IOException
113      * @since Commons Collections 3.1
114      */

115     private void writeObject(ObjectOutputStream JavaDoc out) throws IOException JavaDoc {
116         out.defaultWriteObject();
117         out.writeObject(map);
118     }
119
120     /**
121      * Read the map in using a custom routine.
122      *
123      * @param in the input stream
124      * @throws IOException
125      * @throws ClassNotFoundException
126      * @since Commons Collections 3.1
127      */

128     private void readObject(ObjectInputStream JavaDoc in) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
129         in.defaultReadObject();
130         map = (Map JavaDoc) in.readObject();
131     }
132
133     // Implement OrderedMap
134
//-----------------------------------------------------------------------
135
public MapIterator mapIterator() {
136         return orderedMapIterator();
137     }
138
139     public OrderedMapIterator orderedMapIterator() {
140         return new ListOrderedMapIterator(this);
141     }
142
143     /**
144      * Gets the first key in this map by insert order.
145      *
146      * @return the first key currently in this map
147      * @throws NoSuchElementException if this map is empty
148      */

149     public Object JavaDoc firstKey() {
150         if (size() == 0) {
151             throw new NoSuchElementException JavaDoc("Map is empty");
152         }
153         return insertOrder.get(0);
154     }
155
156     /**
157      * Gets the last key in this map by insert order.
158      *
159      * @return the last key currently in this map
160      * @throws NoSuchElementException if this map is empty
161      */

162     public Object JavaDoc lastKey() {
163         if (size() == 0) {
164             throw new NoSuchElementException JavaDoc("Map is empty");
165         }
166         return insertOrder.get(size() - 1);
167     }
168     
169     /**
170      * Gets the next key to the one specified using insert order.
171      * This method performs a list search to find the key and is O(n).
172      *
173      * @param key the key to find previous for
174      * @return the next key, null if no match or at start
175      */

176     public Object JavaDoc nextKey(Object JavaDoc key) {
177         int index = insertOrder.indexOf(key);
178         if (index >= 0 && index < size() - 1) {
179             return insertOrder.get(index + 1);
180         }
181         return null;
182     }
183
184     /**
185      * Gets the previous key to the one specified using insert order.
186      * This method performs a list search to find the key and is O(n).
187      *
188      * @param key the key to find previous for
189      * @return the previous key, null if no match or at start
190      */

191     public Object JavaDoc previousKey(Object JavaDoc key) {
192         int index = insertOrder.indexOf(key);
193         if (index > 0) {
194             return insertOrder.get(index - 1);
195         }
196         return null;
197     }
198
199     //-----------------------------------------------------------------------
200
public Object JavaDoc put(Object JavaDoc key, Object JavaDoc value) {
201         if (getMap().containsKey(key)) {
202             // re-adding doesn't change order
203
return getMap().put(key, value);
204         } else {
205             // first add, so add to both map and list
206
Object JavaDoc result = getMap().put(key, value);
207             insertOrder.add(key);
208             return result;
209         }
210     }
211
212     public void putAll(Map JavaDoc map) {
213         for (Iterator JavaDoc it = map.entrySet().iterator(); it.hasNext();) {
214             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
215             put(entry.getKey(), entry.getValue());
216         }
217     }
218
219     public Object JavaDoc remove(Object JavaDoc key) {
220         Object JavaDoc result = getMap().remove(key);
221         insertOrder.remove(key);
222         return result;
223     }
224
225     public void clear() {
226         getMap().clear();
227         insertOrder.clear();
228     }
229
230     //-----------------------------------------------------------------------
231
public Set JavaDoc keySet() {
232         return new KeySetView(this);
233     }
234
235     public Collection JavaDoc values() {
236         return new ValuesView(this);
237     }
238
239     public Set JavaDoc entrySet() {
240         return new EntrySetView(this, this.insertOrder);
241     }
242     
243     //-----------------------------------------------------------------------
244
/**
245      * Returns the Map as a string.
246      *
247      * @return the Map as a String
248      */

249     public String JavaDoc toString() {
250         if (isEmpty()) {
251             return "{}";
252         }
253         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
254         buf.append('{');
255         boolean first = true;
256         Iterator JavaDoc it = entrySet().iterator();
257         while (it.hasNext()) {
258             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
259             Object JavaDoc key = entry.getKey();
260             Object JavaDoc value = entry.getValue();
261             if (first) {
262                 first = false;
263             } else {
264                 buf.append(", ");
265             }
266             buf.append(key == this ? "(this Map)" : key);
267             buf.append('=');
268             buf.append(value == this ? "(this Map)" : value);
269         }
270         buf.append('}');
271         return buf.toString();
272     }
273
274     //-----------------------------------------------------------------------
275
/**
276      * Gets the key at the specified index.
277      *
278      * @param index the index to retrieve
279      * @return the key at the specified index
280      * @throws IndexOutOfBoundsException if the index is invalid
281      */

282     public Object JavaDoc get(int index) {
283         return insertOrder.get(index);
284     }
285     
286     /**
287      * Gets the value at the specified index.
288      *
289      * @param index the index to retrieve
290      * @return the key at the specified index
291      * @throws IndexOutOfBoundsException if the index is invalid
292      */

293     public Object JavaDoc getValue(int index) {
294         return get(insertOrder.get(index));
295     }
296     
297     /**
298      * Gets the index of the specified key.
299      *
300      * @param key the key to find the index of
301      * @return the index, or -1 if not found
302      */

303     public int indexOf(Object JavaDoc key) {
304         return insertOrder.indexOf(key);
305     }
306
307     /**
308      * Removes the element at the specified index.
309      *
310      * @param index the index of the object to remove
311      * @return the previous value corresponding the <code>key</code>,
312      * or <code>null</code> if none existed
313      * @throws IndexOutOfBoundsException if the index is invalid
314      */

315     public Object JavaDoc remove(int index) {
316         return remove(get(index));
317     }
318
319     /**
320      * Gets an unmodifiable List view of the keys which changes as the map changes.
321      * <p>
322      * The returned list is unmodifiable because changes to the values of
323      * the list (using {@link java.util.ListIterator#set(Object)}) will
324      * effectively remove the value from the list and reinsert that value at
325      * the end of the list, which is an unexpected side effect of changing the
326      * value of a list. This occurs because changing the key, changes when the
327      * mapping is added to the map and thus where it appears in the list.
328      * <p>
329      * An alternative to this method is to use {@link #keySet()}.
330      *
331      * @see #keySet()
332      * @return The ordered list of keys.
333      */

334     public List JavaDoc asList() {
335         return UnmodifiableList.decorate(insertOrder);
336     }
337
338     //-----------------------------------------------------------------------
339
static class ValuesView extends AbstractCollection JavaDoc {
340         private final ListOrderedMap parent;
341
342         ValuesView(ListOrderedMap parent) {
343             super();
344             this.parent = parent;
345         }
346
347         public int size() {
348             return this.parent.size();
349         }
350
351         public boolean contains(Object JavaDoc value) {
352             return this.parent.containsValue(value);
353         }
354
355         public void clear() {
356             this.parent.clear();
357         }
358
359         public Iterator JavaDoc iterator() {
360             return new AbstractIteratorDecorator(parent.entrySet().iterator()) {
361                 public Object JavaDoc next() {
362                     return ((Map.Entry JavaDoc) iterator.next()).getValue();
363                 }
364             };
365         }
366     }
367     
368     //-----------------------------------------------------------------------
369
static class KeySetView extends AbstractSet JavaDoc {
370         private final ListOrderedMap parent;
371
372         KeySetView(ListOrderedMap parent) {
373             super();
374             this.parent = parent;
375         }
376
377         public int size() {
378             return this.parent.size();
379         }
380
381         public boolean contains(Object JavaDoc value) {
382             return this.parent.containsKey(value);
383         }
384
385         public void clear() {
386             this.parent.clear();
387         }
388
389         public Iterator JavaDoc iterator() {
390             return new AbstractIteratorDecorator(parent.entrySet().iterator()) {
391                 public Object JavaDoc next() {
392                     return ((Map.Entry JavaDoc) super.next()).getKey();
393                 }
394             };
395         }
396     }
397
398     //-----------------------------------------------------------------------
399
static class EntrySetView extends AbstractSet JavaDoc {
400         private final ListOrderedMap parent;
401         private final List JavaDoc insertOrder;
402         private Set JavaDoc entrySet;
403
404         public EntrySetView(ListOrderedMap parent, List JavaDoc insertOrder) {
405             super();
406             this.parent = parent;
407             this.insertOrder = insertOrder;
408         }
409
410         private Set JavaDoc getEntrySet() {
411             if (entrySet == null) {
412                 entrySet = parent.getMap().entrySet();
413             }
414             return entrySet;
415         }
416         
417         public int size() {
418             return this.parent.size();
419         }
420         public boolean isEmpty() {
421             return this.parent.isEmpty();
422         }
423
424         public boolean contains(Object JavaDoc obj) {
425             return getEntrySet().contains(obj);
426         }
427
428         public boolean containsAll(Collection JavaDoc coll) {
429             return getEntrySet().containsAll(coll);
430         }
431
432         public boolean remove(Object JavaDoc obj) {
433             if (obj instanceof Map.Entry JavaDoc == false) {
434                 return false;
435             }
436             if (getEntrySet().contains(obj)) {
437                 Object JavaDoc key = ((Map.Entry JavaDoc) obj).getKey();
438                 parent.remove(key);
439                 return true;
440             }
441             return false;
442         }
443
444         public void clear() {
445             this.parent.clear();
446         }
447         
448         public boolean equals(Object JavaDoc obj) {
449             if (obj == this) {
450                 return true;
451             }
452             return getEntrySet().equals(obj);
453         }
454         
455         public int hashCode() {
456             return getEntrySet().hashCode();
457         }
458
459         public String JavaDoc toString() {
460             return getEntrySet().toString();
461         }
462         
463         public Iterator JavaDoc iterator() {
464             return new ListOrderedIterator(parent, insertOrder);
465         }
466     }
467     
468     //-----------------------------------------------------------------------
469
static class ListOrderedIterator extends AbstractIteratorDecorator {
470         private final ListOrderedMap parent;
471         private Object JavaDoc last = null;
472         
473         ListOrderedIterator(ListOrderedMap parent, List JavaDoc insertOrder) {
474             super(insertOrder.iterator());
475             this.parent = parent;
476         }
477         
478         public Object JavaDoc next() {
479             last = super.next();
480             return new ListOrderedMapEntry(parent, last);
481         }
482
483         public void remove() {
484             super.remove();
485             parent.getMap().remove(last);
486         }
487     }
488     
489     //-----------------------------------------------------------------------
490
static class ListOrderedMapEntry extends AbstractMapEntry {
491         private final ListOrderedMap parent;
492         
493         ListOrderedMapEntry(ListOrderedMap parent, Object JavaDoc key) {
494             super(key, null);
495             this.parent = parent;
496         }
497         
498         public Object JavaDoc getValue() {
499             return parent.get(key);
500         }
501
502         public Object JavaDoc setValue(Object JavaDoc value) {
503             return parent.getMap().put(key, value);
504         }
505     }
506
507     //-----------------------------------------------------------------------
508
static class ListOrderedMapIterator implements OrderedMapIterator, ResettableIterator {
509         private final ListOrderedMap parent;
510         private ListIterator JavaDoc iterator;
511         private Object JavaDoc last = null;
512         private boolean readable = false;
513         
514         ListOrderedMapIterator(ListOrderedMap parent) {
515             super();
516             this.parent = parent;
517             this.iterator = parent.insertOrder.listIterator();
518         }
519         
520         public boolean hasNext() {
521             return iterator.hasNext();
522         }
523         
524         public Object JavaDoc next() {
525             last = iterator.next();
526             readable = true;
527             return last;
528         }
529         
530         public boolean hasPrevious() {
531             return iterator.hasPrevious();
532         }
533         
534         public Object JavaDoc previous() {
535             last = iterator.previous();
536             readable = true;
537             return last;
538         }
539         
540         public void remove() {
541             if (readable == false) {
542                 throw new IllegalStateException JavaDoc(AbstractHashedMap.REMOVE_INVALID);
543             }
544             iterator.remove();
545             parent.map.remove(last);
546             readable = false;
547         }
548         
549         public Object JavaDoc getKey() {
550             if (readable == false) {
551                 throw new IllegalStateException JavaDoc(AbstractHashedMap.GETKEY_INVALID);
552             }
553             return last;
554         }
555
556         public Object JavaDoc getValue() {
557             if (readable == false) {
558                 throw new IllegalStateException JavaDoc(AbstractHashedMap.GETVALUE_INVALID);
559             }
560             return parent.get(last);
561         }
562         
563         public Object JavaDoc setValue(Object JavaDoc value) {
564             if (readable == false) {
565                 throw new IllegalStateException JavaDoc(AbstractHashedMap.SETVALUE_INVALID);
566             }
567             return parent.map.put(last, value);
568         }
569         
570         public void reset() {
571             iterator = parent.insertOrder.listIterator();
572             last = null;
573             readable = false;
574         }
575         
576         public String JavaDoc toString() {
577             if (readable == true) {
578                 return "Iterator[" + getKey() + "=" + getValue() + "]";
579             } else {
580                 return "Iterator[]";
581             }
582         }
583     }
584     
585 }
586
Popular Tags