8b159a63de117095db79ad5bbb7d6c22e784d88c
[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()
401                             .replace('.', gc)
402                             .replace('-', gc)
403                             .replace(' ', gc)
404                 );
405         }
406     }
407
408     /**
409      * DOCUMENT ME!
410      *
411      * @return DOCUMENT ME!
412      */
413     public char getGapCharacter()
414     {
415         return gapCharacter;
416     }
417
418
419     /**
420      * DOCUMENT ME!
421      *
422      * @return DOCUMENT ME!
423      */
424     public boolean isAligned()
425     {
426         int width = getWidth();
427
428         for (int i = 0; i < sequences.size(); i++)
429         {
430             if (getSequenceAt(i).getLength() != width)
431             {
432                 return false;
433             }
434         }
435
436         return true;
437     }
438
439     /**
440      * DOCUMENT ME!
441      *
442      * @param aa DOCUMENT ME!
443      */
444     public void deleteAnnotation(AlignmentAnnotation aa)
445     {
446         int aSize = 1;
447
448         if (annotations != null)
449         {
450             aSize = annotations.length;
451         }
452
453         if(aSize<1)
454           return;
455
456         AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize - 1];
457
458         int tIndex = 0;
459
460         for (int i = 0; i < aSize; i++)
461         {
462             if (annotations[i] == aa)
463             {
464                 continue;
465             }
466
467             temp[tIndex] = annotations[i];
468             tIndex++;
469         }
470
471         annotations = temp;
472     }
473
474
475     /**
476      * DOCUMENT ME!
477      *
478      * @param aa DOCUMENT ME!
479      */
480     public void addAnnotation(AlignmentAnnotation aa)
481     {
482         int aSize = 1;
483         if (annotations != null)
484         {
485             aSize = annotations.length + 1;
486         }
487
488         AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
489
490         temp[aSize-1] = aa;
491
492         int i = 0;
493
494         if (aSize > 1)
495         {
496             for (i = 0; i < (aSize-1); i++)
497             {
498                 temp[i] = annotations[i];
499             }
500         }
501
502         annotations = temp;
503     }
504
505     public void setAnnotationIndex(AlignmentAnnotation aa, int index)
506     {
507       if(aa==null || annotations==null || annotations.length-1<index)
508         return;
509
510       int aSize = annotations.length;
511       AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
512
513       temp[index] = aa;
514
515       for (int i = 0; i < aSize; i++)
516       {
517         if(i==index)
518           continue;
519
520         if(i<index)
521           temp[i] = annotations[i];
522         else
523           temp[i] = annotations[i-1];
524       }
525
526         annotations = temp;
527     }
528
529     /**
530      * DOCUMENT ME!
531      *
532      * @return DOCUMENT ME!
533      */
534     public AlignmentAnnotation[] getAlignmentAnnotation()
535     {
536         return annotations;
537     }
538
539     public void setNucleotide(boolean b)
540     {
541       if(b)
542         type = NUCLEOTIDE;
543       else
544         type = PROTEIN;
545     }
546
547     public boolean isNucleotide()
548     {
549       if(type==NUCLEOTIDE)
550         return true;
551       else
552         return false;
553     }
554
555     public void setDataset(Alignment data)
556     {
557       if(dataset==null && data==null)
558       {
559         // Create a new dataset for this alignment.
560         // Can only be done once, if dataset is not null
561         // This will not be performed
562         Sequence[] seqs = new Sequence[getHeight()];
563         SequenceI currentSeq;
564         for (int i = 0; i < getHeight(); i++)
565         {
566           currentSeq = getSequenceAt(i);
567           if(currentSeq.getDatasetSequence()!=null)
568           {
569             seqs[i] = (Sequence)currentSeq.getDatasetSequence();
570           }
571           else
572           {
573             seqs[i] = new Sequence(currentSeq.getName(),
574                                    AlignSeq.extractGaps(
575                                        jalview.util.Comparison.GapChars,
576                                        currentSeq.getSequenceAsString()
577                                    ),
578                                    currentSeq.getStart(),
579                                    currentSeq.getEnd());
580             seqs[i].sequenceFeatures = currentSeq.getSequenceFeatures();
581             seqs[i].setDescription(currentSeq.getDescription());
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
650   public CigarArray getCompactAlignment()
651   {
652     SeqCigar alseqs[] = new SeqCigar[sequences.size()];
653     for (int i=0; i<sequences.size(); i++) {
654       alseqs[i] = new SeqCigar((SequenceI) sequences.elementAt(i));
655     }
656     CigarArray cal = new CigarArray(alseqs);
657     cal.addOperation(CigarArray.M, getWidth());
658     return cal;
659   }
660
661 }