92ef23a10994772345ac6b78c58cf58e3e20e7cb
[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     public SequenceI [] findSequenceMatch(String name)
431     {
432       Vector matches = new Vector();
433       int i = 0;
434
435       while (i < sequences.size())
436       {
437           if (getSequenceAt(i).getName().equals(name))
438           {
439               matches.addElement(getSequenceAt(i));
440           }
441           i++;
442       }
443
444       SequenceI [] result = new SequenceI[matches.size()];
445       for(i=0; i<result.length; i++)
446         result[i] = (SequenceI)matches.elementAt(i);
447
448       return result;
449
450     }
451
452
453     /**    */
454     public int findIndex(SequenceI s)
455     {
456         int i = 0;
457
458         while (i < sequences.size())
459         {
460             if (s == getSequenceAt(i))
461             {
462                 return i;
463             }
464
465             i++;
466         }
467
468         return -1;
469     }
470
471     /**
472      * DOCUMENT ME!
473      *
474      * @return DOCUMENT ME!
475      */
476     public int getHeight()
477     {
478         return sequences.size();
479     }
480
481     /**
482      * DOCUMENT ME!
483      *
484      * @return DOCUMENT ME!
485      */
486     public int getWidth()
487     {
488         int maxLength = -1;
489
490         for (int i = 0; i < sequences.size(); i++)
491         {
492             if (getSequenceAt(i).getLength() > maxLength)
493             {
494                 maxLength = getSequenceAt(i).getLength();
495             }
496         }
497
498         return maxLength;
499     }
500
501     /**
502      * DOCUMENT ME!
503      *
504      * @return DOCUMENT ME!
505      */
506     public int getMaxIdLength()
507     {
508         int max = 0;
509         int i = 0;
510
511         while (i < sequences.size())
512         {
513             SequenceI seq = getSequenceAt(i);
514             String tmp = seq.getName() + "/" + seq.getStart() + "-" +
515                 seq.getEnd();
516
517             if (tmp.length() > max)
518             {
519                 max = tmp.length();
520             }
521
522             i++;
523         }
524
525         return max;
526     }
527
528     /**
529      * DOCUMENT ME!
530      *
531      * @param gc DOCUMENT ME!
532      */
533     public void setGapCharacter(char gc)
534     {
535         gapCharacter = gc;
536
537         for (int i = 0; i < sequences.size(); i++)
538         {
539             Sequence seq = (Sequence) sequences.elementAt(i);
540             seq.setSequence( seq.getSequence().replace('.', gc) );
541             seq.setSequence( seq.getSequence().replace('-', gc) );
542             seq.setSequence( seq.getSequence().replace(' ', gc) );
543         }
544     }
545
546     /**
547      * DOCUMENT ME!
548      *
549      * @return DOCUMENT ME!
550      */
551     public char getGapCharacter()
552     {
553         return gapCharacter;
554     }
555
556     /**
557      * DOCUMENT ME!
558      *
559      * @return DOCUMENT ME!
560      */
561     public Vector getAAFrequency()
562     {
563         return AAFrequency.calculate(sequences, 0, getWidth());
564     }
565
566     /**
567      * DOCUMENT ME!
568      *
569      * @return DOCUMENT ME!
570      */
571     public boolean isAligned()
572     {
573         int width = getWidth();
574
575         for (int i = 0; i < sequences.size(); i++)
576         {
577             if (getSequenceAt(i).getLength() != width)
578             {
579                 return false;
580             }
581         }
582
583         return true;
584     }
585
586     /**
587      * DOCUMENT ME!
588      *
589      * @param aa DOCUMENT ME!
590      */
591     public void deleteAnnotation(AlignmentAnnotation aa)
592     {
593         int aSize = 1;
594
595         if (annotations != null)
596         {
597             aSize = annotations.length;
598         }
599
600         AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize - 1];
601
602         int tIndex = 0;
603
604         for (int i = 0; i < aSize; i++)
605         {
606             if (annotations[i] == aa)
607             {
608                 continue;
609             }
610
611             temp[tIndex] = annotations[i];
612             tIndex++;
613         }
614
615         annotations = temp;
616     }
617
618
619     public void adjustSequenceAnnotations()
620     {
621       if(annotations!=null)
622       {
623         for (int a = 0; a < annotations.length; a++)
624         {
625           if (annotations[a].sequenceRef != null)
626           {
627             annotations[a].adjustForAlignment();
628           }
629         }
630       }
631     }
632
633     /**
634      * DOCUMENT ME!
635      *
636      * @param aa DOCUMENT ME!
637      */
638     public void addAnnotation(AlignmentAnnotation aa)
639     {
640         int aSize = 1;
641         if (annotations != null)
642         {
643             aSize = annotations.length + 1;
644         }
645
646         AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
647
648         temp[aSize-1] = aa;
649
650         int i = 0;
651
652         if (aSize > 1)
653         {
654             for (i = 0; i < (aSize-1); i++)
655             {
656                 temp[i] = annotations[i];
657             }
658         }
659
660         annotations = temp;
661     }
662
663     public void setAnnotationIndex(AlignmentAnnotation aa, int index)
664     {
665       if(aa==null || annotations==null || annotations.length-1<index)
666         return;
667
668       int aSize = annotations.length;
669       AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
670
671       temp[index] = aa;
672
673       for (int i = 0; i < aSize; i++)
674       {
675         if(i==index)
676           continue;
677
678         if(i<index)
679           temp[i] = annotations[i];
680         else
681           temp[i] = annotations[i-1];
682       }
683
684         annotations = temp;
685     }
686
687     /**
688      * DOCUMENT ME!
689      *
690      * @return DOCUMENT ME!
691      */
692     public AlignmentAnnotation[] getAlignmentAnnotation()
693     {
694         return annotations;
695     }
696
697     public void setNucleotide(boolean b)
698     {
699       if(b)
700         type = NUCLEOTIDE;
701       else
702         type = PROTEIN;
703     }
704
705     public boolean isNucleotide()
706     {
707       if(type==NUCLEOTIDE)
708         return true;
709       else
710         return false;
711     }
712
713     public void setDataset(Alignment data)
714     {
715       if(dataset==null && data==null)
716       {
717         // Create a new dataset for this alignment.
718         // Can only be done once, if dataset is not null
719         // This will not be performed
720         Sequence[] seqs = new Sequence[getHeight()];
721         for (int i = 0; i < getHeight(); i++)
722         {
723           if(getSequenceAt(i).getDatasetSequence()!=null)
724           {
725             seqs[i] = (Sequence)getSequenceAt(i).getDatasetSequence();
726           }
727           else
728           {
729             seqs[i] = new Sequence(getSequenceAt(i).getName(),
730                                    AlignSeq.extractGaps(
731                                        jalview.util.Comparison.GapChars,
732                                        getSequenceAt(i).getSequence()
733                                    ),
734                                    getSequenceAt(i).getStart(),
735                                    getSequenceAt(i).getEnd());
736
737             getSequenceAt(i).setDatasetSequence(seqs[i]);
738           }
739         }
740
741         dataset = new Alignment(seqs);
742       }
743       else if(dataset==null && data!=null)
744       {
745         dataset = data;
746       }
747     }
748
749     public Alignment getDataset()
750     {
751       return dataset;
752     }
753
754     public boolean padGaps() {
755       boolean modified=false;
756
757       //Remove excess gaps from the end of alignment
758       int maxLength = -1;
759
760       SequenceI current;
761       for (int i = 0; i < sequences.size(); i++)
762       {
763         current = getSequenceAt(i);
764         for (int j = current.getLength(); j > maxLength; j--)
765         {
766           if (j > maxLength && !jalview.util.Comparison.isGap(
767               current.getCharAt(j)))
768           {
769             maxLength = j;
770             break;
771           }
772         }
773       }
774
775       maxLength++;
776
777       for (int i = 0; i < sequences.size();
778            i++)
779       {
780         current = getSequenceAt(i);
781
782         if (current.getLength() < maxLength)
783         {
784           current.insertCharAt(maxLength - 1, gapCharacter);
785           modified=true;
786         }
787         else if(current.getLength() > maxLength)
788         {
789           current.deleteChars(maxLength, current.getLength());
790         }
791       }
792       return modified;
793     }
794
795     public HiddenSequences getHiddenSequences()
796     {
797       return hiddenSequences;
798     }
799     SequenceI [] getVisibleAndRepresentedSeqs()
800     {
801       if(hiddenSequences==null || hiddenSequences.getSize()<1)
802         return getSequencesArray();
803
804       Vector seqs = new Vector();
805       SequenceI seq;
806       SequenceGroup hidden;
807       for (int i = 0; i < sequences.size(); i++)
808       {
809         seq = (SequenceI) sequences.elementAt(i);
810         seqs.addElement(seq);
811         hidden = seq.getHiddenSequences();
812         if(hidden!=null)
813         {
814           for(int j=0; j<hidden.getSize(false); j++)
815           {
816             seqs.addElement(hidden.getSequenceAt(j));
817           }
818         }
819       }
820       SequenceI [] result = new SequenceI[seqs.size()];
821       for(int i=0; i<seqs.size(); i++)
822         result[i] = (SequenceI)seqs.elementAt(i);
823
824       return result;
825
826     }
827
828   public CigarArray getCompactAlignment()
829   {
830     SeqCigar alseqs[] = new SeqCigar[sequences.size()];
831     for (int i=0; i<sequences.size(); i++) {
832       alseqs[i] = new SeqCigar((SequenceI) sequences.elementAt(i));
833     }
834     return new CigarArray(alseqs);
835   }
836
837 }