View Javadoc

1   /*
2    *  Copyright 2003-2005, 2008 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 net.sf.morph.util;
17  
18  import java.util.ArrayList;
19  import java.util.Collection;
20  import java.util.HashSet;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.Set;
24  
25  import org.apache.commons.collections.iterators.AbstractIteratorDecorator;
26  import org.apache.commons.collections.list.UnmodifiableList;
27  
28  /**
29   * Decorates another <code>Set</code> to ensure that the order of addition
30   * is retained and used by the iterator.
31   * <p>
32   * If an object is added to the set for a second time, it will remain in the
33   * original position in the iteration.
34   * The order can be observed from the set via the iterator or toArray methods.
35   * <p>
36   * The ListOrderedSet also has various useful direct methods. These include many
37   * from <code>List</code>, such as <code>get(int)</code>, <code>remove(int)</code>
38   * and <code>indexOf(int)</code>. An unmodifiable <code>List</code> view of 
39   * the set can be obtained via <code>asList()</code>.
40   * <p>
41   * This class cannot implement the <code>List</code> interface directly as
42   * various interface methods (notably equals/hashCode) are incompatable with a set.
43   * <p>
44   * This class is Serializable from Commons Collections 3.1.
45   * <p>
46   * <em>Note: this class was copied from Commons Collections.</em>
47   *
48   * @since Commons Collections 3.0
49   * @version $Revision: 155406 $ $Date: 2005-02-26 12:55:26 +0000 (Sat, 26 Feb 2005) $
50   * 
51   * @author Stephen Colebourne
52   * @author Henning P. Schmiedehausen
53   */
54  public class ListOrderedSet implements Set {
55  
56      /** Serialization version */
57      private static final long serialVersionUID = -228664393870420141L;
58  
59      /** The collection being decorated */
60      protected Collection collection;
61  
62      /** Internal list to hold the sequence of objects */
63      protected final List setOrder;
64  
65      /**
66       * Factory method to create an ordered set specifying the list and set to use.
67       * <p>
68       * The list and set must both be empty.
69       * 
70       * @param set  the set to decorate, must be empty and not null
71       * @param list  the list to decorate, must be empty and not null
72       * @throws IllegalArgumentException if set or list is null
73       * @throws IllegalArgumentException if either the set or list is not empty
74       * @since Commons Collections 3.1
75       */
76      public static ListOrderedSet decorate(Set set, List list) {
77          if (set == null) {
78              throw new IllegalArgumentException("Set must not be null");
79          }
80          if (list == null) {
81              throw new IllegalArgumentException("List must not be null");
82          }
83          if (set.size() > 0 || list.size() > 0) {
84              throw new IllegalArgumentException("Set and List must be empty");
85          }
86          return new ListOrderedSet(set, list);
87      }
88  
89      /**
90       * Factory method to create an ordered set.
91       * <p>
92       * An <code>ArrayList</code> is used to retain order.
93       * 
94       * @param set  the set to decorate, must not be null
95       * @throws IllegalArgumentException if set is null
96       */
97      public static ListOrderedSet decorate(Set set) {
98          return new ListOrderedSet(set);
99      }
100 
101     /**
102      * Factory method to create an ordered set using the supplied list to retain order.
103      * <p>
104      * A <code>HashSet</code> is used for the set behaviour.
105      * <p>
106      * NOTE: If the list contains duplicates, the duplicates are removed,
107      * altering the specified list.
108      * 
109      * @param list  the list to decorate, must not be null
110      * @throws IllegalArgumentException if list is null
111      */
112     public static ListOrderedSet decorate(List list) {
113         if (list == null) {
114             throw new IllegalArgumentException("List must not be null");
115         }
116         Set set = new HashSet(list);
117         list.retainAll(set);
118         
119         return new ListOrderedSet(set, list);
120     }
121 
122     //-----------------------------------------------------------------------
123     /**
124      * Constructs a new empty <code>ListOrderedSet</code> using
125      * a <code>HashSet</code> and an <code>ArrayList</code> internally.
126      * 
127      * @since Commons Collections 3.1
128      */
129     public ListOrderedSet() {
130         collection = new HashSet();
131         setOrder = new ArrayList();
132     }
133 
134     /**
135      * Constructor that wraps (not copies).
136      * 
137      * @param set  the set to decorate, must not be null
138      * @throws IllegalArgumentException if set is null
139      */
140     protected ListOrderedSet(Set set) {
141         collection = set;
142         setOrder = new ArrayList(set);
143     }
144 
145     /**
146      * Constructor that wraps (not copies) the Set and specifies the list to use.
147      * <p>
148      * The set and list must both be correctly initialised to the same elements.
149      * 
150      * @param set  the set to decorate, must not be null
151      * @param list  the list to decorate, must not be null
152      * @throws IllegalArgumentException if set or list is null
153      */
154     protected ListOrderedSet(Set set, List list) {
155         collection = set;
156         if (list == null) {
157             throw new IllegalArgumentException("List must not be null");
158         }
159         setOrder = list;
160     }
161 
162     //-----------------------------------------------------------------------
163     /**
164      * Gets an unmodifiable view of the order of the Set.
165      * 
166      * @return an unmodifiable list view
167      */
168     public List asList() {
169         return UnmodifiableList.decorate(setOrder);
170     }
171 
172     //-----------------------------------------------------------------------
173     public void clear() {
174         collection.clear();
175         setOrder.clear();
176     }
177 
178     public Iterator iterator() {
179         return new OrderedSetIterator(setOrder.iterator(), collection);
180     }
181 
182     public boolean add(Object object) {
183         if (collection.contains(object)) {
184             // re-adding doesn't change order
185             return collection.add(object);
186         } else {
187             // first add, so add to both set and list
188             boolean result = collection.add(object);
189             setOrder.add(object);
190             return result;
191         }
192     }
193 
194     public boolean addAll(Collection coll) {
195         boolean result = false;
196         for (Iterator it = coll.iterator(); it.hasNext();) {
197             Object object = it.next();
198             result = result | add(object);
199         }
200         return result;
201     }
202 
203     public boolean remove(Object object) {
204         boolean result = collection.remove(object);
205         setOrder.remove(object);
206         return result;
207     }
208 
209     public boolean removeAll(Collection coll) {
210         boolean result = false;
211         for (Iterator it = coll.iterator(); it.hasNext();) {
212             Object object = it.next();
213             result = result | remove(object);
214         }
215         return result;
216     }
217 
218     public boolean retainAll(Collection coll) {
219         boolean result = collection.retainAll(coll);
220         if (result == false) {
221             return false;
222         } else if (collection.size() == 0) {
223             setOrder.clear();
224         } else {
225             for (Iterator it = setOrder.iterator(); it.hasNext();) {
226                 Object object = it.next();
227                 if (collection.contains(object) == false) {
228                     it.remove();
229                 }
230             }
231         }
232         return result;
233     }
234 
235     public Object[] toArray() {
236         return setOrder.toArray();
237     }
238 
239     public Object[] toArray(Object a[]) {
240         return setOrder.toArray(a);
241     }
242 
243     //-----------------------------------------------------------------------
244     public Object get(int index) {
245         return setOrder.get(index);
246     }
247 
248     public int indexOf(Object object) {
249         return setOrder.indexOf(object);
250     }
251 
252     public void add(int index, Object object) {
253         if (contains(object) == false) {
254             collection.add(object);
255             setOrder.add(index, object);
256         }
257     }
258 
259     public boolean addAll(int index, Collection coll) {
260         boolean changed = false;
261         for (Iterator it = coll.iterator(); it.hasNext();) {
262             Object object = it.next();
263             if (contains(object) == false) {
264                 collection.add(object);
265                 setOrder.add(index, object);
266                 index++;
267                 changed = true;
268             }
269         }
270         return changed;
271     }
272 
273     public Object remove(int index) {
274         Object obj = setOrder.remove(index);
275         remove(obj);
276         return obj;
277     }
278 
279     /**
280      * Uses the underlying List's toString so that order is achieved. 
281      * This means that the decorated Set's toString is not used, so 
282      * any custom toStrings will be ignored. 
283      */
284     // Fortunately List.toString and Set.toString look the same
285     public String toString() {
286         return setOrder.toString();
287     }
288 
289     //-----------------------------------------------------------------------
290     /**
291      * Internal iterator handle remove.
292      */
293     static class OrderedSetIterator extends AbstractIteratorDecorator {
294         
295         /** Object we iterate on */
296         protected final Collection set;
297         /** Last object retrieved */
298         protected Object last;
299 
300         private OrderedSetIterator(Iterator iterator, Collection set) {
301             super(iterator);
302             this.set = set;
303         }
304 
305         public Object next() {
306             last = iterator.next();
307             return last;
308         }
309 
310         public void remove() {
311             set.remove(last);
312             iterator.remove();
313             last = null;
314         }
315     }
316 
317 	public boolean contains(Object o) {
318 	    return collection.contains(o);
319     }
320 
321 	public boolean containsAll(Collection c) {
322 	    return collection.containsAll(c);
323     }
324 
325 	public boolean isEmpty() {
326 	    return collection.isEmpty();
327     }
328 
329 	public int size() {
330 	    return collection.size();
331     }
332 
333 }