Formatting
[jalview.git] / src / jalview / datamodel / Alignment.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer
3  * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.datamodel;
20
21 import java.util.*;
22
23 import jalview.analysis.*;
24
25 /** Data structure to hold and manipulate a multiple sequence alignment
26  */
27 public class Alignment
28     implements AlignmentI
29 {
30   protected Alignment dataset;
31   protected Vector sequences;
32   protected Vector groups = new Vector();
33   protected char gapCharacter = '-';
34   protected int type = NUCLEOTIDE;
35   public static final int PROTEIN = 0;
36   public static final int NUCLEOTIDE = 1;
37
38   /** DOCUMENT ME!! */
39   public AlignmentAnnotation[] annotations;
40
41   HiddenSequences hiddenSequences = new HiddenSequences(this);
42
43   private void initAlignment(SequenceI[] seqs)
44   {
45     int i = 0;
46
47     if (jalview.util.Comparison.isNucleotide(seqs))
48     {
49       type = NUCLEOTIDE;
50     }
51     else
52     {
53       type = PROTEIN;
54     }
55
56     sequences = new Vector();
57
58     for (i = 0; i < seqs.length; i++)
59     {
60       sequences.addElement(seqs[i]);
61     }
62
63   }
64
65   /** Make an alignment from an array of Sequences.
66    *
67    * @param sequences
68    */
69   public Alignment(SequenceI[] seqs)
70   {
71     initAlignment(seqs);
72   }
73
74   /**
75    * Make a new alignment from an array of SeqCigars
76    * @param seqs SeqCigar[]
77    */
78   public Alignment(SeqCigar[] alseqs)
79   {
80     SequenceI[] seqs = SeqCigar.createAlignmentSequences(alseqs, gapCharacter,
81         new ColumnSelection(), null);
82     initAlignment(seqs);
83   }
84
85   /**
86    * Make a new alignment from an CigarArray
87    * JBPNote - can only do this when compactAlignment does not contain hidden regions.
88    * JBPNote - must also check that compactAlignment resolves to a set of SeqCigars - or construct them appropriately.
89    * @param compactAlignment CigarArray
90    */
91   public static AlignmentI createAlignment(CigarArray compactAlignment)
92   {
93     throw new Error("Alignment(CigarArray) not yet implemented");
94     // this(compactAlignment.refCigars);
95   }
96
97   /**
98    * DOCUMENT ME!
99    *
100    * @return DOCUMENT ME!
101    */
102   public Vector getSequences()
103   {
104     return sequences;
105   }
106
107   public SequenceI[] getSequencesArray()
108   {
109     SequenceI[] reply = new SequenceI[sequences.size()];
110     for (int i = 0; i < sequences.size(); i++)
111     {
112       reply[i] = (SequenceI) sequences.elementAt(i);
113     }
114     return reply;
115   }
116
117   /**
118    * DOCUMENT ME!
119    *
120    * @param i DOCUMENT ME!
121    *
122    * @return DOCUMENT ME!
123    */
124   public SequenceI getSequenceAt(int i)
125   {
126     if (i < sequences.size())
127     {
128       return (SequenceI) sequences.elementAt(i);
129     }
130
131     return null;
132   }
133
134   /** Adds a sequence to the alignment.  Recalculates maxLength and size.
135    *
136    * @param snew
137    */
138   public void addSequence(SequenceI snew)
139   {
140     if (dataset != null)
141     {
142       if (snew.getDatasetSequence() != null)
143       {
144         getDataset().addSequence(snew.getDatasetSequence());
145       }
146       else
147       {
148         Sequence ds = new Sequence(snew.getName(),
149                                    AlignSeq.extractGaps("-. ",
150             snew.getSequenceAsString()),
151                                    snew.getStart(),
152                                    snew.getEnd());
153
154         snew.setDatasetSequence(ds);
155         getDataset().addSequence(ds);
156       }
157     }
158     sequences.addElement(snew);
159
160     hiddenSequences.adjustHeightSequenceAdded();
161   }
162
163   /** Adds a sequence to the alignment.  Recalculates maxLength and size.
164    *
165    * @param snew
166    */
167   public void setSequenceAt(int i, SequenceI snew)
168   {
169     SequenceI oldseq = getSequenceAt(i);
170     deleteSequence(oldseq);
171
172     sequences.setElementAt(snew, i);
173   }
174
175   /**
176    * DOCUMENT ME!
177    *
178    * @return DOCUMENT ME!
179    */
180   public Vector getGroups()
181   {
182     return groups;
183   }
184
185   /**
186    * DOCUMENT ME!
187    *
188    * @param s DOCUMENT ME!
189    */
190   public void deleteSequence(SequenceI s)
191   {
192     deleteSequence(findIndex(s));
193   }
194
195   /**
196    * DOCUMENT ME!
197    *
198    * @param i DOCUMENT ME!
199    */
200   public void deleteSequence(int i)
201   {
202     if (i > -1 && i < getHeight())
203     {
204       sequences.removeElementAt(i);
205       hiddenSequences.adjustHeightSequenceDeleted(i);
206     }
207   }
208
209   /**    */
210   public SequenceGroup findGroup(SequenceI s)
211   {
212     for (int i = 0; i < this.groups.size(); i++)
213     {
214       SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
215
216       if (sg.getSequences(null).contains(s))
217       {
218         return sg;
219       }
220     }
221
222     return null;
223   }
224
225   /**
226    * DOCUMENT ME!
227    *
228    * @param s DOCUMENT ME!
229    *
230    * @return DOCUMENT ME!
231    */
232   public SequenceGroup[] findAllGroups(SequenceI s)
233   {
234     Vector temp = new Vector();
235
236     int gSize = groups.size();
237     for (int i = 0; i < gSize; i++)
238     {
239       SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
240       if (sg == null || sg.getSequences(null) == null)
241       {
242         this.deleteGroup(sg);
243         gSize--;
244         continue;
245       }
246
247       if (sg.getSequences(null).contains(s))
248       {
249         temp.addElement(sg);
250       }
251     }
252
253     SequenceGroup[] ret = new SequenceGroup[temp.size()];
254
255     for (int i = 0; i < temp.size(); i++)
256     {
257       ret[i] = (SequenceGroup) temp.elementAt(i);
258     }
259
260     return ret;
261   }
262
263   /**    */
264   public void addGroup(SequenceGroup sg)
265   {
266     if (!groups.contains(sg))
267     {
268       if (hiddenSequences.getSize() > 0)
269       {
270         int i, iSize = sg.getSize();
271         for (i = 0; i < iSize; i++)
272         {
273           if (!sequences.contains(sg.getSequenceAt(i)))
274           {
275             sg.deleteSequence(sg.getSequenceAt(i), false);
276             iSize--;
277             i--;
278           }
279         }
280
281         if (sg.getSize() < 1)
282         {
283           return;
284         }
285       }
286
287       groups.addElement(sg);
288     }
289   }
290
291   /**
292    * DOCUMENT ME!
293    */
294   public void deleteAllGroups()
295   {
296     groups.removeAllElements();
297   }
298
299   /**    */
300   public void deleteGroup(SequenceGroup g)
301   {
302     if (groups.contains(g))
303     {
304       groups.removeElement(g);
305     }
306   }
307
308   /**    */
309   public SequenceI findName(String name)
310   {
311     int i = 0;
312
313     while (i < sequences.size())
314     {
315       if (getSequenceAt(i).getName().equals(name))
316       {
317         return getSequenceAt(i);
318       }
319
320       i++;
321     }
322
323     return null;
324   }
325
326   public SequenceI[] findSequenceMatch(String name)
327   {
328     Vector matches = new Vector();
329     int i = 0;
330
331     while (i < sequences.size())
332     {
333       if (getSequenceAt(i).getName().equals(name))
334       {
335         matches.addElement(getSequenceAt(i));
336       }
337       i++;
338     }
339
340     SequenceI[] result = new SequenceI[matches.size()];
341     for (i = 0; i < result.length; i++)
342     {
343       result[i] = (SequenceI) matches.elementAt(i);
344     }
345
346     return result;
347
348   }
349
350   /**    */
351   public int findIndex(SequenceI s)
352   {
353     int i = 0;
354
355     while (i < sequences.size())
356     {
357       if (s == getSequenceAt(i))
358       {
359         return i;
360       }
361
362       i++;
363     }
364
365     return -1;
366   }
367
368   /**
369    * DOCUMENT ME!
370    *
371    * @return DOCUMENT ME!
372    */
373   public int getHeight()
374   {
375     return sequences.size();
376   }
377
378   /**
379    * DOCUMENT ME!
380    *
381    * @return DOCUMENT ME!
382    */
383   public int getWidth()
384   {
385     int maxLength = -1;
386
387     for (int i = 0; i < sequences.size(); i++)
388     {
389       if (getSequenceAt(i).getLength() > maxLength)
390       {
391         maxLength = getSequenceAt(i).getLength();
392       }
393     }
394
395     return maxLength;
396   }
397
398   /**
399    * DOCUMENT ME!
400    *
401    * @param gc DOCUMENT ME!
402    */
403   public void setGapCharacter(char gc)
404   {
405     gapCharacter = gc;
406
407     for (int i = 0; i < sequences.size(); i++)
408     {
409       Sequence seq = (Sequence) sequences.elementAt(i);
410       seq.setSequence(seq.getSequenceAsString()
411                       .replace('.', gc)
412                       .replace('-', gc)
413                       .replace(' ', gc)
414           );
415     }
416   }
417
418   /**
419    * DOCUMENT ME!
420    *
421    * @return DOCUMENT ME!
422    */
423   public char getGapCharacter()
424   {
425     return gapCharacter;
426   }
427
428   /**
429    * DOCUMENT ME!
430    *
431    * @return DOCUMENT ME!
432    */
433   public boolean isAligned()
434   {
435     int width = getWidth();
436
437     for (int i = 0; i < sequences.size(); i++)
438     {
439       if (getSequenceAt(i).getLength() != width)
440       {
441         return false;
442       }
443     }
444
445     return true;
446   }
447
448   /**
449    * DOCUMENT ME!
450    *
451    * @param aa DOCUMENT ME!
452    */
453   public void deleteAnnotation(AlignmentAnnotation aa)
454   {
455     int aSize = 1;
456
457     if (annotations != null)
458     {
459       aSize = annotations.length;
460     }
461
462     if (aSize < 1)
463     {
464       return;
465     }
466
467     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize - 1];
468
469     int tIndex = 0;
470
471     for (int i = 0; i < aSize; i++)
472     {
473       if (annotations[i] == aa)
474       {
475         continue;
476       }
477
478       temp[tIndex] = annotations[i];
479       tIndex++;
480     }
481
482     annotations = temp;
483   }
484
485   /**
486    * DOCUMENT ME!
487    *
488    * @param aa DOCUMENT ME!
489    */
490   public void addAnnotation(AlignmentAnnotation aa)
491   {
492     int aSize = 1;
493     if (annotations != null)
494     {
495       aSize = annotations.length + 1;
496     }
497
498     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
499
500     temp[aSize - 1] = aa;
501
502     int i = 0;
503
504     if (aSize > 1)
505     {
506       for (i = 0; i < (aSize - 1); i++)
507       {
508         temp[i] = annotations[i];
509       }
510     }
511
512     annotations = temp;
513   }
514
515   public void setAnnotationIndex(AlignmentAnnotation aa, int index)
516   {
517     if (aa == null || annotations == null || annotations.length - 1 < index)
518     {
519       return;
520     }
521
522     int aSize = annotations.length;
523     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
524
525     temp[index] = aa;
526
527     for (int i = 0; i < aSize; i++)
528     {
529       if (i == index)
530       {
531         continue;
532       }
533
534       if (i < index)
535       {
536         temp[i] = annotations[i];
537       }
538       else
539       {
540         temp[i] = annotations[i - 1];
541       }
542     }
543
544     annotations = temp;
545   }
546
547   /**
548    * DOCUMENT ME!
549    *
550    * @return DOCUMENT ME!
551    */
552   public AlignmentAnnotation[] getAlignmentAnnotation()
553   {
554     return annotations;
555   }
556
557   public void setNucleotide(boolean b)
558   {
559     if (b)
560     {
561       type = NUCLEOTIDE;
562     }
563     else
564     {
565       type = PROTEIN;
566     }
567   }
568
569   public boolean isNucleotide()
570   {
571     if (type == NUCLEOTIDE)
572     {
573       return true;
574     }
575     else
576     {
577       return false;
578     }
579   }
580
581   public void setDataset(Alignment data)
582   {
583     if (dataset == null && data == null)
584     {
585       // Create a new dataset for this alignment.
586       // Can only be done once, if dataset is not null
587       // This will not be performed
588       Sequence[] seqs = new Sequence[getHeight()];
589       SequenceI currentSeq;
590       for (int i = 0; i < getHeight(); i++)
591       {
592         currentSeq = getSequenceAt(i);
593         if (currentSeq.getDatasetSequence() != null)
594         {
595           seqs[i] = (Sequence) currentSeq.getDatasetSequence();
596         }
597         else
598         {
599           seqs[i] = new Sequence(currentSeq.getName(),
600                                  AlignSeq.extractGaps(
601                                      jalview.util.Comparison.GapChars,
602                                      currentSeq.getSequenceAsString()
603                                  ),
604                                  currentSeq.getStart(),
605                                  currentSeq.getEnd());
606           seqs[i].sequenceFeatures = currentSeq.getSequenceFeatures();
607           seqs[i].setDescription(currentSeq.getDescription());
608           getSequenceAt(i).setSequenceFeatures(null);
609           getSequenceAt(i).setDatasetSequence(seqs[i]);
610         }
611       }
612
613       dataset = new Alignment(seqs);
614     }
615     else if (dataset == null && data != null)
616     {
617       dataset = data;
618     }
619   }
620
621   public Alignment getDataset()
622   {
623     return dataset;
624   }
625
626   public boolean padGaps()
627   {
628     boolean modified = false;
629
630     //Remove excess gaps from the end of alignment
631     int maxLength = -1;
632
633     SequenceI current;
634     for (int i = 0; i < sequences.size(); i++)
635     {
636       current = getSequenceAt(i);
637       for (int j = current.getLength(); j > maxLength; j--)
638       {
639         if (j > maxLength && !jalview.util.Comparison.isGap(
640             current.getCharAt(j)))
641         {
642           maxLength = j;
643           break;
644         }
645       }
646     }
647
648     maxLength++;
649
650     int cLength;
651     for (int i = 0; i < sequences.size();
652          i++)
653     {
654       current = getSequenceAt(i);
655       cLength = current.getLength();
656
657       if (cLength < maxLength)
658       {
659         current.insertCharAt(cLength,
660                              maxLength - cLength, gapCharacter);
661         modified = true;
662       }
663       else if (current.getLength() > maxLength)
664       {
665         current.deleteChars(maxLength, current.getLength());
666       }
667     }
668     return modified;
669   }
670
671   public HiddenSequences getHiddenSequences()
672   {
673     return hiddenSequences;
674   }
675
676   public CigarArray getCompactAlignment()
677   {
678     SeqCigar alseqs[] = new SeqCigar[sequences.size()];
679     for (int i = 0; i < sequences.size(); i++)
680     {
681       alseqs[i] = new SeqCigar( (SequenceI) sequences.elementAt(i));
682     }
683     CigarArray cal = new CigarArray(alseqs);
684     cal.addOperation(CigarArray.M, getWidth());
685     return cal;
686   }
687
688 }