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