Annotation adjustment moved to EditCommand
[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 java.util.*;
24
25 /** Data structure to hold and manipulate a multiple sequence alignment
26  */
27 public class Alignment implements AlignmentI
28 {
29     protected Alignment dataset;
30     protected Vector sequences;
31     protected Vector groups = new Vector();
32     protected char gapCharacter = '-';
33     protected int type = NUCLEOTIDE;
34     public static final int PROTEIN = 0;
35     public static final int NUCLEOTIDE = 1;
36
37     /** DOCUMENT ME!! */
38     public AlignmentAnnotation[] annotations;
39
40     HiddenSequences hiddenSequences = new HiddenSequences(this);
41
42     private void initAlignment(SequenceI[] seqs) {
43       int i=0;
44
45       if( jalview.util.Comparison.isNucleotide(seqs))
46         type = NUCLEOTIDE;
47       else
48         type = PROTEIN;
49
50       sequences = new Vector();
51
52       for (i = 0; i < seqs.length; i++)
53       {
54         sequences.addElement(seqs[i]);
55       }
56
57     }
58     /** Make an alignment from an array of Sequences.
59      *
60      * @param sequences
61      */
62     public Alignment(SequenceI[] seqs)
63     {
64       initAlignment(seqs);
65     }
66     /**
67      * Make a new alignment from an array of SeqCigars
68      * @param seqs SeqCigar[]
69      */
70     public Alignment(SeqCigar[] alseqs) {
71       SequenceI[] seqs = SeqCigar.createAlignmentSequences(alseqs, gapCharacter, new ColumnSelection(), null);
72       initAlignment(seqs);
73     }
74     /**
75      * Make a new alignment from an CigarArray
76      * JBPNote - can only do this when compactAlignment does not contain hidden regions.
77      * JBPNote - must also check that compactAlignment resolves to a set of SeqCigars - or construct them appropriately.
78      * @param compactAlignment CigarArray
79      */
80     public static AlignmentI createAlignment(CigarArray compactAlignment) {
81       throw new Error("Alignment(CigarArray) not yet implemented");
82       // this(compactAlignment.refCigars);
83     }
84
85     /**
86      * DOCUMENT ME!
87      *
88      * @return DOCUMENT ME!
89      */
90     public Vector getSequences()
91     {
92         return sequences;
93     }
94
95     public SequenceI [] getSequencesArray()
96     {
97       SequenceI [] reply = new SequenceI[sequences.size()];
98       for(int i=0; i<sequences.size(); i++)
99       {
100         reply[i] = (SequenceI)sequences.elementAt(i);
101       }
102       return reply;
103     }
104
105     /**
106      * DOCUMENT ME!
107      *
108      * @param i DOCUMENT ME!
109      *
110      * @return DOCUMENT ME!
111      */
112     public SequenceI getSequenceAt(int i)
113     {
114         if (i < sequences.size())
115         {
116             return (SequenceI) sequences.elementAt(i);
117         }
118
119         return null;
120     }
121
122     /** Adds a sequence to the alignment.  Recalculates maxLength and size.
123      *
124      * @param snew
125      */
126     public void addSequence(SequenceI snew)
127     {
128       if(dataset!=null)
129       {
130         if(snew.getDatasetSequence()!=null)
131         {
132           getDataset().addSequence(snew.getDatasetSequence());
133         }
134         else
135         {
136           Sequence ds = new Sequence(snew.getName(),
137                                      AlignSeq.extractGaps("-. ",
138               snew.getSequenceAsString()),
139                                      snew.getStart(),
140                                      snew.getEnd());
141
142           snew.setDatasetSequence(ds);
143           getDataset().addSequence(ds);
144         }
145       }
146       sequences.addElement(snew);
147
148       hiddenSequences.adjustHeightSequenceAdded();
149     }
150
151
152     /** Adds a sequence to the alignment.  Recalculates maxLength and size.
153      *
154      * @param snew
155      */
156     public void setSequenceAt(int i, SequenceI snew)
157     {
158         SequenceI oldseq = getSequenceAt(i);
159         deleteSequence(oldseq);
160
161         sequences.setElementAt(snew, i);
162     }
163
164     /**
165      * DOCUMENT ME!
166      *
167      * @return DOCUMENT ME!
168      */
169     public Vector getGroups()
170     {
171         return groups;
172     }
173
174     /**
175      * DOCUMENT ME!
176      *
177      * @param s DOCUMENT ME!
178      */
179     public void deleteSequence(SequenceI s)
180     {
181       deleteSequence(findIndex(s));
182     }
183
184     /**
185      * DOCUMENT ME!
186      *
187      * @param i DOCUMENT ME!
188      */
189     public void deleteSequence(int i)
190     {
191       if(i>-1 && i<getHeight())
192       {
193         sequences.removeElementAt(i);
194         hiddenSequences.adjustHeightSequenceDeleted(i);
195       }
196     }
197
198
199     /**    */
200     public SequenceGroup findGroup(SequenceI s)
201     {
202         for (int i = 0; i < this.groups.size(); i++)
203         {
204             SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
205
206             if (sg.getSequences(null).contains(s))
207             {
208                 return sg;
209             }
210         }
211
212         return null;
213     }
214
215     /**
216      * DOCUMENT ME!
217      *
218      * @param s DOCUMENT ME!
219      *
220      * @return DOCUMENT ME!
221      */
222     public SequenceGroup[] findAllGroups(SequenceI s)
223     {
224         Vector temp = new Vector();
225
226         int gSize = groups.size();
227         for (int i = 0; i < gSize; i++)
228         {
229             SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
230             if(sg==null || sg.getSequences(null)==null)
231             {
232               this.deleteGroup(sg);
233               gSize--;
234               continue;
235             }
236
237             if (sg.getSequences(null).contains(s))
238             {
239                 temp.addElement(sg);
240             }
241         }
242
243         SequenceGroup[] ret = new SequenceGroup[temp.size()];
244
245         for (int i = 0; i < temp.size(); i++)
246         {
247             ret[i] = (SequenceGroup) temp.elementAt(i);
248         }
249
250         return ret;
251     }
252
253
254
255     /**    */
256     public void addGroup(SequenceGroup sg)
257     {
258         if (!groups.contains(sg))
259         {
260           if(hiddenSequences.getSize()>0)
261           {
262             int i, iSize = sg.getSize();
263             for (i = 0; i < iSize; i++)
264             {
265               if (!sequences.contains(sg.getSequenceAt(i)))
266               {
267                 sg.deleteSequence(sg.getSequenceAt(i), false);
268                 iSize--;
269                 i--;
270               }
271             }
272
273             if (sg.getSize() < 1)
274               return;
275           }
276
277           groups.addElement(sg);
278         }
279     }
280
281     /**
282      * DOCUMENT ME!
283      */
284     public void deleteAllGroups()
285     {
286         groups.removeAllElements();
287     }
288
289     /**    */
290     public void deleteGroup(SequenceGroup g)
291     {
292         if (groups.contains(g))
293         {
294             groups.removeElement(g);
295         }
296     }
297
298     /**    */
299     public SequenceI findName(String name)
300     {
301         int i = 0;
302
303         while (i < sequences.size())
304         {
305             if (getSequenceAt(i).getName().equals(name))
306             {
307                 return getSequenceAt(i);
308             }
309
310             i++;
311         }
312
313         return null;
314     }
315
316     public SequenceI [] findSequenceMatch(String name)
317     {
318       Vector matches = new Vector();
319       int i = 0;
320
321       while (i < sequences.size())
322       {
323           if (getSequenceAt(i).getName().equals(name))
324           {
325               matches.addElement(getSequenceAt(i));
326           }
327           i++;
328       }
329
330       SequenceI [] result = new SequenceI[matches.size()];
331       for(i=0; i<result.length; i++)
332         result[i] = (SequenceI)matches.elementAt(i);
333
334       return result;
335
336     }
337
338
339     /**    */
340     public int findIndex(SequenceI s)
341     {
342         int i = 0;
343
344         while (i < sequences.size())
345         {
346             if (s == getSequenceAt(i))
347             {
348                 return i;
349             }
350
351             i++;
352         }
353
354         return -1;
355     }
356
357     /**
358      * DOCUMENT ME!
359      *
360      * @return DOCUMENT ME!
361      */
362     public int getHeight()
363     {
364         return sequences.size();
365     }
366
367     /**
368      * DOCUMENT ME!
369      *
370      * @return DOCUMENT ME!
371      */
372     public int getWidth()
373     {
374         int maxLength = -1;
375
376         for (int i = 0; i < sequences.size(); i++)
377         {
378             if (getSequenceAt(i).getLength() > maxLength)
379             {
380                 maxLength = getSequenceAt(i).getLength();
381             }
382         }
383
384         return maxLength;
385     }
386
387
388     /**
389      * DOCUMENT ME!
390      *
391      * @param gc DOCUMENT ME!
392      */
393     public void setGapCharacter(char gc)
394     {
395         gapCharacter = gc;
396
397         for (int i = 0; i < sequences.size(); i++)
398         {
399             Sequence seq = (Sequence) sequences.elementAt(i);
400             seq.setSequence( seq.getSequenceAsString().replace('.', gc) );
401             seq.setSequence( seq.getSequenceAsString().replace('-', gc) );
402             seq.setSequence( seq.getSequenceAsString().replace(' ', gc) );
403         }
404     }
405
406     /**
407      * DOCUMENT ME!
408      *
409      * @return DOCUMENT ME!
410      */
411     public char getGapCharacter()
412     {
413         return gapCharacter;
414     }
415
416
417     /**
418      * DOCUMENT ME!
419      *
420      * @return DOCUMENT ME!
421      */
422     public boolean isAligned()
423     {
424         int width = getWidth();
425
426         for (int i = 0; i < sequences.size(); i++)
427         {
428             if (getSequenceAt(i).getLength() != width)
429             {
430                 return false;
431             }
432         }
433
434         return true;
435     }
436
437     /**
438      * DOCUMENT ME!
439      *
440      * @param aa DOCUMENT ME!
441      */
442     public void deleteAnnotation(AlignmentAnnotation aa)
443     {
444         int aSize = 1;
445
446         if (annotations != null)
447         {
448             aSize = annotations.length;
449         }
450
451         if(aSize<1)
452           return;
453
454         AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize - 1];
455
456         int tIndex = 0;
457
458         for (int i = 0; i < aSize; i++)
459         {
460             if (annotations[i] == aa)
461             {
462                 continue;
463             }
464
465             temp[tIndex] = annotations[i];
466             tIndex++;
467         }
468
469         annotations = temp;
470     }
471
472
473     /**
474      * DOCUMENT ME!
475      *
476      * @param aa DOCUMENT ME!
477      */
478     public void addAnnotation(AlignmentAnnotation aa)
479     {
480         int aSize = 1;
481         if (annotations != null)
482         {
483             aSize = annotations.length + 1;
484         }
485
486         AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
487
488         temp[aSize-1] = aa;
489
490         int i = 0;
491
492         if (aSize > 1)
493         {
494             for (i = 0; i < (aSize-1); i++)
495             {
496                 temp[i] = annotations[i];
497             }
498         }
499
500         annotations = temp;
501     }
502
503     public void setAnnotationIndex(AlignmentAnnotation aa, int index)
504     {
505       if(aa==null || annotations==null || annotations.length-1<index)
506         return;
507
508       int aSize = annotations.length;
509       AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
510
511       temp[index] = aa;
512
513       for (int i = 0; i < aSize; i++)
514       {
515         if(i==index)
516           continue;
517
518         if(i<index)
519           temp[i] = annotations[i];
520         else
521           temp[i] = annotations[i-1];
522       }
523
524         annotations = temp;
525     }
526
527     /**
528      * DOCUMENT ME!
529      *
530      * @return DOCUMENT ME!
531      */
532     public AlignmentAnnotation[] getAlignmentAnnotation()
533     {
534         return annotations;
535     }
536
537     public void setNucleotide(boolean b)
538     {
539       if(b)
540         type = NUCLEOTIDE;
541       else
542         type = PROTEIN;
543     }
544
545     public boolean isNucleotide()
546     {
547       if(type==NUCLEOTIDE)
548         return true;
549       else
550         return false;
551     }
552
553     public void setDataset(Alignment data)
554     {
555       if(dataset==null && data==null)
556       {
557         // Create a new dataset for this alignment.
558         // Can only be done once, if dataset is not null
559         // This will not be performed
560         Sequence[] seqs = new Sequence[getHeight()];
561         SequenceI currentSeq;
562         for (int i = 0; i < getHeight(); i++)
563         {
564           currentSeq = getSequenceAt(i);
565           if(currentSeq.getDatasetSequence()!=null)
566           {
567             seqs[i] = (Sequence)currentSeq.getDatasetSequence();
568           }
569           else
570           {
571             seqs[i] = new Sequence(currentSeq.getName(),
572                                    AlignSeq.extractGaps(
573                                        jalview.util.Comparison.GapChars,
574                                        currentSeq.getSequenceAsString()
575                                    ),
576                                    currentSeq.getStart(),
577                                    currentSeq.getEnd());
578             seqs[i].sequenceFeatures = currentSeq.getSequenceFeatures();
579             seqs[i].setDescription(currentSeq.getDescription());
580             getSequenceAt(i).setSequenceFeatures(null);
581             getSequenceAt(i).setDatasetSequence(seqs[i]);
582           }
583         }
584
585         dataset = new Alignment(seqs);
586       }
587       else if(dataset==null && data!=null)
588       {
589         dataset = data;
590       }
591     }
592
593     public Alignment getDataset()
594     {
595       return dataset;
596     }
597
598     public boolean padGaps()
599     {
600       boolean modified=false;
601
602       //Remove excess gaps from the end of alignment
603       int maxLength = -1;
604
605       SequenceI current;
606       for (int i = 0; i < sequences.size(); i++)
607       {
608         current = getSequenceAt(i);
609         for (int j = current.getLength(); j > maxLength; j--)
610         {
611           if (j > maxLength && !jalview.util.Comparison.isGap(
612               current.getCharAt(j)))
613           {
614             maxLength = j;
615             break;
616           }
617         }
618       }
619
620       maxLength++;
621
622       int cLength;
623       for (int i = 0; i < sequences.size();
624            i++)
625       {
626         current = getSequenceAt(i);
627         cLength = current.getLength();
628
629         if (cLength < maxLength)
630         {
631           current.insertCharAt(cLength,
632                               maxLength-cLength, gapCharacter);
633           modified=true;
634         }
635         else if(current.getLength() > maxLength)
636         {
637           current.deleteChars(maxLength, current.getLength());
638         }
639       }
640       return modified;
641     }
642
643     public HiddenSequences getHiddenSequences()
644     {
645       return hiddenSequences;
646     }
647
648   public CigarArray getCompactAlignment()
649   {
650     SeqCigar alseqs[] = new SeqCigar[sequences.size()];
651     for (int i=0; i<sequences.size(); i++) {
652       alseqs[i] = new SeqCigar((SequenceI) sequences.elementAt(i));
653     }
654     CigarArray cal = new CigarArray(alseqs);
655     cal.addOperation(CigarArray.M, getWidth());
656     return cal;
657   }
658
659 }