Sequence colour moved to viewport
[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
274     /**    */
275     public void deleteGroup(SequenceGroup g)
276     {
277         if (groups.contains(g))
278         {
279             groups.removeElement(g);
280         }
281     }
282
283     /**    */
284     public SequenceI findName(String name)
285     {
286         int i = 0;
287
288         while (i < sequences.size())
289         {
290             if (getSequenceAt(i).getName().equals(name))
291             {
292                 return getSequenceAt(i);
293             }
294
295             i++;
296         }
297
298         return null;
299     }
300
301     public SequenceI [] findSequenceMatch(String name)
302     {
303       Vector matches = new Vector();
304       int i = 0;
305
306       while (i < sequences.size())
307       {
308           if (getSequenceAt(i).getName().equals(name))
309           {
310               matches.addElement(getSequenceAt(i));
311           }
312           i++;
313       }
314
315       SequenceI [] result = new SequenceI[matches.size()];
316       for(i=0; i<result.length; i++)
317         result[i] = (SequenceI)matches.elementAt(i);
318
319       return result;
320
321     }
322
323
324     /**    */
325     public int findIndex(SequenceI s)
326     {
327         int i = 0;
328
329         while (i < sequences.size())
330         {
331             if (s == getSequenceAt(i))
332             {
333                 return i;
334             }
335
336             i++;
337         }
338
339         return -1;
340     }
341
342     /**
343      * DOCUMENT ME!
344      *
345      * @return DOCUMENT ME!
346      */
347     public int getHeight()
348     {
349         return sequences.size();
350     }
351
352     /**
353      * DOCUMENT ME!
354      *
355      * @return DOCUMENT ME!
356      */
357     public int getWidth()
358     {
359         int maxLength = -1;
360
361         for (int i = 0; i < sequences.size(); i++)
362         {
363             if (getSequenceAt(i).getLength() > maxLength)
364             {
365                 maxLength = getSequenceAt(i).getLength();
366             }
367         }
368
369         return maxLength;
370     }
371
372
373     /**
374      * DOCUMENT ME!
375      *
376      * @param gc DOCUMENT ME!
377      */
378     public void setGapCharacter(char gc)
379     {
380         gapCharacter = gc;
381
382         for (int i = 0; i < sequences.size(); i++)
383         {
384             Sequence seq = (Sequence) sequences.elementAt(i);
385             seq.setSequence( seq.getSequence().replace('.', gc) );
386             seq.setSequence( seq.getSequence().replace('-', gc) );
387             seq.setSequence( seq.getSequence().replace(' ', gc) );
388         }
389     }
390
391     /**
392      * DOCUMENT ME!
393      *
394      * @return DOCUMENT ME!
395      */
396     public char getGapCharacter()
397     {
398         return gapCharacter;
399     }
400
401
402     /**
403      * DOCUMENT ME!
404      *
405      * @return DOCUMENT ME!
406      */
407     public boolean isAligned()
408     {
409         int width = getWidth();
410
411         for (int i = 0; i < sequences.size(); i++)
412         {
413             if (getSequenceAt(i).getLength() != width)
414             {
415                 return false;
416             }
417         }
418
419         return true;
420     }
421
422     /**
423      * DOCUMENT ME!
424      *
425      * @param aa DOCUMENT ME!
426      */
427     public void deleteAnnotation(AlignmentAnnotation aa)
428     {
429         int aSize = 1;
430
431         if (annotations != null)
432         {
433             aSize = annotations.length;
434         }
435
436         if(aSize<1)
437           return;
438
439         AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize - 1];
440
441         int tIndex = 0;
442
443         for (int i = 0; i < aSize; i++)
444         {
445             if (annotations[i] == aa)
446             {
447                 continue;
448             }
449
450             temp[tIndex] = annotations[i];
451             tIndex++;
452         }
453
454         annotations = temp;
455     }
456
457
458     public void adjustSequenceAnnotations()
459     {
460       if(annotations!=null)
461       {
462         for (int a = 0; a < annotations.length; a++)
463         {
464           if (annotations[a].sequenceRef != null)
465           {
466             annotations[a].adjustForAlignment();
467           }
468         }
469       }
470     }
471
472     /**
473      * DOCUMENT ME!
474      *
475      * @param aa DOCUMENT ME!
476      */
477     public void addAnnotation(AlignmentAnnotation aa)
478     {
479         int aSize = 1;
480         if (annotations != null)
481         {
482             aSize = annotations.length + 1;
483         }
484
485         AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
486
487         temp[aSize-1] = aa;
488
489         int i = 0;
490
491         if (aSize > 1)
492         {
493             for (i = 0; i < (aSize-1); i++)
494             {
495                 temp[i] = annotations[i];
496             }
497         }
498
499         annotations = temp;
500     }
501
502     public void setAnnotationIndex(AlignmentAnnotation aa, int index)
503     {
504       if(aa==null || annotations==null || annotations.length-1<index)
505         return;
506
507       int aSize = annotations.length;
508       AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
509
510       temp[index] = aa;
511
512       for (int i = 0; i < aSize; i++)
513       {
514         if(i==index)
515           continue;
516
517         if(i<index)
518           temp[i] = annotations[i];
519         else
520           temp[i] = annotations[i-1];
521       }
522
523         annotations = temp;
524     }
525
526     /**
527      * DOCUMENT ME!
528      *
529      * @return DOCUMENT ME!
530      */
531     public AlignmentAnnotation[] getAlignmentAnnotation()
532     {
533         return annotations;
534     }
535
536     public void setNucleotide(boolean b)
537     {
538       if(b)
539         type = NUCLEOTIDE;
540       else
541         type = PROTEIN;
542     }
543
544     public boolean isNucleotide()
545     {
546       if(type==NUCLEOTIDE)
547         return true;
548       else
549         return false;
550     }
551
552     public void setDataset(Alignment data)
553     {
554       if(dataset==null && data==null)
555       {
556         // Create a new dataset for this alignment.
557         // Can only be done once, if dataset is not null
558         // This will not be performed
559         Sequence[] seqs = new Sequence[getHeight()];
560         SequenceI currentSeq;
561         for (int i = 0; i < getHeight(); i++)
562         {
563           currentSeq = getSequenceAt(i);
564           if(currentSeq.getDatasetSequence()!=null)
565           {
566             seqs[i] = (Sequence)currentSeq.getDatasetSequence();
567           }
568           else
569           {
570             seqs[i] = new Sequence(currentSeq.getName(),
571                                    AlignSeq.extractGaps(
572                                        jalview.util.Comparison.GapChars,
573                                        currentSeq.getSequence()
574                                    ),
575                                    currentSeq.getStart(),
576                                    currentSeq.getEnd());
577             seqs[i].sequenceFeatures = currentSeq.getSequenceFeatures();
578             seqs[i].setDescription(currentSeq.getDescription());
579             getSequenceAt(i).setSequenceFeatures(null);
580             getSequenceAt(i).setDatasetSequence(seqs[i]);
581           }
582         }
583
584         dataset = new Alignment(seqs);
585       }
586       else if(dataset==null && data!=null)
587       {
588         dataset = data;
589       }
590     }
591
592     public Alignment getDataset()
593     {
594       return dataset;
595     }
596
597     public boolean padGaps()
598     {
599       boolean modified=false;
600
601       //Remove excess gaps from the end of alignment
602       int maxLength = -1;
603
604       SequenceI current;
605       for (int i = 0; i < sequences.size(); i++)
606       {
607         current = getSequenceAt(i);
608         for (int j = current.getLength(); j > maxLength; j--)
609         {
610           if (j > maxLength && !jalview.util.Comparison.isGap(
611               current.getCharAt(j)))
612           {
613             maxLength = j;
614             break;
615           }
616         }
617       }
618
619       maxLength++;
620
621       int cLength;
622       for (int i = 0; i < sequences.size();
623            i++)
624       {
625         current = getSequenceAt(i);
626         cLength = current.getLength();
627
628         if (cLength < maxLength)
629         {
630           current.insertCharAt(cLength,
631                               maxLength-cLength, 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 }