1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
185 return collection.add(object);
186 } else {
187
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
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 }