AAFrequency optimized
[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     /**
570      * DOCUMENT ME!
571      *
572      * @return DOCUMENT ME!
573      */
574     public boolean isAligned()
575     {
576         int width = getWidth();
577
578         for (int i = 0; i < sequences.size(); i++)
579         {
580             if (getSequenceAt(i).getLength() != width)
581             {
582                 return false;
583             }
584         }
585
586         return true;
587     }
588
589     /**
590      * DOCUMENT ME!
591      *
592      * @param aa DOCUMENT ME!
593      */
594     public void deleteAnnotation(AlignmentAnnotation aa)
595     {
596         int aSize = 1;
597
598         if (annotations != null)
599         {
600             aSize = annotations.length;
601         }
602
603         AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize - 1];
604
605         int tIndex = 0;
606
607         for (int i = 0; i < aSize; i++)
608         {
609             if (annotations[i] == aa)
610             {
611                 continue;
612             }
613
614             temp[tIndex] = annotations[i];
615             tIndex++;
616         }
617
618         annotations = temp;
619     }
620
621
622     public void adjustSequenceAnnotations()
623     {
624       if(annotations!=null)
625       {
626         for (int a = 0; a < annotations.length; a++)
627         {
628           if (annotations[a].sequenceRef != null)
629           {
630             annotations[a].adjustForAlignment();
631           }
632         }
633       }
634     }
635
636     /**
637      * DOCUMENT ME!
638      *
639      * @param aa DOCUMENT ME!
640      */
641     public void addAnnotation(AlignmentAnnotation aa)
642     {
643         int aSize = 1;
644         if (annotations != null)
645         {
646             aSize = annotations.length + 1;
647         }
648
649         AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
650
651         temp[aSize-1] = aa;
652
653         int i = 0;
654
655         if (aSize > 1)
656         {
657             for (i = 0; i < (aSize-1); i++)
658             {
659                 temp[i] = annotations[i];
660             }
661         }
662
663         annotations = temp;
664     }
665
666     public void setAnnotationIndex(AlignmentAnnotation aa, int index)
667     {
668       if(aa==null || annotations==null || annotations.length-1<index)
669         return;
670
671       int aSize = annotations.length;
672       AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
673
674       temp[index] = aa;
675
676       for (int i = 0; i < aSize; i++)
677       {
678         if(i==index)
679           continue;
680
681         if(i<index)
682           temp[i] = annotations[i];
683         else
684           temp[i] = annotations[i-1];
685       }
686
687         annotations = temp;
688     }
689
690     /**
691      * DOCUMENT ME!
692      *
693      * @return DOCUMENT ME!
694      */
695     public AlignmentAnnotation[] getAlignmentAnnotation()
696     {
697         return annotations;
698     }
699
700     public void setNucleotide(boolean b)
701     {
702       if(b)
703         type = NUCLEOTIDE;
704       else
705         type = PROTEIN;
706     }
707
708     public boolean isNucleotide()
709     {
710       if(type==NUCLEOTIDE)
711         return true;
712       else
713         return false;
714     }
715
716     public void setDataset(Alignment data)
717     {
718       if(dataset==null && data==null)
719       {
720         // Create a new dataset for this alignment.
721         // Can only be done once, if dataset is not null
722         // This will not be performed
723         Sequence[] seqs = new Sequence[getHeight()];
724         for (int i = 0; i < getHeight(); i++)
725         {
726           if(getSequenceAt(i).getDatasetSequence()!=null)
727           {
728             seqs[i] = (Sequence)getSequenceAt(i).getDatasetSequence();
729           }
730           else
731           {
732             seqs[i] = new Sequence(getSequenceAt(i).getName(),
733                                    AlignSeq.extractGaps(
734                                        jalview.util.Comparison.GapChars,
735                                        getSequenceAt(i).getSequence()
736                                    ),
737                                    getSequenceAt(i).getStart(),
738                                    getSequenceAt(i).getEnd());
739             seqs[i].sequenceFeatures = getSequenceAt(i).getSequenceFeatures();
740             getSequenceAt(i).setSequenceFeatures(null);
741             getSequenceAt(i).setDatasetSequence(seqs[i]);
742           }
743         }
744
745         dataset = new Alignment(seqs);
746       }
747       else if(dataset==null && data!=null)
748       {
749         dataset = data;
750       }
751     }
752
753     public Alignment getDataset()
754     {
755       return dataset;
756     }
757
758     public boolean padGaps() {
759       boolean modified=false;
760
761       //Remove excess gaps from the end of alignment
762       int maxLength = -1;
763
764       SequenceI current;
765       for (int i = 0; i < sequences.size(); i++)
766       {
767         current = getSequenceAt(i);
768         for (int j = current.getLength(); j > maxLength; j--)
769         {
770           if (j > maxLength && !jalview.util.Comparison.isGap(
771               current.getCharAt(j)))
772           {
773             maxLength = j;
774             break;
775           }
776         }
777       }
778
779       maxLength++;
780
781       for (int i = 0; i < sequences.size();
782            i++)
783       {
784         current = getSequenceAt(i);
785
786         if (current.getLength() < maxLength)
787         {
788           current.insertCharAt(maxLength - 1, gapCharacter);
789           modified=true;
790         }
791         else if(current.getLength() > maxLength)
792         {
793           current.deleteChars(maxLength, current.getLength());
794         }
795       }
796       return modified;
797     }
798
799     public HiddenSequences getHiddenSequences()
800     {
801       return hiddenSequences;
802     }
803     SequenceI [] getVisibleAndRepresentedSeqs()
804     {
805       if(hiddenSequences==null || hiddenSequences.getSize()<1)
806         return getSequencesArray();
807
808       Vector seqs = new Vector();
809       SequenceI seq;
810       SequenceGroup hidden;
811       for (int i = 0; i < sequences.size(); i++)
812       {
813         seq = (SequenceI) sequences.elementAt(i);
814         seqs.addElement(seq);
815         hidden = seq.getHiddenSequences();
816         if(hidden!=null)
817         {
818           for(int j=0; j<hidden.getSize(false); j++)
819           {
820             seqs.addElement(hidden.getSequenceAt(j));
821           }
822         }
823       }
824       SequenceI [] result = new SequenceI[seqs.size()];
825       for(int i=0; i<seqs.size(); i++)
826         result[i] = (SequenceI)seqs.elementAt(i);
827
828       return result;
829
830     }
831
832   public CigarArray getCompactAlignment()
833   {
834     SeqCigar alseqs[] = new SeqCigar[sequences.size()];
835     for (int i=0; i<sequences.size(); i++) {
836       alseqs[i] = new SeqCigar((SequenceI) sequences.elementAt(i));
837     }
838     return new CigarArray(alseqs);
839   }
840
841 }