001 /**
002 * <copyright>
003 *
004 * Copyright (c) 2002-2006 IBM Corporation and others.
005 * All rights reserved. This program and the accompanying materials
006 * are made available under the terms of the Eclipse Public License v1.0
007 * which accompanies this distribution, and is available at
008 * http://www.eclipse.org/legal/epl-v10.html
009 *
010 * Contributors:
011 * IBM - Initial API and implementation
012 *
013 * </copyright>
014 *
015 * $Id: ECollections.java,v 1.8 2008/12/13 15:54:18 emerks Exp $
016 */
017 package org.eclipse.emf.common.util;
018
019
020 import java.util.Arrays;
021 import java.util.Collection;
022 import java.util.Collections;
023 import java.util.Comparator;
024 import java.util.Iterator;
025 import java.util.List;
026 import java.util.ListIterator;
027 import java.util.Map;
028 import java.util.NoSuchElementException;
029 import java.util.Set;
030
031
032 /**
033 * Support for {@link #EMPTY_ELIST empty} and {@link #unmodifiableEList unmodifiable} <code>EList</code>s.
034 */
035 public class ECollections
036 {
037 // Suppress default constructor for noninstantiability.
038 private ECollections()
039 {
040 super();
041 }
042
043 /**
044 * Moves the object to the new position, if is in the list.
045 * @param list
046 * @param newPosition the position of the object after the move.
047 * @param object the object to move.
048 */
049 public static <T> void move(List<T> list, int newPosition, T object)
050 {
051 if (list instanceof EList<?>)
052 {
053 ((EList<T>)list).move(newPosition, object);
054 }
055 else
056 {
057 list.remove(object);
058 list.add(newPosition, object);
059 }
060 }
061
062 /**
063 * Moves the object from the old position to the new position.
064 * @param list
065 * @param targetIndex the position of the object after the move.
066 * @param sourceIndex the position of the object before the move.
067 * @return the moved object
068 */
069 public static <T> T move(List<T> list, int targetIndex, int sourceIndex)
070 {
071 if (list instanceof EList<?>)
072 {
073 return ((EList<T>)list).move(targetIndex, sourceIndex);
074 }
075 else
076 {
077 T object = list.remove(sourceIndex);
078 list.add(targetIndex, object);
079 return object;
080 }
081 }
082
083 /**
084 * Reverses the order of the elements in the specified EList.
085 */
086 public static void reverse(EList<?> list)
087 {
088 int last = list.size() - 1;
089 for (int i = 0; i < last; i++)
090 {
091 list.move(i, last);
092 }
093 }
094
095 /**
096 * Searches for the first occurrence of the given argument in list starting from
097 * a specified index. The equality is tested using the operator <tt>==<tt> and
098 * the <tt>equals</tt> method.
099 * @param list
100 * @param o an object (can be null)
101 * @param fromIndex
102 * @return the index of the first occurrence of the argument in this
103 * list (where index>=fromIndex); returns <tt>-1</tt> if the
104 * object is not found.
105 * @since 2.1.0
106 */
107 public static int indexOf(List<?> list, Object o, int fromIndex)
108 {
109 if (fromIndex < 0)
110 {
111 fromIndex = 0;
112 }
113
114 int size = list.size();
115 for (int i = fromIndex; i < size; i++)
116 {
117 Object element = list.get(i);
118 if (o == null)
119 {
120 if (element == null)
121 {
122 return i;
123 }
124 }
125 else if (o == element || o.equals(element))
126 {
127 return i;
128 }
129 }
130 return -1;
131 }
132
133 /**
134 * Sorts the specified list. Use this method instead of
135 * {@link Collections#sort(java.util.List)} to
136 * avoid errors when sorting unique lists.
137 * @since 2.1.0
138 */
139 public static void sort(EList<?> list)
140 {
141 Object[] listAsArray = list.toArray();
142 Arrays.sort(listAsArray);
143 for (int i=0; i < listAsArray.length; i++)
144 {
145 int oldIndex = indexOf(list, listAsArray[i], i);
146 if (i != oldIndex)
147 {
148 list.move(i, oldIndex);
149 }
150 }
151 }
152
153 /**
154 * Sorts the specified list based on the order defined by the
155 * specified comparator. Use this method instead of
156 * {@link Collections#sort(java.util.List, java.util.Comparator)} to
157 * avoid errors when sorting unique lists.
158 * @since 2.1.0
159 */
160 public static <T> void sort(EList<T> list, Comparator<? super T> comparator)
161 {
162 Object[] listAsArray = list.toArray();
163 @SuppressWarnings("unchecked") Comparator<Object> objectComparator = (Comparator<Object>)comparator;
164 Arrays.sort(listAsArray, objectComparator);
165 for (int i=0; i < listAsArray.length; i++)
166 {
167 int oldIndex = indexOf(list, listAsArray[i], i);
168 if (i != oldIndex)
169 {
170 list.move(i, oldIndex);
171 }
172 }
173 }
174
175 /**
176 * Sets the <code>eList</code>'s contents and order to be exactly that of the <code>prototype</code> list.
177 * This implementation minimizes the number of notifications the operation will produce.
178 * Objects already in the list will be moved, missing objects will be added, and extra objects will be removed.
179 * If <code>eList</code>'s contents and order are already exactly that of the <code>prototype</code> list,
180 * no change will be made.
181 * @param eList the list to set.
182 * @param prototypeList the list representing the desired content and order.
183 */
184 public static <T> void setEList(EList<T> eList, List<? extends T> prototypeList)
185 {
186 int index = 0;
187 for (T prototypeObject : prototypeList)
188 {
189 if (eList.size() <= index)
190 {
191 eList.add(prototypeObject);
192 }
193 else
194 {
195 boolean done;
196 do
197 {
198 done = true;
199 Object targetObject = eList.get(index);
200 if (targetObject == null ? prototypeObject != null : !targetObject.equals(prototypeObject))
201 {
202 int position = indexOf(eList, prototypeObject, index);
203 if (position != -1)
204 {
205 int targetIndex = indexOf(prototypeList, targetObject, index);
206 if (targetIndex == -1)
207 {
208 eList.remove(index);
209 done = false;
210 }
211 else if (targetIndex > position)
212 {
213 if (eList.size() <= targetIndex)
214 {
215 targetIndex = eList.size() - 1;
216 }
217 eList.move(targetIndex, index);
218
219 done = false;
220 }
221 else
222 {
223 eList.move(index, position);
224 }
225 }
226 else
227 {
228 eList.add(index, prototypeObject);
229 }
230 }
231 }
232 while (!done);
233 }
234 ++index;
235 }
236 for (int i = eList.size(); i > index;)
237 {
238 eList.remove(--i);
239 }
240 }
241
242 /**
243 * Returns an unmodifiable view of the list.
244 * @return an unmodifiable view of the list.
245 */
246 public static <T> EList<T> unmodifiableEList(EList<? extends T> list)
247 {
248 return new UnmodifiableEList<T>(list);
249 }
250
251 /**
252 * Returns an unmodifiable view of the map.
253 * @return an unmodifiable view of the map.
254 */
255 public static <K, V> EMap<K, V> unmodifiableEMap(EMap<? extends K, ? extends V> map)
256 {
257 return new UnmodifiableEMap<K, V>(map);
258 }
259
260 /**
261 * An unmodifiable empty list with an efficient reusable iterator.
262 */
263 public static final EList<?> EMPTY_ELIST = new EmptyUnmodifiableEList();
264
265 @SuppressWarnings("unchecked")
266 public static <T> EList<T> emptyEList()
267 {
268 return (EList<T>)EMPTY_ELIST;
269 }
270
271 /**
272 * An unmodifiable empty map with an efficient reusable iterator.
273 */
274 public static final EMap<?, ?> EMPTY_EMAP = new EmptyUnmodifiableEMap();
275
276 @SuppressWarnings("unchecked")
277 public static <K, V> EMap<K, V> emptyEMap()
278 {
279 return (EMap<K, V>)EMPTY_EMAP;
280 }
281
282 private static class UnmodifiableEList<E> implements EList<E>
283 {
284 protected EList<? extends E> list;
285
286 public UnmodifiableEList(EList<? extends E> list)
287 {
288 this.list = list;
289 }
290
291 public int size()
292 {
293 return list.size();
294 }
295
296 public boolean isEmpty()
297 {
298 return list.isEmpty();
299 }
300
301 public boolean contains(Object o)
302 {
303 return list.contains(o);
304 }
305
306 public Object[] toArray()
307 {
308 return list.toArray();
309 }
310
311 public <T> T[] toArray(T[] a)
312 {
313 return list.toArray(a);
314 }
315
316 @Override
317 public String toString()
318 {
319 return list.toString();
320 }
321
322 public Iterator<E> iterator()
323 {
324 return
325 new Iterator<E>()
326 {
327 Iterator<? extends E> i = list.iterator();
328
329 public boolean hasNext()
330 {
331 return i.hasNext();
332 }
333 public E next()
334 {
335 return i.next();
336 }
337 public void remove()
338 {
339 throw new UnsupportedOperationException();
340 }
341 };
342 }
343
344 public boolean add(E o)
345 {
346 throw new UnsupportedOperationException();
347 }
348
349 public boolean remove(Object o)
350 {
351 throw new UnsupportedOperationException();
352 }
353
354 public boolean containsAll(Collection<?> coll)
355 {
356 return list.containsAll(coll);
357 }
358
359 public boolean addAll(Collection<? extends E> coll)
360 {
361 throw new UnsupportedOperationException();
362 }
363
364 public boolean removeAll(Collection<?> coll)
365 {
366 throw new UnsupportedOperationException();
367 }
368
369 public boolean retainAll(Collection<?> coll)
370 {
371 throw new UnsupportedOperationException();
372 }
373
374 public void clear()
375 {
376 throw new UnsupportedOperationException();
377 }
378
379 @Override
380 public boolean equals(Object o)
381 {
382 return list.equals(o);
383 }
384
385 @Override
386 public int hashCode()
387 {
388 return list.hashCode();
389 }
390
391 public E get(int index)
392 {
393 return list.get(index);
394 }
395
396 public E set(int index, E element)
397 {
398 throw new UnsupportedOperationException();
399 }
400
401 public void add(int index, Object element)
402 {
403 throw new UnsupportedOperationException();
404 }
405
406 public E remove(int index)
407 {
408 throw new UnsupportedOperationException();
409 }
410
411 public int indexOf(Object o)
412 {
413 return list.indexOf(o);
414 }
415
416 public int lastIndexOf(Object o)
417 {
418 return list.lastIndexOf(o);
419 }
420
421 public boolean addAll(int index, Collection<? extends E> collection)
422 {
423 throw new UnsupportedOperationException();
424 }
425
426 public ListIterator<E> listIterator()
427 {
428 return listIterator(0);
429 }
430
431 public ListIterator<E> listIterator(final int index)
432 {
433 return
434 new ListIterator<E>()
435 {
436 ListIterator<? extends E> i = list.listIterator(index);
437
438 public boolean hasNext()
439 {
440 return i.hasNext();
441 }
442
443 public E next()
444 {
445 return i.next();
446 }
447
448 public boolean hasPrevious()
449 {
450 return i.hasPrevious();
451 }
452
453 public E previous()
454 {
455 return i.previous();
456 }
457
458 public int nextIndex()
459 {
460 return i.nextIndex();
461 }
462
463 public int previousIndex()
464 {
465 return i.previousIndex();
466 }
467
468 public void remove()
469 {
470 throw new UnsupportedOperationException();
471 }
472
473 public void set(E o)
474 {
475 throw new UnsupportedOperationException();
476 }
477
478 public void add(E o)
479 {
480 throw new UnsupportedOperationException();
481 }
482 };
483 }
484
485 public List<E> subList(int fromIndex, int toIndex)
486 {
487 return new UnmodifiableEList<E>(new BasicEList<E>(list.subList(fromIndex, toIndex)));
488 }
489
490 public void move(int newPosition, E o)
491 {
492 throw new UnsupportedOperationException();
493 }
494
495 public E move(int newPosition, int oldPosition)
496 {
497 throw new UnsupportedOperationException();
498 }
499 }
500
501 private static class UnmodifiableEMap<K, V> extends UnmodifiableEList<Map.Entry<K, V>> implements EMap<K, V>
502 {
503 protected EMap<? extends K, ? extends V> eMap;
504
505 @SuppressWarnings("unchecked")
506 public UnmodifiableEMap(EMap<? extends K, ? extends V> eMap)
507 {
508 super((EMap<K, V>)eMap);
509 this.eMap = eMap;
510 }
511
512 public boolean containsKey(Object key)
513 {
514 return eMap.containsKey(key);
515 }
516
517 public boolean containsValue(Object value)
518 {
519 return eMap.containsValue(value);
520 }
521
522 @SuppressWarnings("unchecked")
523 public Set<Map.Entry<K, V>> entrySet()
524 {
525 return Collections.unmodifiableSet((Set<Map.Entry<K, V>>)(Set<?>)eMap.entrySet());
526 }
527
528 public V get(Object key)
529 {
530 return eMap.get(key);
531 }
532
533 public int indexOfKey(Object key)
534 {
535 return eMap.indexOf(key);
536 }
537
538 public Set<K> keySet()
539 {
540 return Collections.unmodifiableSet(eMap.keySet());
541 }
542
543 public Map<K, V> map()
544 {
545 return Collections.unmodifiableMap(eMap.map());
546 }
547
548 public Collection<V> values()
549 {
550 return Collections.unmodifiableCollection(eMap.values());
551 }
552
553 public V put(K key, V value)
554 {
555 throw new UnsupportedOperationException();
556 }
557
558 public void putAll(EMap<? extends K, ? extends V> map)
559 {
560 throw new UnsupportedOperationException();
561 }
562
563 public void putAll(Map<? extends K, ? extends V> map)
564 {
565 throw new UnsupportedOperationException();
566 }
567
568 public V removeKey(Object key)
569 {
570 throw new UnsupportedOperationException();
571 }
572 }
573
574 private static class BasicEmptyUnmodifiableEList<E>
575 {
576 public int size()
577 {
578 return 0;
579 }
580
581 public boolean isEmpty()
582 {
583 return true;
584 }
585
586 @Override
587 public boolean equals(Object o)
588 {
589 return Collections.EMPTY_LIST.equals(o);
590 }
591
592 @Override
593 public int hashCode()
594 {
595 return Collections.EMPTY_LIST.hashCode();
596 }
597
598 public E get(int index)
599 {
600 Collections.EMPTY_LIST.get(index);
601 return null;
602 }
603
604 public boolean contains(Object o)
605 {
606 return false;
607 }
608
609 public int indexOf(Object o)
610 {
611 return -1;
612 }
613
614 public int lastIndexOf(Object o)
615 {
616 return -1;
617 }
618
619 ListIterator<E> listIterator =
620 new ListIterator<E>()
621 {
622 public boolean hasNext()
623 {
624 return false;
625 }
626 public E next()
627 {
628 throw new NoSuchElementException();
629 }
630 public boolean hasPrevious()
631 {
632 return false;
633 }
634 public E previous()
635 {
636 throw new NoSuchElementException();
637 }
638 public int nextIndex()
639 {
640 return 0;
641 }
642 public int previousIndex()
643 {
644 return -1;
645 }
646
647 public void remove()
648 {
649 throw new UnsupportedOperationException();
650 }
651 public void set(E o)
652 {
653 throw new UnsupportedOperationException();
654 }
655 public void add(E o)
656 {
657 throw new UnsupportedOperationException();
658 }
659 };
660
661 public Iterator<E> iterator()
662 {
663 return listIterator;
664 }
665
666 public ListIterator<E> listIterator()
667 {
668 return listIterator;
669 }
670
671 public ListIterator<E> listIterator(int index)
672 {
673 return listIterator;
674 }
675
676 public List<E> subList(int fromIndex, int toIndex)
677 {
678 return Collections.<E>emptyList().subList(fromIndex, toIndex);
679 }
680
681 public Object[] toArray()
682 {
683 return Collections.EMPTY_LIST.toArray();
684 }
685
686 public <T> T[] toArray(T[] a)
687 {
688 return Collections.<T>emptyList().toArray(a);
689 }
690
691 @Override
692 public String toString()
693 {
694 return Collections.EMPTY_LIST.toString();
695 }
696
697 public boolean add(E o)
698 {
699 throw new UnsupportedOperationException();
700 }
701
702 public boolean remove(Object o)
703 {
704 throw new UnsupportedOperationException();
705 }
706
707 public boolean containsAll(Collection<?> coll)
708 {
709 return false;
710 }
711
712 public boolean addAll(Collection<? extends E> coll)
713 {
714 throw new UnsupportedOperationException();
715 }
716
717 public boolean removeAll(Collection<?> coll)
718 {
719 throw new UnsupportedOperationException();
720 }
721
722 public boolean retainAll(Collection<?> coll)
723 {
724 throw new UnsupportedOperationException();
725 }
726
727 public void clear()
728 {
729 throw new UnsupportedOperationException();
730 }
731
732 public E set(int index, E element)
733 {
734 throw new UnsupportedOperationException();
735 }
736
737 public void add(int index, E element)
738 {
739 throw new UnsupportedOperationException();
740 }
741
742 public E remove(int index)
743 {
744 throw new UnsupportedOperationException();
745 }
746
747 public boolean addAll(int index, Collection<? extends E> collection)
748 {
749 throw new UnsupportedOperationException();
750 }
751
752 public void move(int newPosition, E o)
753 {
754 throw new UnsupportedOperationException();
755 }
756
757 public E move(int newPosition, int oldPosition)
758 {
759 throw new UnsupportedOperationException();
760 }
761 }
762
763 private static class EmptyUnmodifiableEList extends BasicEmptyUnmodifiableEList<Object> implements EList<Object>
764 {
765 private EmptyUnmodifiableEList()
766 {
767 super();
768 }
769 }
770
771 private static class EmptyUnmodifiableEMap extends BasicEmptyUnmodifiableEList<Map.Entry<Object, Object>> implements EMap<Object, Object>
772 {
773 public boolean containsKey(Object key)
774 {
775 return false;
776 }
777
778 public boolean containsValue(Object value)
779 {
780 return false;
781 }
782
783 public Set<Map.Entry<Object, Object>> entrySet()
784 {
785 return Collections.emptySet();
786 }
787
788 public Object get(Object key)
789 {
790 return null;
791 }
792
793 public int indexOfKey(Object key)
794 {
795 return -1;
796 }
797
798 public Set<Object> keySet()
799 {
800 return Collections.emptySet();
801 }
802
803 public Map<Object, Object> map()
804 {
805 return Collections.emptyMap();
806 }
807
808 public Collection<Object> values()
809 {
810 return Collections.emptyList();
811 }
812
813 public Object put(Object key, Object value)
814 {
815 throw new UnsupportedOperationException();
816 }
817
818 public void putAll(EMap<? extends Object, ? extends Object> map)
819 {
820 throw new UnsupportedOperationException();
821 }
822
823 public void putAll(Map<? extends Object, ? extends Object> map)
824 {
825 throw new UnsupportedOperationException();
826 }
827
828 public Object removeKey(Object key)
829 {
830 throw new UnsupportedOperationException();
831 }
832 }
833 }