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    }