507102ec59d6be4ed6ee1fb8ab9d3d5f42dac503
[jalview.git] / src / jalview / datamodel / Alignment.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer
3  * Copyright (C) 2005 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 jalview.analysis.*;
22
23 import jalview.util.*;
24
25 import java.util.*;
26
27 /** Data structure to hold and manipulate a multiple sequence alignment
28  */
29 public class Alignment implements AlignmentI
30 {
31     protected Alignment dataset;
32     protected Vector sequences;
33     protected Vector groups = new Vector();
34     protected char gapCharacter = '-';
35     protected int type = NUCLEOTIDE;
36     public static final int PROTEIN = 0;
37     public static final int NUCLEOTIDE = 1;
38
39     /** DOCUMENT ME!! */
40     public AlignmentAnnotation[] annotations;
41
42     HiddenSequences hiddenSequences = new HiddenSequences(this);
43
44     private void initAlignment(SequenceI[] seqs) {
45       int i=0;
46
47       if( jalview.util.Comparison.isNucleotide(seqs))
48         type = NUCLEOTIDE;
49       else
50         type = PROTEIN;
51
52       sequences = new Vector();
53
54       for (i = 0; i < seqs.length; i++)
55       {
56         sequences.addElement(seqs[i]);
57       }
58
59     }
60     /** Make an alignment from an array of Sequences.
61      *
62      * @param sequences
63      */
64     public Alignment(SequenceI[] seqs)
65     {
66       initAlignment(seqs);
67     }
68     /**
69      * Make a new alignment from an array of SeqCigars
70      * @param seqs SeqCigar[]
71      */
72     public Alignment(SeqCigar[] alseqs) {
73       SequenceI[] seqs = SeqCigar.createAlignmentSequences(alseqs, gapCharacter, new ColumnSelection(), null);
74       initAlignment(seqs);
75     }
76     /**
77      * Make a new alignment from an CigarArray
78      * JBPNote - can only do this when compactAlignment does not contain hidden regions.
79      * JBPNote - must also check that compactAlignment resolves to a set of SeqCigars - or construct them appropriately.
80      * @param compactAlignment CigarArray
81      */
82     public static AlignmentI createAlignment(CigarArray compactAlignment) {
83       throw new Error("Alignment(CigarArray) not yet implemented");
84       // this(compactAlignment.refCigars);
85     }
86
87     /**
88      * DOCUMENT ME!
89      *
90      * @return DOCUMENT ME!
91      */
92     public Vector getSequences()
93     {
94         return sequences;
95     }
96
97     public SequenceI [] getSequencesArray()
98     {
99       SequenceI [] reply = new SequenceI[sequences.size()];
100       for(int i=0; i<sequences.size(); i++)
101       {
102         reply[i] = (SequenceI)sequences.elementAt(i);
103       }
104       return reply;
105     }
106
107     /**
108      * DOCUMENT ME!
109      *
110      * @param i DOCUMENT ME!
111      *
112      * @return DOCUMENT ME!
113      */
114     public SequenceI getSequenceAt(int i)
115     {
116         if (i < sequences.size())
117         {
118             return (SequenceI) sequences.elementAt(i);
119         }
120
121         return null;
122     }
123
124     /** Adds a sequence to the alignment.  Recalculates maxLength and size.
125      *
126      * @param snew
127      */
128     public void addSequence(SequenceI snew)
129     {
130       if(dataset!=null)
131       {
132         if(snew.getDatasetSequence()!=null)
133         {
134           System.out.println(snew.getName());
135           getDataset().addSequence(snew.getDatasetSequence());
136         }
137         else
138         {
139           Sequence ds = new Sequence(snew.getName(),
140                                      AlignSeq.extractGaps("-. ",
141               snew.getSequence()),
142                                      snew.getStart(),
143                                      snew.getEnd());
144
145           snew.setDatasetSequence(ds);
146           getDataset().addSequence(ds);
147         }
148       }
149
150       sequences.addElement(snew);
151     }
152
153
154     /** Adds a sequence to the alignment.  Recalculates maxLength and size.
155      *
156      * @param snew
157      */
158     public void setSequenceAt(int i, SequenceI snew)
159     {
160         SequenceI oldseq = getSequenceAt(i);
161         deleteSequence(oldseq);
162
163         sequences.setElementAt(snew, i);
164     }
165
166     /**
167      * DOCUMENT ME!
168      *
169      * @return DOCUMENT ME!
170      */
171     public Vector getGroups()
172     {
173         return groups;
174     }
175
176     /** Takes out columns consisting entirely of gaps (-,.," ")
177      */
178     public void removeGaps()
179     {
180         SequenceI[] seqs = getVisibleAndRepresentedSeqs();
181         int j, jSize = seqs.length;
182
183         int width = 0;
184         for (int i = 0; i < jSize; i++)
185         {
186           if (seqs[i].getLength() > width)
187           {
188             width = seqs[i].getLength();
189           }
190         }
191
192         int startCol = -1, endCol = -1;
193         boolean delete = true;
194         for (int i = 0; i < width; i++)
195         {
196             delete = true;
197
198             for (j = 0; j < jSize; j++)
199             {
200                 if (seqs[j].getLength() > i)
201                 {
202                     if (!jalview.util.Comparison.isGap(seqs[j].getCharAt(i)))
203                     {
204                         if(delete)
205                           endCol = i;
206
207                         delete = false;
208                         break;
209                     }
210                 }
211             }
212
213             if(delete && startCol==-1)
214             {
215               startCol = i;
216             }
217
218
219             if (!delete && startCol > -1)
220             {
221               deleteColumns(seqs, startCol, endCol);
222               width -= (endCol - startCol);
223               i -= (endCol - startCol);
224               startCol = -1;
225               endCol = -1;
226             }
227         }
228
229         if (delete && startCol > -1)
230         {
231           deleteColumns(seqs, startCol, endCol);
232         }
233     }
234
235     /** Removes a range of columns (start to end inclusive).
236      *
237      * @param seqs Sequences to remove columns from
238      * @param start Start column in the alignment
239      * @param end End column in the alignment
240      */
241     public void deleteColumns(SequenceI [] seqs, int start, int end)
242     {
243       for(int i=0; i<seqs.length; i++)
244         seqs[i].deleteChars(start, end);
245     }
246
247
248     /**
249      * DOCUMENT ME!
250      *
251      * @param i DOCUMENT ME!
252      */
253     public void trimLeft(int i)
254     {
255         SequenceI[] seqs = getVisibleAndRepresentedSeqs();
256         int j, jSize = seqs.length;
257         for (j = 0; j < jSize; j++)
258         {
259             int newstart = seqs[j].findPosition(i);
260
261             if(i>seqs[j].getLength())
262             {
263               sequences.removeElement(seqs[j]);
264               j--;
265               jSize--;
266             }
267             else
268             {
269               seqs[j].setStart(newstart);
270               seqs[j].setSequence(seqs[j].getSequence().substring(i));
271             }
272         }
273     }
274
275     /**
276      * DOCUMENT ME!
277      *
278      * @param i DOCUMENT ME!
279      */
280     public void trimRight(int i)
281     {
282         SequenceI[] seqs = getVisibleAndRepresentedSeqs();
283         int j, jSize = seqs.length;
284         for (j = 0; j < jSize; j++)
285         {
286             int newend = seqs[j].findPosition(i);
287
288             seqs[j].setEnd(newend);
289             if(seqs[j].getLength()>i)
290               seqs[j].setSequence(seqs[j].getSequence().substring(0, i + 1));
291         }
292     }
293
294     /**
295      * DOCUMENT ME!
296      *
297      * @param s DOCUMENT ME!
298      */
299     public void deleteSequence(SequenceI s)
300     {
301         for (int i = 0; i < getHeight(); i++)
302         {
303             if (getSequenceAt(i) == s)
304             {
305                 deleteSequence(i);
306             }
307         }
308     }
309
310     /**
311      * DOCUMENT ME!
312      *
313      * @param i DOCUMENT ME!
314      */
315     public void deleteSequence(int i)
316     {
317         sequences.removeElementAt(i);
318     }
319
320
321     /**    */
322     public SequenceGroup findGroup(SequenceI s)
323     {
324         for (int i = 0; i < this.groups.size(); i++)
325         {
326             SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
327
328             if (sg.getSequences(false).contains(s))
329             {
330                 return sg;
331             }
332         }
333
334         return null;
335     }
336
337     /**
338      * DOCUMENT ME!
339      *
340      * @param s DOCUMENT ME!
341      *
342      * @return DOCUMENT ME!
343      */
344     public SequenceGroup[] findAllGroups(SequenceI s)
345     {
346         Vector temp = new Vector();
347
348         int gSize = groups.size();
349         for (int i = 0; i < gSize; i++)
350         {
351             SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
352             if(sg==null || sg.getSequences(false)==null)
353             {
354               this.deleteGroup(sg);
355               gSize--;
356               continue;
357             }
358
359             if (sg.getSequences(false).contains(s))
360             {
361                 temp.addElement(sg);
362             }
363         }
364
365         SequenceGroup[] ret = new SequenceGroup[temp.size()];
366
367         for (int i = 0; i < temp.size(); i++)
368         {
369             ret[i] = (SequenceGroup) temp.elementAt(i);
370         }
371
372         return ret;
373     }
374
375
376
377     /**    */
378     public void addGroup(SequenceGroup sg)
379     {
380         if (!groups.contains(sg))
381         {
382             groups.addElement(sg);
383         }
384     }
385
386     /**
387      * DOCUMENT ME!
388      */
389     public void deleteAllGroups()
390     {
391         groups.removeAllElements();
392
393         int i = 0;
394
395         while (i < sequences.size())
396         {
397             SequenceI s = getSequenceAt(i);
398             s.setColor(java.awt.Color.white);
399             i++;
400         }
401     }
402
403     /**    */
404     public void deleteGroup(SequenceGroup g)
405     {
406         if (groups.contains(g))
407         {
408             groups.removeElement(g);
409         }
410     }
411
412     /**    */
413     public SequenceI findName(String name)
414     {
415         int i = 0;
416
417         while (i < sequences.size())
418         {
419             if (getSequenceAt(i).getName().equals(name))
420             {
421                 return getSequenceAt(i);
422             }
423
424             i++;
425         }
426
427         return null;
428     }
429
430
431     /**    */
432     public int findIndex(SequenceI s)
433     {
434         int i = 0;
435
436         while (i < sequences.size())
437         {
438             if (s == getSequenceAt(i))
439             {
440                 return i;
441             }
442
443             i++;
444         }
445
446         return -1;
447     }
448
449     /**
450      * DOCUMENT ME!
451      *
452      * @return DOCUMENT ME!
453      */
454     public int getHeight()
455     {
456         return sequences.size();
457     }
458
459     /**
460      * DOCUMENT ME!
461      *
462      * @return DOCUMENT ME!
463      */
464     public int getWidth()
465     {
466         int maxLength = -1;
467
468         for (int i = 0; i < sequences.size(); i++)
469         {
470             if (getSequenceAt(i).getLength() > maxLength)
471             {
472                 maxLength = getSequenceAt(i).getLength();
473             }
474         }
475
476         return maxLength;
477     }
478
479     /**
480      * DOCUMENT ME!
481      *
482      * @return DOCUMENT ME!
483      */
484     public int getMaxIdLength()
485     {
486         int max = 0;
487         int i = 0;
488
489         while (i < sequences.size())
490         {
491             SequenceI seq = getSequenceAt(i);
492             String tmp = seq.getName() + "/" + seq.getStart() + "-" +
493                 seq.getEnd();
494
495             if (tmp.length() > max)
496             {
497                 max = tmp.length();
498             }
499
500             i++;
501         }
502
503         return max;
504     }
505
506     /**
507      * DOCUMENT ME!
508      *
509      * @param gc DOCUMENT ME!
510      */
511     public void setGapCharacter(char gc)
512     {
513         gapCharacter = gc;
514
515         for (int i = 0; i < sequences.size(); i++)
516         {
517             Sequence seq = (Sequence) sequences.elementAt(i);
518             seq.setSequence( seq.getSequence().replace('.', gc) );
519             seq.setSequence( seq.getSequence().replace('-', gc) );
520             seq.setSequence( seq.getSequence().replace(' ', gc) );
521         }
522     }
523
524     /**
525      * DOCUMENT ME!
526      *
527      * @return DOCUMENT ME!
528      */
529     public char getGapCharacter()
530     {
531         return gapCharacter;
532     }
533
534     /**
535      * DOCUMENT ME!
536      *
537      * @return DOCUMENT ME!
538      */
539     public Vector getAAFrequency()
540     {
541         return AAFrequency.calculate(sequences, 0, getWidth());
542     }
543
544     /**
545      * DOCUMENT ME!
546      *
547      * @return DOCUMENT ME!
548      */
549     public boolean isAligned()
550     {
551         int width = getWidth();
552
553         for (int i = 0; i < sequences.size(); i++)
554         {
555             if (getSequenceAt(i).getLength() != width)
556             {
557                 return false;
558             }
559         }
560
561         return true;
562     }
563
564     /**
565      * DOCUMENT ME!
566      *
567      * @param aa DOCUMENT ME!
568      */
569     public void deleteAnnotation(AlignmentAnnotation aa)
570     {
571         int aSize = 1;
572
573         if (annotations != null)
574         {
575             aSize = annotations.length;
576         }
577
578         AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize - 1];
579
580         int tIndex = 0;
581
582         for (int i = 0; i < aSize; i++)
583         {
584             if (annotations[i] == aa)
585             {
586                 continue;
587             }
588
589             temp[tIndex] = annotations[i];
590             tIndex++;
591         }
592
593         annotations = temp;
594     }
595
596
597     public void adjustSequenceAnnotations()
598     {
599       if(annotations!=null)
600       {
601         for (int a = 0; a < annotations.length; a++)
602         {
603           if (annotations[a].sequenceRef != null)
604           {
605             annotations[a].adjustForAlignment();
606           }
607         }
608       }
609     }
610
611     /**
612      * DOCUMENT ME!
613      *
614      * @param aa DOCUMENT ME!
615      */
616     public void addAnnotation(AlignmentAnnotation aa)
617     {
618         int aSize = 1;
619         if (annotations != null)
620         {
621             aSize = annotations.length + 1;
622         }
623
624         AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
625
626         temp[aSize-1] = aa;
627
628         int i = 0;
629
630         if (aSize > 1)
631         {
632             for (i = 0; i < (aSize-1); i++)
633             {
634                 temp[i] = annotations[i];
635             }
636         }
637
638         annotations = temp;
639     }
640
641     public void setAnnotationIndex(AlignmentAnnotation aa, int index)
642     {
643       if(aa==null || annotations==null || annotations.length-1<index)
644         return;
645
646       int aSize = annotations.length;
647       AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
648
649       temp[index] = aa;
650
651       for (int i = 0; i < aSize; i++)
652       {
653         if(i==index)
654           continue;
655
656         if(i<index)
657           temp[i] = annotations[i];
658         else
659           temp[i] = annotations[i-1];
660       }
661
662         annotations = temp;
663     }
664
665     /**
666      * DOCUMENT ME!
667      *
668      * @return DOCUMENT ME!
669      */
670     public AlignmentAnnotation[] getAlignmentAnnotation()
671     {
672         return annotations;
673     }
674
675     public void setNucleotide(boolean b)
676     {
677       if(b)
678         type = NUCLEOTIDE;
679       else
680         type = PROTEIN;
681     }
682
683     public boolean isNucleotide()
684     {
685       if(type==NUCLEOTIDE)
686         return true;
687       else
688         return false;
689     }
690
691     public void setDataset(Alignment data)
692     {
693       if(dataset==null && data==null)
694       {
695         // Create a new dataset for this alignment.
696         // Can only be done once, if dataset is not null
697         // This will not be performed
698         Sequence[] seqs = new Sequence[getHeight()];
699         for (int i = 0; i < getHeight(); i++)
700         {
701           if(getSequenceAt(i).getDatasetSequence()!=null)
702           {
703             seqs[i] = (Sequence)getSequenceAt(i).getDatasetSequence();
704           }
705           else
706           {
707             seqs[i] = new Sequence(getSequenceAt(i).getName(),
708                                    AlignSeq.extractGaps(
709                                        jalview.util.Comparison.GapChars,
710                                        getSequenceAt(i).getSequence()
711                                    ),
712                                    getSequenceAt(i).getStart(),
713                                    getSequenceAt(i).getEnd());
714
715             getSequenceAt(i).setDatasetSequence(seqs[i]);
716           }
717         }
718
719         dataset = new Alignment(seqs);
720       }
721       else if(dataset==null && data!=null)
722       {
723         dataset = data;
724       }
725     }
726
727     public Alignment getDataset()
728     {
729       return dataset;
730     }
731
732     public boolean padGaps() {
733       boolean modified=false;
734
735       //Remove excess gaps from the end of alignment
736       int maxLength = -1;
737
738       SequenceI current;
739       for (int i = 0; i < sequences.size(); i++)
740       {
741         current = getSequenceAt(i);
742         for (int j = current.getLength(); j > maxLength; j--)
743         {
744           if (j > maxLength && !jalview.util.Comparison.isGap(
745               current.getCharAt(j)))
746           {
747             maxLength = j;
748             break;
749           }
750         }
751       }
752
753       maxLength++;
754
755       for (int i = 0; i < sequences.size();
756            i++)
757       {
758         current = getSequenceAt(i);
759
760         if (current.getLength() < maxLength)
761         {
762           current.insertCharAt(maxLength - 1, gapCharacter);
763           modified=true;
764         }
765         else if(current.getLength() > maxLength)
766         {
767           current.deleteChars(maxLength, current.getLength());
768         }
769       }
770       return modified;
771     }
772
773     public HiddenSequences getHiddenSequences()
774     {
775       return hiddenSequences;
776     }
777     SequenceI [] getVisibleAndRepresentedSeqs()
778     {
779       if(hiddenSequences==null || hiddenSequences.getSize()<1)
780         return getSequencesArray();
781
782       Vector seqs = new Vector();
783       SequenceI seq;
784       SequenceGroup hidden;
785       for (int i = 0; i < sequences.size(); i++)
786       {
787         seq = (SequenceI) sequences.elementAt(i);
788         seqs.addElement(seq);
789         hidden = seq.getHiddenSequences();
790         if(hidden!=null)
791         {
792           for(int j=0; j<hidden.getSize(false); j++)
793           {
794             seqs.addElement(hidden.getSequenceAt(j));
795           }
796         }
797       }
798       SequenceI [] result = new SequenceI[seqs.size()];
799       for(int i=0; i<seqs.size(); i++)
800         result[i] = (SequenceI)seqs.elementAt(i);
801
802       return result;
803
804     }
805
806   public CigarArray getCompactAlignment()
807   {
808     SeqCigar alseqs[] = new SeqCigar[sequences.size()];
809     for (int i=0; i<sequences.size(); i++) {
810       alseqs[i] = new SeqCigar((SequenceI) sequences.elementAt(i));
811     }
812     return new CigarArray(alseqs);
813   }
814
815 }