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