All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.emf.common.util.ECollections Maven / Gradle / Ivy

/**
 * Copyright (c) 2002-2012 IBM Corporation and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v20.html
 * 
 * Contributors: 
 *   IBM - Initial API and implementation
 */
package org.eclipse.emf.common.util;


import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.AbstractList;
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;

import org.eclipse.emf.common.util.BasicEList.BasicIndexOutOfBoundsException;


/**
 * Support for {@link #EMPTY_ELIST empty} and {@link #unmodifiableEList unmodifiable} ELists.
 */
public class ECollections
{
  // Suppress default constructor for noninstantiability.
  private ECollections()
  {
    super();
  }
  
  /**
   * Moves the object to the new position, if is in the list.
   * @param newPosition the position of the object after the move.
   * @param object the object to move.
   */
  public static  void move(List list, int newPosition, T object)
  {
    if (list instanceof EList)
    {
      ((EList)list).move(newPosition, object);
    }
    else
    {
      list.remove(object);
      list.add(newPosition, object);
    }
  }

  /**
   * Moves the object from the old position to the new position.
   * @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 move(List list, int targetIndex, int sourceIndex)
  {
    if (list instanceof EList)
    {
      return ((EList)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 == and
   * the equals method. 
   * @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 -1 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  void sort(EList list, Comparator comparator)
  {
    Object[] listAsArray = list.toArray();
    @SuppressWarnings("unchecked") Comparator objectComparator = (Comparator)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 eList's contents and order to be exactly that of the prototype 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 eList's contents and order are already exactly that of the prototype list,
   * no change will be made.
   * @param eList the list to set.
   * @param prototypeList the list representing the desired content and order.
   */
  public static  void setEList(EList eList, List 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  EList unmodifiableEList(EList list)
  {
    return new UnmodifiableEList(list);
  }

  /**
   * Returns an unmodifiable view of the map.
   * @return an unmodifiable view of the map.
   */
  public static  EMap unmodifiableEMap(EMap map)
  {
    return new UnmodifiableEMap(map);
  }

  /**
   * An unmodifiable empty list with an efficient reusable iterator.
   */
  public static final EList EMPTY_ELIST = new EmptyUnmodifiableEList();
  
  /**
   * Returns an empty unmodifiable list.
   * @return an empty unmodifiable list.
   */
  @SuppressWarnings("unchecked")
  public static  EList emptyEList()
  {
    return (EList)EMPTY_ELIST;
  }

  /**
   * An unmodifiable empty map with an efficient reusable iterator.
   */
  public static final EMap EMPTY_EMAP = new EmptyUnmodifiableEMap();
  
  /**
   * Returns an empty unmodifiable map.
   * @return an empty unmodifiable map.
   */
  @SuppressWarnings("unchecked")
  public static  EMap emptyEMap()
  {
    return (EMap)EMPTY_EMAP;
  }

  private static class UnmodifiableEList implements EList
  {
    protected List list;

    public UnmodifiableEList(List 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[] toArray(T[] a)
    {
      return list.toArray(a);
    }

    @Override
    public String toString()
    {
      return list.toString();
    }

    public Iterator iterator()
    {
      return 
        new Iterator()
        {
          Iterator 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 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 collection)
    {
      throw new UnsupportedOperationException();
    }

    public ListIterator listIterator()
    {
      return listIterator(0);
    }

    public ListIterator listIterator(final int index)
    {
      return 
        new ListIterator()
        {
          ListIterator 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 subList(int fromIndex, int toIndex)
    {
      return new UnmodifiableEList(new BasicEList(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 extends UnmodifiableEList> implements EMap
  {
    protected EMap eMap;
    
    @SuppressWarnings("unchecked")
    public UnmodifiableEMap(EMap eMap)
    {
      super((EMap)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> entrySet()
    {
      return Collections.unmodifiableSet((Set>)(Set)eMap.entrySet());
    }

    public V get(Object key)
    {
      return eMap.get(key);
    }

    public int indexOfKey(Object key)
    {
      return eMap.indexOf(key);
    }

    public Set keySet()
    {
      return Collections.unmodifiableSet(eMap.keySet());
    }

    public Map map()
    {
      return Collections.unmodifiableMap(eMap.map());
    }
    
    public Collection values()
    {
      return Collections.unmodifiableCollection(eMap.values());
    }
    
    public V put(K key, V value)
    {
      throw new UnsupportedOperationException();
    }

    public void putAll(EMap map)
    {
      throw new UnsupportedOperationException();
    }

    public void putAll(Map map)
    {
      throw new UnsupportedOperationException();
    }

    public V removeKey(Object key)
    {
      throw new UnsupportedOperationException();
    }
  }
  
  private static class BasicEmptyUnmodifiableEList
  {
    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 listIterator = 
      new ListIterator()
      {
        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 iterator()
    {
      return listIterator;
    }

    public ListIterator listIterator()
    {
      return listIterator;
    }

    public ListIterator listIterator(int index)
    {
      return listIterator;
    }

    public List subList(int fromIndex, int toIndex)
    {
      return Collections.emptyList().subList(fromIndex, toIndex);
    }

    public Object[] toArray()
    {
      return Collections.EMPTY_LIST.toArray();
    }

    public  T[] toArray(T[] a)
    {
      return Collections.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 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 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 implements EList
  {
    private EmptyUnmodifiableEList()
    {
      super();
    }
  }

  private static class EmptyUnmodifiableEMap extends BasicEmptyUnmodifiableEList> implements EMap
  {
    public boolean containsKey(Object key)
    {
      return false;
    }

    public boolean containsValue(Object value)
    {
      return false;
    }

    public Set> entrySet()
    {
      return Collections.emptySet();
    }

    public Object get(Object key)
    {
      return null;
    }

    public int indexOfKey(Object key)
    {
      return -1;
    }

    public Set keySet()
    {
      return Collections.emptySet();
    }

    public Map map()
    {
      return Collections.emptyMap();
    }
    
    public Collection values()
    {
      return Collections.emptyList();
    }
    
    public Object put(Object key, Object value)
    {
      throw new UnsupportedOperationException();
    }

    public void putAll(EMap map)
    {
      throw new UnsupportedOperationException();
    }

    public void putAll(Map 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  EList singletonEList(T o)
  {
    return new UnmodifiableEList(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  EMap singletonEMap(K key, V value)
  {
    BasicEMap result = new BasicEMap(1);
    result.put(key, value);
    return new UnmodifiableEMap(result);
  }
  
  /**
   * Returns a mutable list containing the elements of the given iterator.
   * @return a mutable list containing the same elements as the given iterator.
   * @since 2.9
   */
  public static  EList toEList(Iterator iterator)
  {
    return ECollections.newBasicEList(iterator);
  }

  /**
   * Returns a list containing the elements of the given iterable.
   * If the iterable is of type {@link EList}, that list itself is returned.
   * If the iterable is of type {@link List}, a {@link ECollections#asEList(List) view} of that list is returned;
   * all changes to view are reflected in the underlying list and all changes to the underlying list are reflected in the view.
   * In all other cases, the result is a {@link ECollections#newBasicEList(Iterable) copy} of the iterable.
   * @return a list containing the same elements as the given iterable.
   * @since 2.9
   */
  public static  EList toEList(Iterable iterable)
  {
    if (iterable instanceof EList)
    {
      @SuppressWarnings("unchecked")
      EList result = (EList)iterable;
      return result;
    }
    else if (iterable instanceof List)
    {
      @SuppressWarnings("unchecked")
      final List list = (List)iterable;
      return
        new DelegatingEList()
        {
          private static final long serialVersionUID = 1L;

          @Override
          protected List delegateList()
          {
            return list;
          }
        };
    }
    else
    {
      return ECollections.newBasicEList(iterable);
    }
  }

  /**
   * Returns a mutable EMap view of the specified map.
   * {@link EList#move(int, int)}, {@link EList#move(int, Object)}, {@link List#add(int, Object)}, and {@link List#addAll(int, Collection)},
   * i.e., the methods that expect to control the exact order of entries in the map's entry set,
   * are not supported and throw {@link UnsupportedOperationException}.
   * All other changes to the EMap write through to the underlying map.
   * @param map the map to which the EMap delegates.
   * @return an EMap view of the specified map.
   * @since 2.9
   */
  public static  EMap asEMap(final Map map)
  {
    return 
      new EMap()
      {
        public void move(int newPosition, Map.Entry object)
        {
          throw new UnsupportedOperationException();
        }

        public Map.Entry move(int newPosition, int oldPosition)
        {
          throw new UnsupportedOperationException();
        }
        
        public void add(int index, Map.Entry element)
        {
          throw new UnsupportedOperationException();
        }

        public boolean addAll(int index, Collection> c)
        {
          throw new UnsupportedOperationException();
        }

        public int size()
        {
          return map.size();
        }

        public boolean isEmpty()
        {
          return map.isEmpty();
        }

        public boolean contains(Object o)
        {
          return map.entrySet().contains(o) || map.containsKey(o);
        }

        public Iterator> iterator()
        {
          return map.entrySet().iterator();
        }

        public Object[] toArray()
        {
          return map.entrySet().toArray();
        }

        public  T[] toArray(T[] a)
        {
          return map.entrySet().toArray(a);
        }

        public boolean add(Map.Entry e)
        {
          K key = e.getKey();
          boolean result = map.containsKey(key);
          map.put(key,  e.getValue());
          return result;
        }

        public boolean remove(Object o)
        {
          if (o instanceof Map.Entry)
          {
            Map.Entry entry = (Map.Entry)o;
            Object key = entry.getKey();
            if (map.containsKey(key))
            {
              map.remove(key);
              return true;
            }
          }
          if (map.containsKey(o))
          {
            map.remove(o);
            return true;
          }
          return false;
        }

        public boolean containsAll(Collection c)
        {
          return map.entrySet().containsAll(c);
        }

        public boolean addAll(Collection> c)
        {
          boolean result = false;
          for (Map.Entry entry : c)
          {
            if (add(entry))
            {
              result = true;
            }
          }
          return result;
        }

        public boolean removeAll(Collection c)
        {
          boolean result = false;
          for (Object o : c)
          {
            if (remove(o))
            {
              result = true;
            }
          }
          return result;
        }

        public boolean retainAll(Collection c)
        {
          return listView().retainAll(c);
        }

        public void clear()
        {
          map.clear();
        }

        protected void rangeCheck(int index)
        {
          int size = map.size();
          if (index >= size || index < 0)
            throw new BasicIndexOutOfBoundsException(index, size);
        }

        public Map.Entry get(int index)
        {
          rangeCheck(index);
          int i = 0;
          for (Map.Entry entry : map.entrySet())
          {
            if (i++ == index)
            {
              return entry;
            }
          }
          return null;
        }

        public Map.Entry set(int index, Map.Entry element)
        {
          rangeCheck(index);
          int i = 0;
          for (Map.Entry entry : map.entrySet())
          {
            if (i++ == index)
            {
              map.remove(entry.getKey());
              return entry;
            }
          }
          return null;
        }

        public Map.Entry remove(int index)
        {
          rangeCheck(index);
          int i = 0;
          for (Map.Entry entry : map.entrySet())
          {
            if (i++ == index)
            {
              map.remove(entry.getKey());
              return entry;
            }
          }
          return null;
        }

        public int indexOf(Object o)
        {
          int i = 0;
          for (Map.Entry entry : map.entrySet())
          {
            if (entry.equals(o))
            {
              return i;
            }
          }
          return -1;
        }

        public int lastIndexOf(Object o)
        {
          return indexOf(o);
        }

        protected List> listView;
        
        protected Map.Entry basicGet(int index)
        {
          return get(index);
        }
        
        protected List> listView()
        {
          if (listView == null)
          {
            listView =
              new AbstractList>()
              {
                @Override
                public Map.Entry get(int index)
                {
                  return basicGet(index);
                }

                @Override
                public int size()
                {
                  return map.size();
                }
              };
          }
          return listView;
        }
        
        public ListIterator> listIterator()
        {
          return listView().listIterator();
        }

        public ListIterator> listIterator(int index)
        {
          return listView().listIterator(index);
        }

        public List> subList(int fromIndex, int toIndex)
        {
          return listView().subList(fromIndex, toIndex);
        }

        public V get(Object key)
        {
          return map.get(key);
        }

        public V put(K key, V value)
        {
          return map.put(key, value);
        }

        public void putAll(Map m)
        {
          map.putAll(m);
        }

        public void putAll(EMap m)
        {
          map.putAll(m.map());
        }

        public int indexOfKey(Object key)
        {
          int i = 0;
          for (Map.Entry entry : map.entrySet())
          {
            if (key == null ? entry.getKey() == null : key.equals(entry.getKey()))
            {
              return i;
            }
          }
          return -1;
        }

        public boolean containsKey(Object key)
        {
          return map.containsKey(key);
        }

        public boolean containsValue(Object value)
        {
          return map.containsValue(value);
        }

        public V removeKey(Object key)
        {
          return map.remove(key);
        }

        public Map map()
        {
          return map;
        }

        public Set> entrySet()
        {
          return map.entrySet();
        }

        public Set keySet()
        {
          return map.keySet();
        }

        public Collection values()
        {
          return map.values();
        }

        @Override
        public boolean equals(Object object)
        {
          if (object instanceof List)
          {
            return listView().equals(object);
          }
          else
          {
            return false;
          }
        }

        @Override
        public int hashCode()
        {
          return listView().hashCode();
        }

        @Override
        public String toString()
        {
          return map.toString();
        }
      };
  }

  /**
   * Returns an EList view of the specified list.
   * All changes to the EList write through to the underlying list.
   * @param list the list to which the EList delegates.
   * @return an EList view of the specified list.
   * @since 2.9
   */
  public static  EList asEList(final List list)
  {
    return
      new DelegatingEList()
      {
        private static final long serialVersionUID = 1L;

        @Override
        protected List delegateList()
        {
          return list;
        }
      };
  }

  /**
   * Returns a mutable, fixed-size, random access EList backed by the given array.
   * Changes to the list, i.e., set and move, write through to the array.
   * All other list modifying operations throw {@link UnsupportedOperationException}.
   * This is analogous to {@link Arrays#asList(Object...)} with the advantage that you can {@link EList#move(int, int) move} objects;
   * hence you can {@link #sort(EList) sort} without using {@link List#add(int, Object)} and {@link List#remove(int)}.
   * @param elements the array to which the EList delegates.
   * @return an EList view of the specified array.
   * @since 2.9
   */
  @SafeVarargs
  public static  EList asEList(final T...elements)
  {
    return
      new BasicEList()
      {
        private static final long serialVersionUID = 1L;

        {
          this.data = elements;
          size = elements.length;
        }

        @Override
        public void setData(int size, Object[] data)
        {
          throw new UnsupportedOperationException();
        }

        @Override
        public void grow(int minimumCapacity)
        {
          throw new UnsupportedOperationException();
        }

        @Override
        public void shrink()
        {
          throw new UnsupportedOperationException();
        }

        @Override
        public T remove(int index)
        {
          throw new UnsupportedOperationException();
        }

        @Override
        public boolean remove(Object object)
        {
          throw new UnsupportedOperationException();
        }

        @Override
        public boolean removeAll(Collection collection)
        {
          throw new UnsupportedOperationException();
        }

        @Override
        public void clear()
        {
          throw new UnsupportedOperationException();
        }
      };
  }

  /**
   * Returns an unmodifiable view of the list.
   * @return an unmodifiable view of the list.
   * @since 2.9
   */
  public static  EList unmodifiableEList(List list)
  {
    return new UnmodifiableEList(list);
  }

  /**
   * Creates an empty mutable {@link BasicEList}.
   * @return an empty mutable {@link BasicEList}.
   * @since 2.9
   */
  public static  BasicEList newBasicEList()
  {
    return new BasicEList();
  }

  /**
   * Creates an empty mutable {@link BasicEList} with the given capacity.
   * @return an empty mutable {@link BasicEList}.
   * @since 2.9
   */
  public static  BasicEList newBasicEListWithCapacity(int capacity)
  {
    return new BasicEList(capacity);
  }

  /**
   * Creates an empty mutable {@link BasicEList} with a capacity large enough to hold a bit more than the estimated number of elements.
   * If you know the exact size, use {@link #newBasicEListWithCapacity(int)} instead.
   * @return an empty mutable {@link BasicEList}.
   * @since 2.9
   */
  public static  BasicEList newBasicEListWithExpectedSize(int estimatedSize)
  {
    BasicEList result = new BasicEList();
    result.grow(estimatedSize);
    return result;
  }

  /**
   * Creates a mutable {@link BasicEList} containing the given elements.
   * @return a mutable {@link BasicEList} containing the given elements.
   * @since 2.9
   */
  @SafeVarargs
  public static  BasicEList newBasicEList(T... elements)
  {
    BasicEList result = new BasicEList(elements.length);
    System.arraycopy(elements, 0, result.data, 0, elements.length);
    result.size = elements.length;
    return result;
  }

  /**
   * Creates a mutable {@link BasicEList} containing the given elements.
   * @return a mutable {@link BasicEList} containing the given elements.
   * @since 2.9
   */
  public static  BasicEList newBasicEList(Iterator iterator)
  {
    BasicEList result = new BasicEList();
    while (iterator.hasNext())
    {
      result.add(iterator.next());
    }
    return result;
  }

  /**
   * Creates a mutable {@link BasicEList} containing the given elements.
   * @return a mutable {@link BasicEList} containing the given elements.
   * @since 2.9
   */
  public static  BasicEList newBasicEList(Iterable iterable)
  {
    if (iterable instanceof Collection)
    {
      return new BasicEList((Collection)iterable);
    }
    else
    {
      BasicEList result = new BasicEList();
      for (T t : iterable)
      {
        result.add(t);
      }
      return result;
    }
  }

  /**
   * Returns a synchronized, i.e., thread-safe view of the interning set.
   * The result is serializeable if the argument is serializeable.
   * @param set the interning set to which this interning set delegates.
   * @return a synchronized view of the interning set.
   * @since 2.9
   */
  public static  InterningSet synchronizedInterningSet(InterningSet set)
  {
    return new SynchronizedInterningSet(set);
  }

  private static final class SynchronizedInterningSet implements InterningSet, Serializable
  {
    private static final long serialVersionUID = 1L;

    private InterningSet set;

    public SynchronizedInterningSet(InterningSet set)
    {
      this.set = set;
    }

    public synchronized E intern(E object)
    {
      return set.intern(object);
    }

    public synchronized E get(E object)
    {
      return set.get(object);
    }

    public synchronized int size()
    {
      return set.size();
    }

    public synchronized boolean isEmpty()
    {
      return set.isEmpty();
    }

    public synchronized boolean contains(Object o)
    {
      return set.contains(o);
    }

    public synchronized Iterator iterator()
    {
      return set.iterator();
    }

    public synchronized Object[] toArray()
    {
      return set.toArray();
    }

    public synchronized  T[] toArray(T[] a)
    {
      return set.toArray(a);
    }

    public synchronized boolean add(E e)
    {
      return set.add(e);
    }

    public synchronized boolean remove(Object o)
    {
      return set.remove(o);
    }

    public synchronized boolean containsAll(Collection c)
    {
      return set.containsAll(c);
    }

    public synchronized boolean addAll(Collection c)
    {
      return set.addAll(c);
    }

    public synchronized boolean retainAll(Collection c)
    {
      return set.retainAll(c);
    }

    public synchronized boolean removeAll(Collection c)
    {
      return set.removeAll(c);
    }

    public synchronized void clear()
    {
      set.clear();
    }

    @Override
    public synchronized boolean equals(Object o)
    {
      return set.equals(o);
    }

    @Override
    public synchronized int hashCode()
    {
      return set.hashCode();
    }

    @Override
    public synchronized String toString()
    {
      return set.toString();
    }

    private synchronized void writeObject(ObjectOutputStream out) throws IOException
    {
      out.defaultWriteObject();
    }
  }
}