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