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 */
021
022package org.biojava.bio.symbol;
023
024/**
025 * Factory methods for constructing useful views onto SymbolLists.
026 *
027 * @author Thomas Down
028 * @author Matthew Pocock
029 * @since 1.1
030 */
031
032import java.util.ArrayList;
033import java.util.Collections;
034import java.util.Iterator;
035import java.util.List;
036import java.util.Map;
037import java.util.Set;
038
039import org.biojava.bio.alignment.Alignment;
040import org.biojava.bio.alignment.SimpleAlignment;
041import org.biojava.bio.seq.SequenceIterator;
042import org.biojava.utils.ChangeVetoException;
043import org.biojava.utils.Unchangeable;
044
045/**
046 * Tools class for constructing views of <code>SymbolList</code> objects.
047 *
048 * @since 1.2
049 */
050
051public final class SymbolListViews {
052  private SymbolListViews() {}
053
054    /**
055     * An n-th order view of another SymbolList.
056     * <p>
057     * In practice, what this means is that you can view a DNA sequence into an
058     * overlapping dinucleotide sequence without having to do any work yourself.
059     * </p>
060     *
061     * @param source The underlying SymbolList to view
062     * @param order The window size
063     */
064
065    public static SymbolList orderNSymbolList(SymbolList source, int order)
066        throws IllegalAlphabetException
067    {
068        if (order == 1)
069            return source;
070
071        return new OrderNSymbolList(source, order);
072    }
073
074    /**
075     * A view of windows onto another SymbolList.
076     * <p>
077     * In practice, what this means is that you can view a DNA sequence as codons which
078     * do not overlap.
079     * </p>
080     *
081     * @param source The underlying SymbolList to view
082     * @param wsize The window size.
083     * @throws IllegalArgumentException if the symbollist length isn't an integer multiple of wsize.
084     */
085
086    public static SymbolList windowedSymbolList(SymbolList source, int wsize)
087        throws IllegalArgumentException
088    {
089        return new WindowedSymbolList(source, wsize);
090    }
091
092    /**
093     * A reversed view onto a SymbolList.
094     *
095     * @param symbols the SymbolList to reverse.
096     */
097
098    public static SymbolList reverse(SymbolList symbols) {
099        return new ReverseSymbolList(symbols);
100    }
101
102    /**
103     * Provides a 'translated' view of an underlying SymbolList.
104     * <p>
105     * This method allows you to translate from one alphabet into another, so
106     * for example, you could translate from DNA-triplets into amino-acids. You
107     * could also translate from DNA-dinucleotide into the 'twist' structural
108     * metric, or any other translation that takes your fancy.
109     * </p>
110     * <p>
111     * The actual mapping from source to view Symbol is encapsulated in a
112     * TranslationTable object.
113     * </p>
114     * <p>
115     * The translated SymbolList will be the same length as the source, and each
116     * Symbol in the view will correspond to a single Symbol in the source.
117     * </p>
118     *
119     * @param symbols a SymbolList to translate.
120     * @param table a translation table for mapping symbols.
121     */
122
123    public static SymbolList translate(SymbolList symbols,
124                                TranslationTable table)
125        throws IllegalAlphabetException
126    {
127        return new TranslatedSymbolList(symbols, table);
128    }
129
130    /**
131     * Construct an alignment of the SymbolLists contained in the values collection
132     * of <code>labelToSymList</code>.
133     *
134     * @param labelToSymList A Map containing label -> SymbolList mappings
135     */
136
137    public static Alignment alignment(Map labelToSymList)
138    throws IllegalArgumentException {
139      return new SimpleAlignment(labelToSymList);
140    }
141
142    /**
143     * View a SymbolList over a cross-product Alphabet as an Alignment.
144     *
145     * @param labels a List of labels, which should be the same length
146     *               as the order <code>symList</code>'s Alphabet.
147     * @param symList a SymbolList over a cross-product alphabet.
148     */
149
150
151    public static Alignment alignment(List labels, SymbolList symList)
152    throws IllegalArgumentException {
153      return new SymListAsAlignment(labels, symList);
154    }
155
156    /**
157     * View a portion of a SymbolList.  Unlike SymbolList.subList, this
158     * method is guarenteed to return a view, which will change when
159     * the underlying SymbolList is modified.
160     *
161     * @param parent the SymbolList to view
162     * @param start the first index to include in the view
163     * @param end the last index to include in the view
164     * @throws IllegalArgumentException if the start or end points fall outside the parent SymbolList.
165     * @since 1.4
166     */
167
168    public static SymbolList subList(SymbolList parent, int start, int end)
169        throws IllegalArgumentException
170    {
171        if (start < 1 || end > parent.length()) {
172            throw new IndexOutOfBoundsException(
173                "Sublist index out of bounds " + parent.length() + ":" + start + "," + end
174            );
175        }
176
177        if (end < start) {
178            throw new IllegalArgumentException(
179                "end must not be lower than start: start=" + start + ", end=" + end
180            );
181        }
182        return new SubList(parent, start, end);
183    }
184
185  /**
186   * Get a new immutable, empty symbol list with the given alphabet.
187   *
188   * @since 1.4
189   * @param alpha   the Alphabet this symbol list is over
190   * @return  a new empty SymbolList
191   */
192  public static SymbolList emptyList(Alphabet alpha)
193  {
194    return new EmptySymbolList(alpha);
195  }
196
197    private static class SymListAsAlignment
198    extends Unchangeable
199    implements Alignment {
200      private final SymbolList symList;
201      private final List<String> labels;
202
203      public SymListAsAlignment(List<String> labels, SymbolList symList) {
204        if(labels.size() != symList.getAlphabet().getAlphabets().size()) {
205          throw new IllegalArgumentException("There must be one label per symbol list");
206        }
207
208        this.labels = Collections.unmodifiableList(new ArrayList<String>(labels));
209        this.symList = symList;
210      }
211
212      public List<String> getLabels() {
213        return labels;
214      }
215
216      public SequenceIterator sequenceIterator() {
217        throw new UnsupportedOperationException("This method sucks");
218      }
219
220      public Iterator symbolListIterator() {
221        return new Alignment.SymbolListIterator(this);
222      }
223
224      public Symbol symbolAt(String label, int column) {
225        BasisSymbol sym = (BasisSymbol) symList.symbolAt(column);
226        return (Symbol) sym.getSymbols().get(labels.indexOf(label));
227      }
228
229      public SymbolList symbolListForLabel(String label) {
230        return new IndexedSymbolList(symList, labels.indexOf(label));
231      }
232
233      public Alphabet getAlphabet() {
234        return symList.getAlphabet();
235      }
236
237      public Iterator iterator() {
238        return symList.iterator();
239      }
240
241      public int length() {
242        return symList.length();
243      }
244
245      public String seqString() {
246        return symList.seqString();
247      }
248
249      public SymbolList subList(int start, int end)
250      throws IndexOutOfBoundsException {
251        return symList.subList(start, end);
252      }
253
254      public String subStr(int start, int end)
255      throws IndexOutOfBoundsException {
256        return symList.subStr(start, end);
257      }
258
259      public void edit(Edit edit)
260      throws
261        IndexOutOfBoundsException,
262        IllegalAlphabetException,
263        ChangeVetoException
264      {
265        symList.edit(edit);
266      }
267
268      public List toList() {
269        return symList.toList();
270      }
271
272      public Symbol symbolAt(int indx)
273      throws IndexOutOfBoundsException {
274        return symList.symbolAt(indx);
275      }
276
277      public Alignment subAlignment(Set labels, Location loc) {
278        throw new UnsupportedOperationException("Fixme: this needs to be implemented");
279      }
280    }
281
282    private static class IndexedSymbolList
283    extends AbstractSymbolList {
284      private final int indx;
285      private final SymbolList symList;
286
287      public IndexedSymbolList(SymbolList symList, int indx)
288      throws IllegalArgumentException {
289        if(indx >= symList.getAlphabet().getAlphabets().size()) {
290          throw new IllegalArgumentException("index too high");
291        }
292
293        this.indx = indx;
294        this.symList = symList;
295      }
296
297      public Alphabet getAlphabet() {
298        return (Alphabet) symList.getAlphabet().getAlphabets().get(indx);
299      }
300
301      public int length() {
302        return symList.length();
303      }
304
305      public Symbol symbolAt(int indx)
306      throws IndexOutOfBoundsException {
307        return (Symbol) ((BasisSymbol) symList.symbolAt(indx)).getSymbols().get(this.indx);
308      }
309    }
310}