001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021package org.biojava.utils;
022
023import java.io.Serializable;
024import java.util.AbstractList;
025import java.util.AbstractMap;
026import java.util.AbstractSet;
027import java.util.ArrayList;
028import java.util.Arrays;
029import java.util.Collections;
030import java.util.Iterator;
031import java.util.List;
032import java.util.Map;
033import java.util.Set;
034
035/**
036 * @author Matthew Pocock
037 * @author Thomas Down
038 * @author Mark Schreiber
039 */
040public class ListTools implements Serializable{
041  public static Iterator nonRemoveIterator(Iterator i) {
042    final Iterator it = i;
043    return new Iterator() {
044      public boolean hasNext() {
045        return it.hasNext();
046      }
047      public Object next() {
048        return it.next();
049      }
050      public void remove() {
051        throw new UnsupportedOperationException();
052      }
053    };
054  }
055
056  public static List createList(List l) {
057    switch (l.size()) {
058      case 0:
059        return Collections.EMPTY_LIST;
060      case 1:
061        return Collections.nCopies(1, l.get(0));
062      case 2:
063        Doublet d = new Doublet(l.get(0), l.get(1));
064        return d;
065      case 3:
066        Triplet t = new Triplet(l.get(0), l.get(1), l.get(2));
067        return t;
068      default:
069        return new ArrayList(l);
070    }
071  }
072
073  public static List createList(Object[] a) {
074    switch (a.length) {
075      case 0:
076        return Collections.EMPTY_LIST;
077      case 1:
078        return Collections.nCopies(1, a[0]);
079      case 2:
080        Doublet d = new Doublet(a[0],a[1]);
081        return d;
082      case 3:
083        Triplet t = new Triplet(a[0],a[1],a[2]);
084        return t;
085      default:
086        return Arrays.asList(a);
087    }
088  }
089
090  /**
091   * Create a new SeriesList with the given leader, trailer and size.
092   *
093   * @param leader  the String that will prefix the index
094   * @param trailer the String that will suffix the index
095   * @param size  the length of the list
096   * @throws NullPointerException if leader or trailer are null (use the empty
097   *   string instead)
098   * @throws IllegalArgumentException if the size is negative
099   */
100  public static SeriesList createSeriesList(
101    String leader,
102    String trailer,
103    int size
104  ) {
105    return new SeriesList(leader, trailer, size);
106  }
107
108  public static class Doublet extends AbstractList implements Serializable {
109    private Object a;
110    private Object b;
111
112    public Doublet() {}
113    public Doublet(Object a, Object b) {
114      this();
115      set(a, b);
116    }
117
118    public void set(Object a, Object b) {
119      this.a = a;
120      this.b = b;
121    }
122
123    public void setA(Object a) {
124      this.a = a;
125    }
126
127    public void setB(Object b) {
128      this.b = b;
129    }
130
131    public Object getA() {
132      return a;
133    }
134
135    public Object getB() {
136      return b;
137    }
138
139    public int size() {
140      return 2;
141    }
142
143    public Object get(int indx) {
144      switch (indx) {
145        case 0:
146          return a;
147        case 1:
148          return b;
149        default:
150          throw new IndexOutOfBoundsException("indx must be 0 or 1");
151      }
152    }
153
154    public Iterator getIterator() {
155      return new Iterator() {
156        private int indx = 0;
157
158        public boolean hasNext() {
159          return indx < 2;
160        }
161
162        public Object next() {
163          return get(indx++);
164        }
165
166        public void remove()
167        throws UnsupportedOperationException {
168          throw new UnsupportedOperationException();
169        }
170      };
171    }
172
173    public int hashCode() {
174      int hashcode = 1;
175      hashcode = 31*hashcode + a.hashCode();
176      hashcode = 31*hashcode + b.hashCode();
177      return hashcode;
178    }
179
180    public boolean equals(Object o) {
181      if(! (o instanceof List) ) {
182        return false;
183      }
184
185      List other = (List) o;
186      if(other.size() != 2) {
187        return false;
188      }
189
190      return other.get(0).equals(a) && other.get(1).equals(b);
191    }
192  }
193
194  public static class Triplet extends AbstractList implements Serializable {
195    private Object a;
196    private Object b;
197    private Object c;
198
199    public Triplet() {}
200    public Triplet(Object a, Object b, Object c) {
201      this();
202      set(a, b, c);
203    }
204
205    public void set(Object a, Object b, Object c) {
206      this.a = a;
207      this.b = b;
208      this.c = c;
209    }
210
211    public void setA(Object a) {
212      this.a = a;
213    }
214
215    public void setB(Object b) {
216      this.b = b;
217    }
218
219    public void setC(Object c) {
220      this.c = c;
221    }
222
223    public Object getA() {
224      return a;
225    }
226
227    public Object getB() {
228      return b;
229    }
230
231    public Object getC() {
232      return c;
233    }
234
235    public int size() {
236      return 3;
237    }
238
239    public Object get(int indx) {
240      switch (indx) {
241        case 0:
242          return a;
243        case 1:
244          return b;
245        case 2:
246          return c;
247        default:
248          throw new IndexOutOfBoundsException("indx must be 0 or 1");
249      }
250    }
251
252    public Iterator getIterator() {
253      return new Iterator() {
254        private int indx = 0;
255
256        public boolean hasNext() {
257          return indx < 3;
258        }
259
260        public Object next() {
261          return get(indx++);
262        }
263
264        public void remove()
265        throws UnsupportedOperationException {
266          throw new UnsupportedOperationException();
267        }
268      };
269    }
270
271    public int hashCode() {
272      int hashcode = 1;
273      hashcode = 31*hashcode + a.hashCode();
274      hashcode = 31*hashcode + b.hashCode();
275      hashcode = 31*hashcode + c.hashCode();
276      return hashcode;
277    }
278
279    public boolean equals(Object o) {
280      if(! (o instanceof List) ) {
281        return false;
282      }
283
284      List other = (List) o;
285      if(other.size() != 3) {
286        return false;
287      }
288
289      return other.get(0).equals(a) && other.get(1).equals(b) && other.get(2).equals(c);
290    }
291  }
292
293  /**
294   * A list that represents a series of values.
295   *
296   * <p>This provides a simple list implementation that synthesises elements from
297   * a leading and trailing string and the index into the list.</p>
298   *
299   * <p>For example, a SeriesList with leader "" and trailer ":" will contain
300   * values like "0:", "1:", "2:" and so on. A SeriesList with leader "Chapter "
301   * and trailer "" will have values like "Chapter 5".</p>
302   *
303   * @author Matthew Pocock
304   * @since 1.4
305   */
306  public static class SeriesList
307  extends AbstractList {
308    private final String leader;
309    private final String trailer;
310    private final int size;
311
312    private SeriesList(String leader, String trailer, int size) {
313      if(leader == null) {
314        throw new NullPointerException(
315        "Leader was null. Use the empty string instead");
316      }
317
318      if(trailer == null) {
319        throw new NullPointerException(
320        "Trailer was null. Use the empty string instead");
321      }
322
323      if(size < 0) {
324        throw new IllegalArgumentException(
325          "Size must be zero or positive: " + size );
326      }
327
328      this.leader = leader;
329      this.trailer = trailer;
330      this.size = size;
331    }
332
333    public String getLeader() {
334      return leader;
335    }
336
337    public String getTrailer() {
338      return trailer;
339    }
340
341    public int size() {
342      return size;
343    }
344
345    public Object get(int indx) {
346      return leader + indx + trailer;
347    }
348  }
349
350  public static List mapList(final List list,
351                             final Mapper mapper)
352  {
353    return new AbstractList() {
354      public Object get(int index)
355      {
356        return mapper.map(list.get(index));
357      }
358
359      public int size()
360      {
361        return list.size();
362      }
363    };
364  }
365
366  public static Set mapSet(final Set set,
367                           final Mapper mapper)
368  {
369    return new AbstractSet() {
370      public Iterator iterator()
371      {
372        return new Iterator() {
373          Iterator i = set.iterator();
374          public boolean hasNext()
375          {
376            return i.hasNext();
377          }
378
379          public Object next()
380          {
381            return mapper.map(i.next());
382          }
383
384          public void remove()
385          {
386            i.remove();
387          }
388        };
389      }
390
391      public int size()
392      {
393        return set.size();
394      }
395    };
396  }
397
398  public static Map mapMap(final Map map,
399                           final Mapper keyMapper,
400                           final Mapper valMapper)
401  {
402    return new AbstractMap() {
403      public Set entrySet()
404      {
405        return mapSet(map.entrySet(), new Mapper() {
406          public Object map(Object val)
407          {
408            final Map.Entry ent = (Map.Entry) val;
409            return new Map.Entry() {
410              public Object getKey()
411              {
412                return keyMapper.map(ent.getKey());
413              }
414
415              public Object getValue()
416              {
417                return valMapper.map(ent.getValue());
418              }
419
420              public Object setValue(Object value)
421              {
422                throw new UnsupportedOperationException();
423              }
424            };
425          }
426        });
427      }
428    };
429  }
430
431  /**
432   * Maps one object to another.
433   *
434   * @author Matthew Pocock
435   * @since 1.4
436   */
437  public interface Mapper {
438    /**
439     * Map the object.
440     *
441     * @param val   the object to map
442     * @return      the new value
443     */
444    public Object map(Object val);
445  }
446
447  public static final Mapper NULL_MAPPER = new Mapper() {
448    public Object map(Object val)
449    {
450      return val;
451    }
452  };
453}