90a8b318e0fdd42a477c626f8d94c358ab671190
[jalview.git] / src / jalview / datamodel / Alignment.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer
3  * Copyright (C) 2006 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     /** Takes out columns consisting entirely of gaps (-,.," ")
176      */
177     public void removeGaps() {
178       removeGaps((ShiftList)null);
179     }
180     /**
181      * remove gaps in alignment - recording any frame shifts in shiftrecord
182      * intended to be passed to ColumnSelection.compensateForEdits(shiftrecord)
183      * @param shiftrecord
184      */
185     public void removeGaps(ShiftList shiftrecord) {
186         SequenceI[] seqs = getVisibleAndRepresentedSeqs();
187         int j, jSize = seqs.length;
188
189         int width = 0;
190         for (int i = 0; i < jSize; i++)
191         {
192           if (seqs[i].getLength() > width)
193           {
194             width = seqs[i].getLength();
195           }
196         }
197
198         int startCol = -1, endCol = -1;
199         boolean delete = true;
200         for (int i = 0; i < width; i++)
201         {
202             delete = true;
203
204             for (j = 0; j < jSize; j++)
205             {
206                 if (seqs[j].getLength() > i)
207                 {
208                     if (!jalview.util.Comparison.isGap(seqs[j].getCharAt(i)))
209                     {
210                         if(delete)
211                           endCol = i;
212
213                         delete = false;
214                         break;
215                     }
216                 }
217             }
218
219             if(delete && startCol==-1)
220             {
221               startCol = i;
222             }
223
224
225             if (!delete && startCol > -1)
226             {
227               deleteColumns(seqs, startCol, endCol);
228               if (shiftrecord!=null) {
229                 shiftrecord.addShift(startCol, 1+endCol-startCol);
230               }
231               width -= (endCol - startCol);
232               i -= (endCol - startCol);
233               startCol = -1;
234               endCol = -1;
235             }
236         }
237
238         if (delete && startCol > -1)
239         {
240           deleteColumns(seqs, startCol, endCol);
241           if (shiftrecord!=null) {
242             shiftrecord.addShift(startCol, 1+endCol-startCol);
243           }
244         }
245     }
246
247     /** Removes a range of columns (start to end inclusive).
248      *
249      * @param seqs Sequences to remove columns from
250      * @param start Start column in the alignment
251      * @param end End column in the alignment
252      */
253     public void deleteColumns(SequenceI [] seqs, int start, int end)
254     {
255       for(int i=0; i<seqs.length; i++)
256         seqs[i].deleteChars(start, end);
257     }
258
259
260     /**
261      * DOCUMENT ME!
262      *
263      * @param i DOCUMENT ME!
264      */
265     public void trimLeft(int i)
266     {
267         SequenceI[] seqs = getVisibleAndRepresentedSeqs();
268         int j, jSize = seqs.length;
269         for (j = 0; j < jSize; j++)
270         {
271             int newstart = seqs[j].findPosition(i);
272
273             if(i>seqs[j].getLength())
274             {
275               sequences.removeElement(seqs[j]);
276               j--;
277               jSize--;
278             }
279             else
280             {
281               seqs[j].setStart(newstart);
282               seqs[j].setSequence(seqs[j].getSequence().substring(i));
283             }
284         }
285     }
286
287     /**
288      * DOCUMENT ME!
289      *
290      * @param i DOCUMENT ME!
291      */
292     public void trimRight(int i)
293     {
294         SequenceI[] seqs = getVisibleAndRepresentedSeqs();
295         int j, jSize = seqs.length;
296         for (j = 0; j < jSize; j++)
297         {
298             int newend = seqs[j].findPosition(i);
299
300             seqs[j].setEnd(newend);
301             if(seqs[j].getLength()>i)
302               seqs[j].setSequence(seqs[j].getSequence().substring(0, i + 1));
303         }
304     }
305
306     /**
307      * DOCUMENT ME!
308      *
309      * @param s DOCUMENT ME!
310      */
311     public void deleteSequence(SequenceI s)
312     {
313         for (int i = 0; i < getHeight(); i++)
314         {
315             if (getSequenceAt(i) == s)
316             {
317                 deleteSequence(i);
318             }
319         }
320     }
321
322     /**
323      * DOCUMENT ME!
324      *
325      * @param i DOCUMENT ME!
326      */
327     public void deleteSequence(int i)
328     {
329         sequences.removeElementAt(i);
330     }
331
332
333     /**    */
334     public SequenceGroup findGroup(SequenceI s)
335     {
336         for (int i = 0; i < this.groups.size(); i++)
337         {
338             SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
339
340             if (sg.getSequences(false).contains(s))
341             {
342                 return sg;
343             }
344         }
345
346         return null;
347     }
348
349     /**
350      * DOCUMENT ME!
351      *
352      * @param s DOCUMENT ME!
353      *
354      * @return DOCUMENT ME!
355      */
356     public SequenceGroup[] findAllGroups(SequenceI s)
357     {
358         Vector temp = new Vector();
359
360         int gSize = groups.size();
361         for (int i = 0; i < gSize; i++)
362         {
363             SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
364             if(sg==null || sg.getSequences(false)==null)
365             {
366               this.deleteGroup(sg);
367               gSize--;
368               continue;
369             }
370
371             if (sg.getSequences(false).contains(s))
372             {
373                 temp.addElement(sg);
374             }
375         }
376
377         SequenceGroup[] ret = new SequenceGroup[temp.size()];
378
379         for (int i = 0; i < temp.size(); i++)
380         {
381             ret[i] = (SequenceGroup) temp.elementAt(i);
382         }
383
384         return ret;
385     }
386
387
388
389     /**    */
390     public void addGroup(SequenceGroup sg)
391     {
392         if (!groups.contains(sg))
393         {
394             groups.addElement(sg);
395         }
396     }
397
398     /**
399      * DOCUMENT ME!
400      */
401     public void deleteAllGroups()
402     {
403         groups.removeAllElements();
404
405         int i = 0;
406
407         while (i < sequences.size())
408         {
409             SequenceI s = getSequenceAt(i);
410             s.setColor(java.awt.Color.white);
411             i++;
412         }
413     }
414
415     /**    */
416     public void deleteGroup(SequenceGroup g)
417     {
418         if (groups.contains(g))
419         {
420             groups.removeElement(g);
421         }
422     }
423
424     /**    */
425     public SequenceI findName(String name)
426     {
427         int i = 0;
428
429         while (i < sequences.size())
430         {
431             if (getSequenceAt(i).getName().equals(name))
432             {
433                 return getSequenceAt(i);
434             }
435
436             i++;
437         }
438
439         return null;
440     }
441
442     public SequenceI [] findSequenceMatch(String name)
443     {
444       Vector matches = new Vector();
445       int i = 0;
446
447       while (i < sequences.size())
448       {
449           if (getSequenceAt(i).getName().equals(name))
450           {
451               matches.addElement(getSequenceAt(i));
452           }
453           i++;
454       }
455
456       SequenceI [] result = new SequenceI[matches.size()];
457       for(i=0; i<result.length; i++)
458         result[i] = (SequenceI)matches.elementAt(i);
459
460       return result;
461
462     }
463
464
465     /**    */
466     public int findIndex(SequenceI s)
467     {
468         int i = 0;
469
470         while (i < sequences.size())
471         {
472             if (s == getSequenceAt(i))
473             {
474                 return i;
475             }
476
477             i++;
478         }
479
480         return -1;
481     }
482
483     /**
484      * DOCUMENT ME!
485      *
486      * @return DOCUMENT ME!
487      */
488     public int getHeight()
489     {
490         return sequences.size();
491     }
492
493     /**
494      * DOCUMENT ME!
495      *
496      * @return DOCUMENT ME!
497      */
498     public int getWidth()
499     {
500         int maxLength = -1;
501
502         for (int i = 0; i < sequences.size(); i++)
503         {
504             if (getSequenceAt(i).getLength() > maxLength)
505             {
506                 maxLength = getSequenceAt(i).getLength();
507             }
508         }
509
510         return maxLength;
511     }
512
513     /**
514      * DOCUMENT ME!
515      *
516      * @return DOCUMENT ME!
517      */
518     public int getMaxIdLength()
519     {
520         int max = 0;
521         int i = 0;
522
523         while (i < sequences.size())
524         {
525             SequenceI seq = getSequenceAt(i);
526             String tmp = seq.getName() + "/" + seq.getStart() + "-" +
527                 seq.getEnd();
528
529             if (tmp.length() > max)
530             {
531                 max = tmp.length();
532             }
533
534             i++;
535         }
536
537         return max;
538     }
539
540     /**
541      * DOCUMENT ME!
542      *
543      * @param gc DOCUMENT ME!
544      */
545     public void setGapCharacter(char gc)
546     {
547         gapCharacter = gc;
548
549         for (int i = 0; i < sequences.size(); i++)
550         {
551             Sequence seq = (Sequence) sequences.elementAt(i);
552             seq.setSequence( seq.getSequence().replace('.', gc) );
553             seq.setSequence( seq.getSequence().replace('-', gc) );
554             seq.setSequence( seq.getSequence().replace(' ', gc) );
555         }
556     }
557
558     /**
559      * DOCUMENT ME!
560      *
561      * @return DOCUMENT ME!
562      */
563     public char getGapCharacter()
564     {
565         return gapCharacter;
566     }
567
568     /**
569      * DOCUMENT ME!
570      *
571      * @return DOCUMENT ME!
572      */
573     public Vector getAAFrequency()
574     {
575         return AAFrequency.calculate(sequences, 0, getWidth());
576     }
577
578     /**
579      * DOCUMENT ME!
580      *
581      * @return DOCUMENT ME!
582      */
583     public boolean isAligned()
584     {
585         int width = getWidth();
586
587         for (int i = 0; i < sequences.size(); i++)
588         {
589             if (getSequenceAt(i).getLength() != width)
590             {
591                 return false;
592             }
593         }
594
595         return true;
596     }
597
598     /**
599      * DOCUMENT ME!
600      *
601      * @param aa DOCUMENT ME!
602      */
603     public void deleteAnnotation(AlignmentAnnotation aa)
604     {
605         int aSize = 1;
606
607         if (annotations != null)
608         {
609             aSize = annotations.length;
610         }
611
612         AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize - 1];
613
614         int tIndex = 0;
615
616         for (int i = 0; i < aSize; i++)
617         {
618             if (annotations[i] == aa)
619             {
620                 continue;
621             }
622
623             temp[tIndex] = annotations[i];
624             tIndex++;
625         }
626
627         annotations = temp;
628     }
629
630
631     public void adjustSequenceAnnotations()
632     {
633       if(annotations!=null)
634       {
635         for (int a = 0; a < annotations.length; a++)
636         {
637           if (annotations[a].sequenceRef != null)
638           {
639             annotations[a].adjustForAlignment();
640           }
641         }
642       }
643     }
644
645     /**
646      * DOCUMENT ME!
647      *
648      * @param aa DOCUMENT ME!
649      */
650     public void addAnnotation(AlignmentAnnotation aa)
651     {
652         int aSize = 1;
653         if (annotations != null)
654         {
655             aSize = annotations.length + 1;
656         }
657
658         AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
659
660         temp[aSize-1] = aa;
661
662         int i = 0;
663
664         if (aSize > 1)
665         {
666             for (i = 0; i < (aSize-1); i++)
667             {
668                 temp[i] = annotations[i];
669             }
670         }
671
672         annotations = temp;
673     }
674
675     public void setAnnotationIndex(AlignmentAnnotation aa, int index)
676     {
677       if(aa==null || annotations==null || annotations.length-1<index)
678         return;
679
680       int aSize = annotations.length;
681       AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
682
683       temp[index] = aa;
684
685       for (int i = 0; i < aSize; i++)
686       {
687         if(i==index)
688           continue;
689
690         if(i<index)
691           temp[i] = annotations[i];
692         else
693           temp[i] = annotations[i-1];
694       }
695
696         annotations = temp;
697     }
698
699     /**
700      * DOCUMENT ME!
701      *
702      * @return DOCUMENT ME!
703      */
704     public AlignmentAnnotation[] getAlignmentAnnotation()
705     {
706         return annotations;
707     }
708
709     public void setNucleotide(boolean b)
710     {
711       if(b)
712         type = NUCLEOTIDE;
713       else
714         type = PROTEIN;
715     }
716
717     public boolean isNucleotide()
718     {
719       if(type==NUCLEOTIDE)
720         return true;
721       else
722         return false;
723     }
724
725     public void setDataset(Alignment data)
726     {
727       if(dataset==null && data==null)
728       {
729         // Create a new dataset for this alignment.
730         // Can only be done once, if dataset is not null
731         // This will not be performed
732         Sequence[] seqs = new Sequence[getHeight()];
733         for (int i = 0; i < getHeight(); i++)
734         {
735           if(getSequenceAt(i).getDatasetSequence()!=null)
736           {
737             seqs[i] = (Sequence)getSequenceAt(i).getDatasetSequence();
738           }
739           else
740           {
741             seqs[i] = new Sequence(getSequenceAt(i).getName(),
742                                    AlignSeq.extractGaps(
743                                        jalview.util.Comparison.GapChars,
744                                        getSequenceAt(i).getSequence()
745                                    ),
746                                    getSequenceAt(i).getStart(),
747                                    getSequenceAt(i).getEnd());
748             seqs[i].sequenceFeatures = getSequenceAt(i).getSequenceFeatures();
749             getSequenceAt(i).setSequenceFeatures(null);
750             getSequenceAt(i).setDatasetSequence(seqs[i]);
751           }
752         }
753
754         dataset = new Alignment(seqs);
755       }
756       else if(dataset==null && data!=null)
757       {
758         dataset = data;
759       }
760     }
761
762     public Alignment getDataset()
763     {
764       return dataset;
765     }
766
767     public boolean padGaps() {
768       boolean modified=false;
769
770       //Remove excess gaps from the end of alignment
771       int maxLength = -1;
772
773       SequenceI current;
774       for (int i = 0; i < sequences.size(); i++)
775       {
776         current = getSequenceAt(i);
777         for (int j = current.getLength(); j > maxLength; j--)
778         {
779           if (j > maxLength && !jalview.util.Comparison.isGap(
780               current.getCharAt(j)))
781           {
782             maxLength = j;
783             break;
784           }
785         }
786       }
787
788       maxLength++;
789
790       for (int i = 0; i < sequences.size();
791            i++)
792       {
793         current = getSequenceAt(i);
794
795         if (current.getLength() < maxLength)
796         {
797           current.insertCharAt(maxLength - 1, gapCharacter);
798           modified=true;
799         }
800         else if(current.getLength() > maxLength)
801         {
802           current.deleteChars(maxLength, current.getLength());
803         }
804       }
805       return modified;
806     }
807
808     public HiddenSequences getHiddenSequences()
809     {
810       return hiddenSequences;
811     }
812     SequenceI [] getVisibleAndRepresentedSeqs()
813     {
814       if(hiddenSequences==null || hiddenSequences.getSize()<1)
815         return getSequencesArray();
816
817       Vector seqs = new Vector();
818       SequenceI seq;
819       SequenceGroup hidden;
820       for (int i = 0; i < sequences.size(); i++)
821       {
822         seq = (SequenceI) sequences.elementAt(i);
823         seqs.addElement(seq);
824         hidden = seq.getHiddenSequences();
825         if(hidden!=null)
826         {
827           for(int j=0; j<hidden.getSize(false); j++)
828           {
829             seqs.addElement(hidden.getSequenceAt(j));
830           }
831         }
832       }
833       SequenceI [] result = new SequenceI[seqs.size()];
834       for(int i=0; i<seqs.size(); i++)
835         result[i] = (SequenceI)seqs.elementAt(i);
836
837       return result;
838
839     }
840
841   public CigarArray getCompactAlignment()
842   {
843     SeqCigar alseqs[] = new SeqCigar[sequences.size()];
844     for (int i=0; i<sequences.size(); i++) {
845       alseqs[i] = new SeqCigar((SequenceI) sequences.elementAt(i));
846     }
847     return new CigarArray(alseqs);
848   }
849
850 }