maxIdLength redundant, deleteCols, trimLeft, removeGaps moved to Commands
[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       sequences.addElement(snew);
150
151       hiddenSequences.adjustHeightSequenceAdded();
152     }
153
154
155     /** Adds a sequence to the alignment.  Recalculates maxLength and size.
156      *
157      * @param snew
158      */
159     public void setSequenceAt(int i, SequenceI snew)
160     {
161         SequenceI oldseq = getSequenceAt(i);
162         deleteSequence(oldseq);
163
164         sequences.setElementAt(snew, i);
165     }
166
167     /**
168      * DOCUMENT ME!
169      *
170      * @return DOCUMENT ME!
171      */
172     public Vector getGroups()
173     {
174         return groups;
175     }
176
177     /**
178      * DOCUMENT ME!
179      *
180      * @param s DOCUMENT ME!
181      */
182     public void deleteSequence(SequenceI s)
183     {
184       deleteSequence(findIndex(s));
185     }
186
187     /**
188      * DOCUMENT ME!
189      *
190      * @param i DOCUMENT ME!
191      */
192     public void deleteSequence(int i)
193     {
194       if(i>0 && i<getHeight())
195       {
196         sequences.removeElementAt(i);
197         hiddenSequences.adjustHeightSequenceDeleted(i);
198       }
199     }
200
201
202     /**    */
203     public SequenceGroup findGroup(SequenceI s)
204     {
205         for (int i = 0; i < this.groups.size(); i++)
206         {
207             SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
208
209             if (sg.getSequences(false).contains(s))
210             {
211                 return sg;
212             }
213         }
214
215         return null;
216     }
217
218     /**
219      * DOCUMENT ME!
220      *
221      * @param s DOCUMENT ME!
222      *
223      * @return DOCUMENT ME!
224      */
225     public SequenceGroup[] findAllGroups(SequenceI s)
226     {
227         Vector temp = new Vector();
228
229         int gSize = groups.size();
230         for (int i = 0; i < gSize; i++)
231         {
232             SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
233             if(sg==null || sg.getSequences(false)==null)
234             {
235               this.deleteGroup(sg);
236               gSize--;
237               continue;
238             }
239
240             if (sg.getSequences(false).contains(s))
241             {
242                 temp.addElement(sg);
243             }
244         }
245
246         SequenceGroup[] ret = new SequenceGroup[temp.size()];
247
248         for (int i = 0; i < temp.size(); i++)
249         {
250             ret[i] = (SequenceGroup) temp.elementAt(i);
251         }
252
253         return ret;
254     }
255
256
257
258     /**    */
259     public void addGroup(SequenceGroup sg)
260     {
261         if (!groups.contains(sg))
262         {
263             groups.addElement(sg);
264         }
265     }
266
267     /**
268      * DOCUMENT ME!
269      */
270     public void deleteAllGroups()
271     {
272         groups.removeAllElements();
273
274         int i = 0;
275
276         while (i < sequences.size())
277         {
278             SequenceI s = getSequenceAt(i);
279             s.setColor(java.awt.Color.white);
280             i++;
281         }
282     }
283
284     /**    */
285     public void deleteGroup(SequenceGroup g)
286     {
287         if (groups.contains(g))
288         {
289             groups.removeElement(g);
290         }
291     }
292
293     /**    */
294     public SequenceI findName(String name)
295     {
296         int i = 0;
297
298         while (i < sequences.size())
299         {
300             if (getSequenceAt(i).getName().equals(name))
301             {
302                 return getSequenceAt(i);
303             }
304
305             i++;
306         }
307
308         return null;
309     }
310
311     public SequenceI [] findSequenceMatch(String name)
312     {
313       Vector matches = new Vector();
314       int i = 0;
315
316       while (i < sequences.size())
317       {
318           if (getSequenceAt(i).getName().equals(name))
319           {
320               matches.addElement(getSequenceAt(i));
321           }
322           i++;
323       }
324
325       SequenceI [] result = new SequenceI[matches.size()];
326       for(i=0; i<result.length; i++)
327         result[i] = (SequenceI)matches.elementAt(i);
328
329       return result;
330
331     }
332
333
334     /**    */
335     public int findIndex(SequenceI s)
336     {
337         int i = 0;
338
339         while (i < sequences.size())
340         {
341             if (s == getSequenceAt(i))
342             {
343                 return i;
344             }
345
346             i++;
347         }
348
349         return -1;
350     }
351
352     /**
353      * DOCUMENT ME!
354      *
355      * @return DOCUMENT ME!
356      */
357     public int getHeight()
358     {
359         return sequences.size();
360     }
361
362     /**
363      * DOCUMENT ME!
364      *
365      * @return DOCUMENT ME!
366      */
367     public int getWidth()
368     {
369         int maxLength = -1;
370
371         for (int i = 0; i < sequences.size(); i++)
372         {
373             if (getSequenceAt(i).getLength() > maxLength)
374             {
375                 maxLength = getSequenceAt(i).getLength();
376             }
377         }
378
379         return maxLength;
380     }
381
382
383     /**
384      * DOCUMENT ME!
385      *
386      * @param gc DOCUMENT ME!
387      */
388     public void setGapCharacter(char gc)
389     {
390         gapCharacter = gc;
391
392         for (int i = 0; i < sequences.size(); i++)
393         {
394             Sequence seq = (Sequence) sequences.elementAt(i);
395             seq.setSequence( seq.getSequence().replace('.', gc) );
396             seq.setSequence( seq.getSequence().replace('-', gc) );
397             seq.setSequence( seq.getSequence().replace(' ', gc) );
398         }
399     }
400
401     /**
402      * DOCUMENT ME!
403      *
404      * @return DOCUMENT ME!
405      */
406     public char getGapCharacter()
407     {
408         return gapCharacter;
409     }
410
411
412     /**
413      * DOCUMENT ME!
414      *
415      * @return DOCUMENT ME!
416      */
417     public boolean isAligned()
418     {
419         int width = getWidth();
420
421         for (int i = 0; i < sequences.size(); i++)
422         {
423             if (getSequenceAt(i).getLength() != width)
424             {
425                 return false;
426             }
427         }
428
429         return true;
430     }
431
432     /**
433      * DOCUMENT ME!
434      *
435      * @param aa DOCUMENT ME!
436      */
437     public void deleteAnnotation(AlignmentAnnotation aa)
438     {
439         int aSize = 1;
440
441         if (annotations != null)
442         {
443             aSize = annotations.length;
444         }
445
446         AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize - 1];
447
448         int tIndex = 0;
449
450         for (int i = 0; i < aSize; i++)
451         {
452             if (annotations[i] == aa)
453             {
454                 continue;
455             }
456
457             temp[tIndex] = annotations[i];
458             tIndex++;
459         }
460
461         annotations = temp;
462     }
463
464
465     public void adjustSequenceAnnotations()
466     {
467       if(annotations!=null)
468       {
469         for (int a = 0; a < annotations.length; a++)
470         {
471           if (annotations[a].sequenceRef != null)
472           {
473             annotations[a].adjustForAlignment();
474           }
475         }
476       }
477     }
478
479     /**
480      * DOCUMENT ME!
481      *
482      * @param aa DOCUMENT ME!
483      */
484     public void addAnnotation(AlignmentAnnotation aa)
485     {
486         int aSize = 1;
487         if (annotations != null)
488         {
489             aSize = annotations.length + 1;
490         }
491
492         AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
493
494         temp[aSize-1] = aa;
495
496         int i = 0;
497
498         if (aSize > 1)
499         {
500             for (i = 0; i < (aSize-1); i++)
501             {
502                 temp[i] = annotations[i];
503             }
504         }
505
506         annotations = temp;
507     }
508
509     public void setAnnotationIndex(AlignmentAnnotation aa, int index)
510     {
511       if(aa==null || annotations==null || annotations.length-1<index)
512         return;
513
514       int aSize = annotations.length;
515       AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
516
517       temp[index] = aa;
518
519       for (int i = 0; i < aSize; i++)
520       {
521         if(i==index)
522           continue;
523
524         if(i<index)
525           temp[i] = annotations[i];
526         else
527           temp[i] = annotations[i-1];
528       }
529
530         annotations = temp;
531     }
532
533     /**
534      * DOCUMENT ME!
535      *
536      * @return DOCUMENT ME!
537      */
538     public AlignmentAnnotation[] getAlignmentAnnotation()
539     {
540         return annotations;
541     }
542
543     public void setNucleotide(boolean b)
544     {
545       if(b)
546         type = NUCLEOTIDE;
547       else
548         type = PROTEIN;
549     }
550
551     public boolean isNucleotide()
552     {
553       if(type==NUCLEOTIDE)
554         return true;
555       else
556         return false;
557     }
558
559     public void setDataset(Alignment data)
560     {
561       if(dataset==null && data==null)
562       {
563         // Create a new dataset for this alignment.
564         // Can only be done once, if dataset is not null
565         // This will not be performed
566         Sequence[] seqs = new Sequence[getHeight()];
567         for (int i = 0; i < getHeight(); i++)
568         {
569           if(getSequenceAt(i).getDatasetSequence()!=null)
570           {
571             seqs[i] = (Sequence)getSequenceAt(i).getDatasetSequence();
572           }
573           else
574           {
575             seqs[i] = new Sequence(getSequenceAt(i).getName(),
576                                    AlignSeq.extractGaps(
577                                        jalview.util.Comparison.GapChars,
578                                        getSequenceAt(i).getSequence()
579                                    ),
580                                    getSequenceAt(i).getStart(),
581                                    getSequenceAt(i).getEnd());
582             seqs[i].sequenceFeatures = getSequenceAt(i).getSequenceFeatures();
583             getSequenceAt(i).setSequenceFeatures(null);
584             getSequenceAt(i).setDatasetSequence(seqs[i]);
585           }
586         }
587
588         dataset = new Alignment(seqs);
589       }
590       else if(dataset==null && data!=null)
591       {
592         dataset = data;
593       }
594     }
595
596     public Alignment getDataset()
597     {
598       return dataset;
599     }
600
601     public boolean padGaps() {
602       boolean modified=false;
603
604       //Remove excess gaps from the end of alignment
605       int maxLength = -1;
606
607       SequenceI current;
608       for (int i = 0; i < sequences.size(); i++)
609       {
610         current = getSequenceAt(i);
611         for (int j = current.getLength(); j > maxLength; j--)
612         {
613           if (j > maxLength && !jalview.util.Comparison.isGap(
614               current.getCharAt(j)))
615           {
616             maxLength = j;
617             break;
618           }
619         }
620       }
621
622       maxLength++;
623
624       for (int i = 0; i < sequences.size();
625            i++)
626       {
627         current = getSequenceAt(i);
628
629         if (current.getLength() < maxLength)
630         {
631           current.insertCharAt(maxLength - 1, gapCharacter);
632           modified=true;
633         }
634         else if(current.getLength() > maxLength)
635         {
636           current.deleteChars(maxLength, current.getLength());
637         }
638       }
639       return modified;
640     }
641
642     public HiddenSequences getHiddenSequences()
643     {
644       return hiddenSequences;
645     }
646     SequenceI [] getVisibleAndRepresentedSeqs()
647     {
648       if(hiddenSequences==null || hiddenSequences.getSize()<1)
649         return getSequencesArray();
650
651       Vector seqs = new Vector();
652       SequenceI seq;
653       SequenceGroup hidden;
654       for (int i = 0; i < sequences.size(); i++)
655       {
656         seq = (SequenceI) sequences.elementAt(i);
657         seqs.addElement(seq);
658         hidden = seq.getHiddenSequences();
659         if(hidden!=null)
660         {
661           for(int j=0; j<hidden.getSize(false); j++)
662           {
663             seqs.addElement(hidden.getSequenceAt(j));
664           }
665         }
666       }
667       SequenceI [] result = new SequenceI[seqs.size()];
668       for(int i=0; i<seqs.size(); i++)
669         result[i] = (SequenceI)seqs.elementAt(i);
670
671       return result;
672
673     }
674
675   public CigarArray getCompactAlignment()
676   {
677     SeqCigar alseqs[] = new SeqCigar[sequences.size()];
678     for (int i=0; i<sequences.size(); i++) {
679       alseqs[i] = new SeqCigar((SequenceI) sequences.elementAt(i));
680     }
681     CigarArray cal = new CigarArray(alseqs);
682     cal.addOperation(CigarArray.M, getWidth());
683     return cal;
684   }
685
686 }