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.alignment;
023
024import java.io.Serializable;
025import java.util.AbstractList;
026import java.util.ArrayList;
027import java.util.Collections;
028import java.util.Iterator;
029import java.util.LinkedHashMap;
030import java.util.List;
031import java.util.Map;
032import java.util.NoSuchElementException;
033import java.util.Set;
034
035import org.biojava.bio.BioError;
036import org.biojava.bio.symbol.AbstractSymbolList;
037import org.biojava.bio.symbol.Alphabet;
038import org.biojava.bio.symbol.AlphabetManager;
039import org.biojava.bio.symbol.IllegalSymbolException;
040import org.biojava.bio.symbol.Location;
041import org.biojava.bio.symbol.Symbol;
042import org.biojava.bio.symbol.SymbolList;
043
044/**
045 * A simple implementation of an Alignment.
046 * <p>
047 * This is a simple-stupid implementation that is made from a set of
048 * same-lengthed SymbolList objects each with an associated label. It does not
049 * handle differently lengthed sequences and doesn't contain any gap-editing
050 * concepts.
051 * 
052 * @author Matthew Pocock
053 * @author Greg Cox
054 * @author Nimesh Singh
055 */
056public class SimpleAlignment extends AbstractSymbolList implements Alignment,
057                Serializable {
058        private static final long serialVersionUID = -1760075176220928440L;
059
060        /**
061         * 
062         */
063        private LinkedHashMap<String, SymbolList> labelToSymbolList;
064        /**
065         * 
066         */
067        private List<String> labels;
068        /**
069         * 
070         */
071        private Alphabet alphabet;
072        /**
073         * 
074         */
075        private int length;
076        /**
077         * 
078         */
079        private int score;
080
081        /*
082         * (non-Javadoc)
083         * 
084         * @see java.lang.Object#finalize()
085         */
086        protected void finalize() throws Throwable {
087                super.finalize();
088                // System.err.println("Finalizing a SimpleAlignement");
089        }
090
091        /*
092         * (non-Javadoc)
093         * 
094         * @see org.biojava.bio.symbol.SymbolList#length()
095         */
096        public int length() {
097                return length;
098        }
099
100        /*
101         * (non-Javadoc)
102         * 
103         * @see org.biojava.bio.symbol.SymbolList#getAlphabet()
104         */
105        public Alphabet getAlphabet() {
106                return alphabet;
107        }
108
109        /*
110         * (non-Javadoc)
111         * 
112         * @see org.biojava.bio.symbol.SymbolList#symbolAt(int)
113         */
114        public Symbol symbolAt(int index) {
115                try {
116                        if (labels.size() == 1) {
117                                return symbolAt(labels.get(0), index);
118                        } else {
119                                return alphabet.getSymbol(new ColAsList<Symbol>(index));
120                        }
121                } catch (IllegalSymbolException ire) {
122                        throw new BioError(
123
124                        "Somehow my crossproduct alphabet is incompatible with column "
125                                        + index, ire);
126                }
127        }
128
129        /*
130         * (non-Javadoc)
131         * 
132         * @see org.biojava.bio.alignment.Alignment#getLabels()
133         */
134        public List<String> getLabels() {
135                return labels;
136        }
137
138        /*
139         * (non-Javadoc)
140         * 
141         * @see org.biojava.bio.alignment.Alignment#symbolAt(java.lang.Object, int)
142         */
143        public Symbol symbolAt(String label, int column) {
144                return symbolListForLabel(label).symbolAt(column);
145        }
146
147        /*
148         * (non-Javadoc)
149         * 
150         * @see org.biojava.bio.alignment.Alignment#subAlignment(java.util.Set,
151         * org.biojava.bio.symbol.Location)
152         */
153        public Alignment subAlignment(Set<String> labels, Location loc)
154                        throws NoSuchElementException {
155                Map<String, SymbolList> labelsToResList = new LinkedHashMap<String, SymbolList>();
156                Iterator<String> i;
157                if (labels != null) {
158                        i = labels.iterator();
159                } else {
160                        i = getLabels().iterator();
161                }
162                while (i.hasNext()) {
163                        String label = i.next();
164                        SymbolList sym = symbolListForLabel(label);
165                        if (loc != null) {
166                                sym = loc.symbols(sym);
167                        }
168                        labelsToResList.put(label, sym);
169                }
170                return new SimpleAlignment(labelsToResList);
171        }
172
173        /*
174         * (non-Javadoc)
175         * 
176         * @see
177         * org.biojava.bio.alignment.Alignment#symbolListForLabel(java.lang.String)
178         */
179        public SymbolList symbolListForLabel(String label)
180                        throws NoSuchElementException {
181                SymbolList rl = labelToSymbolList.get(label);
182                if (rl == null) {
183                        throw new NoSuchElementException(
184                                        "No symbol list associated with label " + label);
185                }
186                return rl;
187        }
188
189        /**
190         * Generate an alignment from a list of SymbolLists.
191         * <p>
192         * The SymbolLists must all be of the same length.
193         * 
194         * @param labelToResList
195         *            the label-to-symbol list mapping
196         * @throws IllegalArgumentException
197         *             if the SymbolLists are not the same length
198         */
199        public SimpleAlignment(Map<String, SymbolList> labelToResList)
200                        throws IllegalArgumentException {
201                if (labelToResList.isEmpty()) {
202                        throw new IllegalArgumentException(
203                                        "Can't create an alignment with no sequences");
204                }
205
206                this.labels = Collections.unmodifiableList(new ArrayList<String>(
207                                labelToResList.keySet()));
208                this.labelToSymbolList = new LinkedHashMap<String, SymbolList>(
209                                labelToResList);
210
211                int length = -1;
212                List<Alphabet> alphaList = new ArrayList<Alphabet>();
213                for (Iterator<String> li = labels.iterator(); li.hasNext();) {
214                        String label = li.next();
215                        try {
216                                SymbolList rl = symbolListForLabel(label);
217                                alphaList.add(rl.getAlphabet());
218                                if (length == -1) {
219                                        length = rl.length();
220                                } else {
221                                        if (rl.length() != length) {
222                                                StringBuffer sb = new StringBuffer();
223                                                for (Iterator<String> labI = labels.iterator(); labI
224                                                                .hasNext();) {
225                                                        String lab = labI.next();
226                                                        sb.append("\n\t" + lab + " ("
227                                                                        + symbolListForLabel(lab).length() + ")");
228                                                }
229                                                throw new IllegalArgumentException(
230                                                                "All SymbolLists must be the same length: "
231                                                                                + sb.substring(0));
232                                        }
233                                }
234                        } catch (NoSuchElementException nsee) {
235                                if (labelToSymbolList.containsKey(label)) {
236                                        throw new IllegalArgumentException(
237                                                        "The symbol list associated with " + label
238                                                                        + " is null");
239                                } else {
240                                        throw new BioError(
241                                                        "Something is screwey - map is lying about key/values",
242                                                        nsee);
243                                }
244                        }
245                }
246
247                this.alphabet = AlphabetManager.getCrossProductAlphabet(alphaList);
248                this.length = length;
249        }
250
251        /**
252         * 
253         */
254        public Iterator<SymbolList> symbolListIterator() {
255                return new Alignment.SymbolListIterator(this);
256        }
257
258        /**
259         * Makes a column of the alignment behave like a list.
260         * 
261         * @author Matthew Pocock
262         */
263        private final class ColAsList<T extends Symbol> extends AbstractList<T>
264                        implements Serializable {
265                /**
266                 * Generated serial version identifier.
267                 */
268                private static final long serialVersionUID = 8254702569039851040L;
269                private final int col;
270
271                public ColAsList(int col) {
272                        this.col = col;
273                }
274
275                // protected ColAsList() {
276                // this.col = 0;
277                // }
278
279                /*
280                 * (non-Javadoc)
281                 * 
282                 * @see java.util.AbstractList#get(int)
283                 */
284                @SuppressWarnings("unchecked")
285                public T get(int indx) {
286                        return (T) symbolAt(labels.get(indx), col);
287                }
288
289                /*
290                 * (non-Javadoc)
291                 * 
292                 * @see java.util.AbstractCollection#size()
293                 */
294                public int size() {
295                        return labels.size();
296                }
297        }
298
299        /**
300         * 
301         * @return
302         */
303        public int getScore() {
304                return score;
305        }
306
307        /**
308         * 
309         * @param score
310         */
311        public void setScore(int score) {
312                this.score = score;
313        }
314
315}