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