/** * Copyright (c) 2002-2010 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM - Initial API and implementation */ package org.eclipse.emf.common.util; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; /** * Support for {@link #EMPTY_ELIST empty} and {@link #unmodifiableEList unmodifiable} <code>EList</code>s. */ public class ECollections { // Suppress default constructor for noninstantiability. private ECollections() { super(); } /** * Moves the object to the new position, if is in the list. * @param list * @param newPosition the position of the object after the move. * @param object the object to move. */ public static <T> void move(List<T> list, int newPosition, T object) { if (list instanceof EList<?>) { ((EList<T>)list).move(newPosition, object); } else { list.remove(object); list.add(newPosition, object); } } /** * Moves the object from the old position to the new position. * @param list * @param targetIndex the position of the object after the move. * @param sourceIndex the position of the object before the move. * @return the moved object */ public static <T> T move(List<T> list, int targetIndex, int sourceIndex) { if (list instanceof EList<?>) { return ((EList<T>)list).move(targetIndex, sourceIndex); } else { T object = list.remove(sourceIndex); list.add(targetIndex, object); return object; } } /** * Reverses the order of the elements in the specified EList. */ public static void reverse(EList<?> list) { int last = list.size() - 1; for (int i = 0; i < last; i++) { list.move(i, last); } } /** * Searches for the first occurrence of the given argument in list starting from * a specified index. The equality is tested using the operator <tt>==<tt> and * the <tt>equals</tt> method. * @param list * @param o an object (can be null) * @param fromIndex * @return the index of the first occurrence of the argument in this * list (where index>=fromIndex); returns <tt>-1</tt> if the * object is not found. * @since 2.1.0 */ public static int indexOf(List<?> list, Object o, int fromIndex) { if (fromIndex < 0) { fromIndex = 0; } int size = list.size(); for (int i = fromIndex; i < size; i++) { Object element = list.get(i); if (o == null) { if (element == null) { return i; } } else if (o == element || o.equals(element)) { return i; } } return -1; } /** * Sorts the specified list. Use this method instead of * {@link Collections#sort(java.util.List)} to * avoid errors when sorting unique lists. * @since 2.1.0 */ public static void sort(EList<?> list) { Object[] listAsArray = list.toArray(); Arrays.sort(listAsArray); for (int i=0; i < listAsArray.length; i++) { int oldIndex = indexOf(list, listAsArray[i], i); if (i != oldIndex) { list.move(i, oldIndex); } } } /** * Sorts the specified list based on the order defined by the * specified comparator. Use this method instead of * {@link Collections#sort(java.util.List, java.util.Comparator)} to * avoid errors when sorting unique lists. * @since 2.1.0 */ public static <T> void sort(EList<T> list, Comparator<? super T> comparator) { Object[] listAsArray = list.toArray(); @SuppressWarnings("unchecked") Comparator<Object> objectComparator = (Comparator<Object>)comparator; Arrays.sort(listAsArray, objectComparator); for (int i=0; i < listAsArray.length; i++) { int oldIndex = indexOf(list, listAsArray[i], i); if (i != oldIndex) { list.move(i, oldIndex); } } } /** * Sets the <code>eList</code>'s contents and order to be exactly that of the <code>prototype</code> list. * This implementation minimizes the number of notifications the operation will produce. * Objects already in the list will be moved, missing objects will be added, and extra objects will be removed. * If <code>eList</code>'s contents and order are already exactly that of the <code>prototype</code> list, * no change will be made. * @param eList the list to set. * @param prototypeList the list representing the desired content and order. */ public static <T> void setEList(EList<T> eList, List<? extends T> prototypeList) { int index = 0; for (T prototypeObject : prototypeList) { if (eList.size() <= index) { eList.add(prototypeObject); } else { boolean done; do { done = true; Object targetObject = eList.get(index); if (targetObject == null ? prototypeObject != null : !targetObject.equals(prototypeObject)) { int position = indexOf(eList, prototypeObject, index); if (position != -1) { int targetIndex = indexOf(prototypeList, targetObject, index); if (targetIndex == -1) { eList.remove(index); done = false; } else if (targetIndex > position) { if (eList.size() <= targetIndex) { targetIndex = eList.size() - 1; } eList.move(targetIndex, index); done = false; } else { eList.move(index, position); } } else { eList.add(index, prototypeObject); } } } while (!done); } ++index; } for (int i = eList.size(); i > index;) { eList.remove(--i); } } /** * Returns an unmodifiable view of the list. * @return an unmodifiable view of the list. */ public static <T> EList<T> unmodifiableEList(EList<? extends T> list) { return new UnmodifiableEList<T>(list); } /** * Returns an unmodifiable view of the map. * @return an unmodifiable view of the map. */ public static <K, V> EMap<K, V> unmodifiableEMap(EMap<? extends K, ? extends V> map) { return new UnmodifiableEMap<K, V>(map); } /** * An unmodifiable empty list with an efficient reusable iterator. */ public static final EList<?> EMPTY_ELIST = new EmptyUnmodifiableEList(); @SuppressWarnings("unchecked") public static <T> EList<T> emptyEList() { return (EList<T>)EMPTY_ELIST; } /** * An unmodifiable empty map with an efficient reusable iterator. */ public static final EMap<?, ?> EMPTY_EMAP = new EmptyUnmodifiableEMap(); @SuppressWarnings("unchecked") public static <K, V> EMap<K, V> emptyEMap() { return (EMap<K, V>)EMPTY_EMAP; } private static class UnmodifiableEList<E> implements EList<E> { protected List<? extends E> list; public UnmodifiableEList(List<? extends E> list) { this.list = list; } public int size() { return list.size(); } public boolean isEmpty() { return list.isEmpty(); } public boolean contains(Object o) { return list.contains(o); } public Object[] toArray() { return list.toArray(); } public <T> T[] toArray(T[] a) { return list.toArray(a); } @Override public String toString() { return list.toString(); } public Iterator<E> iterator() { return new Iterator<E>() { Iterator<? extends E> i = list.iterator(); public boolean hasNext() { return i.hasNext(); } public E next() { return i.next(); } public void remove() { throw new UnsupportedOperationException(); } }; } public boolean add(E o) { throw new UnsupportedOperationException(); } public boolean remove(Object o) { throw new UnsupportedOperationException(); } public boolean containsAll(Collection<?> coll) { return list.containsAll(coll); } public boolean addAll(Collection<? extends E> coll) { throw new UnsupportedOperationException(); } public boolean removeAll(Collection<?> coll) { throw new UnsupportedOperationException(); } public boolean retainAll(Collection<?> coll) { throw new UnsupportedOperationException(); } public void clear() { throw new UnsupportedOperationException(); } @Override public boolean equals(Object o) { return list.equals(o); } @Override public int hashCode() { return list.hashCode(); } public E get(int index) { return list.get(index); } public E set(int index, E element) { throw new UnsupportedOperationException(); } public void add(int index, Object element) { throw new UnsupportedOperationException(); } public E remove(int index) { throw new UnsupportedOperationException(); } public int indexOf(Object o) { return list.indexOf(o); } public int lastIndexOf(Object o) { return list.lastIndexOf(o); } public boolean addAll(int index, Collection<? extends E> collection) { throw new UnsupportedOperationException(); } public ListIterator<E> listIterator() { return listIterator(0); } public ListIterator<E> listIterator(final int index) { return new ListIterator<E>() { ListIterator<? extends E> i = list.listIterator(index); public boolean hasNext() { return i.hasNext(); } public E next() { return i.next(); } public boolean hasPrevious() { return i.hasPrevious(); } public E previous() { return i.previous(); } public int nextIndex() { return i.nextIndex(); } public int previousIndex() { return i.previousIndex(); } public void remove() { throw new UnsupportedOperationException(); } public void set(E o) { throw new UnsupportedOperationException(); } public void add(E o) { throw new UnsupportedOperationException(); } }; } public List<E> subList(int fromIndex, int toIndex) { return new UnmodifiableEList<E>(new BasicEList<E>(list.subList(fromIndex, toIndex))); } public void move(int newPosition, E o) { throw new UnsupportedOperationException(); } public E move(int newPosition, int oldPosition) { throw new UnsupportedOperationException(); } } private static class UnmodifiableEMap<K, V> extends UnmodifiableEList<Map.Entry<K, V>> implements EMap<K, V> { protected EMap<? extends K, ? extends V> eMap; @SuppressWarnings("unchecked") public UnmodifiableEMap(EMap<? extends K, ? extends V> eMap) { super((EMap<K, V>)eMap); this.eMap = eMap; } public boolean containsKey(Object key) { return eMap.containsKey(key); } public boolean containsValue(Object value) { return eMap.containsValue(value); } @SuppressWarnings("unchecked") public Set<Map.Entry<K, V>> entrySet() { return Collections.unmodifiableSet((Set<Map.Entry<K, V>>)(Set<?>)eMap.entrySet()); } public V get(Object key) { return eMap.get(key); } public int indexOfKey(Object key) { return eMap.indexOf(key); } public Set<K> keySet() { return Collections.unmodifiableSet(eMap.keySet()); } public Map<K, V> map() { return Collections.unmodifiableMap(eMap.map()); } public Collection<V> values() { return Collections.unmodifiableCollection(eMap.values()); } public V put(K key, V value) { throw new UnsupportedOperationException(); } public void putAll(EMap<? extends K, ? extends V> map) { throw new UnsupportedOperationException(); } public void putAll(Map<? extends K, ? extends V> map) { throw new UnsupportedOperationException(); } public V removeKey(Object key) { throw new UnsupportedOperationException(); } } private static class BasicEmptyUnmodifiableEList<E> { public int size() { return 0; } public boolean isEmpty() { return true; } @Override public boolean equals(Object o) { return Collections.EMPTY_LIST.equals(o); } @Override public int hashCode() { return Collections.EMPTY_LIST.hashCode(); } public E get(int index) { Collections.EMPTY_LIST.get(index); return null; } public boolean contains(Object o) { return false; } public int indexOf(Object o) { return -1; } public int lastIndexOf(Object o) { return -1; } ListIterator<E> listIterator = new ListIterator<E>() { public boolean hasNext() { return false; } public E next() { throw new NoSuchElementException(); } public boolean hasPrevious() { return false; } public E previous() { throw new NoSuchElementException(); } public int nextIndex() { return 0; } public int previousIndex() { return -1; } public void remove() { throw new UnsupportedOperationException(); } public void set(E o) { throw new UnsupportedOperationException(); } public void add(E o) { throw new UnsupportedOperationException(); } }; public Iterator<E> iterator() { return listIterator; } public ListIterator<E> listIterator() { return listIterator; } public ListIterator<E> listIterator(int index) { return listIterator; } public List<E> subList(int fromIndex, int toIndex) { return Collections.<E>emptyList().subList(fromIndex, toIndex); } public Object[] toArray() { return Collections.EMPTY_LIST.toArray(); } public <T> T[] toArray(T[] a) { return Collections.<T>emptyList().toArray(a); } @Override public String toString() { return Collections.EMPTY_LIST.toString(); } public boolean add(E o) { throw new UnsupportedOperationException(); } public boolean remove(Object o) { throw new UnsupportedOperationException(); } public boolean containsAll(Collection<?> coll) { return false; } public boolean addAll(Collection<? extends E> coll) { throw new UnsupportedOperationException(); } public boolean removeAll(Collection<?> coll) { throw new UnsupportedOperationException(); } public boolean retainAll(Collection<?> coll) { throw new UnsupportedOperationException(); } public void clear() { throw new UnsupportedOperationException(); } public E set(int index, E element) { throw new UnsupportedOperationException(); } public void add(int index, E element) { throw new UnsupportedOperationException(); } public E remove(int index) { throw new UnsupportedOperationException(); } public boolean addAll(int index, Collection<? extends E> collection) { throw new UnsupportedOperationException(); } public void move(int newPosition, E o) { throw new UnsupportedOperationException(); } public E move(int newPosition, int oldPosition) { throw new UnsupportedOperationException(); } } private static class EmptyUnmodifiableEList extends BasicEmptyUnmodifiableEList<Object> implements EList<Object> { private EmptyUnmodifiableEList() { super(); } } private static class EmptyUnmodifiableEMap extends BasicEmptyUnmodifiableEList<Map.Entry<Object, Object>> implements EMap<Object, Object> { public boolean containsKey(Object key) { return false; } public boolean containsValue(Object value) { return false; } public Set<Map.Entry<Object, Object>> entrySet() { return Collections.emptySet(); } public Object get(Object key) { return null; } public int indexOfKey(Object key) { return -1; } public Set<Object> keySet() { return Collections.emptySet(); } public Map<Object, Object> map() { return Collections.emptyMap(); } public Collection<Object> values() { return Collections.emptyList(); } public Object put(Object key, Object value) { throw new UnsupportedOperationException(); } public void putAll(EMap<? extends Object, ? extends Object> map) { throw new UnsupportedOperationException(); } public void putAll(Map<? extends Object, ? extends Object> map) { throw new UnsupportedOperationException(); } public Object removeKey(Object key) { throw new UnsupportedOperationException(); } } /** * Returns an immutable list containing just the one object. * @return an immutable list containing just the one object. * @since 2.7 */ public static <T> EList<T> singletonEList(T o) { return new UnmodifiableEList<T>(Collections.singletonList(o)); } /** * Returns an immutable map containing just the one key/value mapping. * @return an immutable map containing just the one key/value mapping. * @since 2.7 */ public static <K, V> EMap<K, V> singletonEMap(K key, V value) { BasicEMap<K, V> result = new BasicEMap<K, V>(1); result.put(key, value); return new UnmodifiableEMap<K, V>(result); } }