remove seqref annotations
[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       // maintain dataset integrity
143       if (snew.getDatasetSequence() != null)
144       {
145         getDataset().addSequence(snew.getDatasetSequence());
146       }
147       else
148       {
149         // derive new sequence
150         SequenceI adding = snew.deriveSequence();
151         getDataset().addSequence(adding.getDatasetSequence());
152         snew = adding;
153       }
154     }
155     if (sequences==null) {
156       initAlignment(new SequenceI[] { snew });
157     } else {
158       sequences.addElement(snew);
159     }
160     if (hiddenSequences!=null)
161       hiddenSequences.adjustHeightSequenceAdded();
162   }
163
164   /** Adds a sequence to the alignment.  Recalculates maxLength and size.
165    *
166    * @param snew
167    */
168   public void setSequenceAt(int i, SequenceI snew)
169   {
170     SequenceI oldseq = getSequenceAt(i);
171     deleteSequence(oldseq);
172
173     sequences.setElementAt(snew, i);
174   }
175
176   /**
177    * DOCUMENT ME!
178    *
179    * @return DOCUMENT ME!
180    */
181   public Vector getGroups()
182   {
183     return groups;
184   }
185
186   public void finalize()
187   {
188     if(getDataset()!=null)
189       getDataset().finalize();
190
191     dataset = null;
192     sequences = null;
193     groups = null;
194     annotations = null;
195     hiddenSequences = null;
196   }
197
198
199   /**
200    * DOCUMENT ME!
201    *
202    * @param s DOCUMENT ME!
203    */
204   public void deleteSequence(SequenceI s)
205   {
206     deleteSequence(findIndex(s));
207   }
208
209   /**
210    * DOCUMENT ME!
211    *
212    * @param i DOCUMENT ME!
213    */
214   public void deleteSequence(int i)
215   {
216     if (i > -1 && i < getHeight())
217     {
218       sequences.removeElementAt(i);
219       hiddenSequences.adjustHeightSequenceDeleted(i);
220     }
221   }
222
223   /**    */
224   public SequenceGroup findGroup(SequenceI s)
225   {
226     for (int i = 0; i < this.groups.size(); i++)
227     {
228       SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
229
230       if (sg.getSequences(null).contains(s))
231       {
232         return sg;
233       }
234     }
235
236     return null;
237   }
238
239   /**
240    * DOCUMENT ME!
241    *
242    * @param s DOCUMENT ME!
243    *
244    * @return DOCUMENT ME!
245    */
246   public SequenceGroup[] findAllGroups(SequenceI s)
247   {
248     Vector temp = new Vector();
249
250     int gSize = groups.size();
251     for (int i = 0; i < gSize; i++)
252     {
253       SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
254       if (sg == null || sg.getSequences(null) == null)
255       {
256         this.deleteGroup(sg);
257         gSize--;
258         continue;
259       }
260
261       if (sg.getSequences(null).contains(s))
262       {
263         temp.addElement(sg);
264       }
265     }
266
267     SequenceGroup[] ret = new SequenceGroup[temp.size()];
268
269     for (int i = 0; i < temp.size(); i++)
270     {
271       ret[i] = (SequenceGroup) temp.elementAt(i);
272     }
273
274     return ret;
275   }
276
277   /**    */
278   public void addGroup(SequenceGroup sg)
279   {
280     if (!groups.contains(sg))
281     {
282       if (hiddenSequences.getSize() > 0)
283       {
284         int i, iSize = sg.getSize();
285         for (i = 0; i < iSize; i++)
286         {
287           if (!sequences.contains(sg.getSequenceAt(i)))
288           {
289             sg.deleteSequence(sg.getSequenceAt(i), false);
290             iSize--;
291             i--;
292           }
293         }
294
295         if (sg.getSize() < 1)
296         {
297           return;
298         }
299       }
300
301       groups.addElement(sg);
302     }
303   }
304
305   /**
306    * DOCUMENT ME!
307    */
308   public void deleteAllGroups()
309   {
310     groups.removeAllElements();
311   }
312
313   /**    */
314   public void deleteGroup(SequenceGroup g)
315   {
316     if (groups.contains(g))
317     {
318       groups.removeElement(g);
319     }
320   }
321
322   /**    */
323   public SequenceI findName(String name)
324   {
325     int i = 0;
326
327     while (i < sequences.size())
328     {
329       if (getSequenceAt(i).getName().equals(name))
330       {
331         return getSequenceAt(i);
332       }
333
334       i++;
335     }
336
337     return null;
338   }
339
340   public SequenceI[] findSequenceMatch(String name)
341   {
342     Vector matches = new Vector();
343     int i = 0;
344
345     while (i < sequences.size())
346     {
347       if (getSequenceAt(i).getName().equals(name))
348       {
349         matches.addElement(getSequenceAt(i));
350       }
351       i++;
352     }
353
354     SequenceI[] result = new SequenceI[matches.size()];
355     for (i = 0; i < result.length; i++)
356     {
357       result[i] = (SequenceI) matches.elementAt(i);
358     }
359
360     return result;
361
362   }
363
364   /**    */
365   public int findIndex(SequenceI s)
366   {
367     int i = 0;
368
369     while (i < sequences.size())
370     {
371       if (s == getSequenceAt(i))
372       {
373         return i;
374       }
375
376       i++;
377     }
378
379     return -1;
380   }
381
382   /**
383    * DOCUMENT ME!
384    *
385    * @return DOCUMENT ME!
386    */
387   public int getHeight()
388   {
389     return sequences.size();
390   }
391
392   /**
393    * DOCUMENT ME!
394    *
395    * @return DOCUMENT ME!
396    */
397   public int getWidth()
398   {
399     int maxLength = -1;
400
401     for (int i = 0; i < sequences.size(); i++)
402     {
403       if (getSequenceAt(i).getLength() > maxLength)
404       {
405         maxLength = getSequenceAt(i).getLength();
406       }
407     }
408
409     return maxLength;
410   }
411
412   /**
413    * DOCUMENT ME!
414    *
415    * @param gc DOCUMENT ME!
416    */
417   public void setGapCharacter(char gc)
418   {
419     gapCharacter = gc;
420
421     for (int i = 0; i < sequences.size(); i++)
422     {
423       Sequence seq = (Sequence) sequences.elementAt(i);
424       seq.setSequence(seq.getSequenceAsString()
425                       .replace('.', gc)
426                       .replace('-', gc)
427                       .replace(' ', gc)
428           );
429     }
430   }
431
432   /**
433    * DOCUMENT ME!
434    *
435    * @return DOCUMENT ME!
436    */
437   public char getGapCharacter()
438   {
439     return gapCharacter;
440   }
441
442   /**
443    * DOCUMENT ME!
444    *
445    * @return DOCUMENT ME!
446    */
447   public boolean isAligned()
448   {
449     int width = getWidth();
450
451     for (int i = 0; i < sequences.size(); i++)
452     {
453       if (getSequenceAt(i).getLength() != width)
454       {
455         return false;
456       }
457     }
458
459     return true;
460   }
461
462   /**
463    * DOCUMENT ME!
464    *
465    * @param aa DOCUMENT ME!
466    */
467   public void deleteAnnotation(AlignmentAnnotation aa)
468   {
469     if(aa.sequenceRef!=null)
470       aa.sequenceRef.removeAlignmentAnnotation(aa);
471
472     int aSize = 1;
473
474     if (annotations != null)
475     {
476       aSize = annotations.length;
477     }
478
479     if (aSize < 1)
480     {
481       return;
482     }
483
484     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize - 1];
485
486     int tIndex = 0;
487
488     for (int i = 0; i < aSize; i++)
489     {
490       if (annotations[i] == aa)
491       {
492         continue;
493       }
494
495       temp[tIndex] = annotations[i];
496       tIndex++;
497     }
498
499     annotations = temp;
500   }
501
502   /**
503    * DOCUMENT ME!
504    *
505    * @param aa DOCUMENT ME!
506    */
507   public void addAnnotation(AlignmentAnnotation aa)
508   {
509     int aSize = 1;
510     if (annotations != null)
511     {
512       aSize = annotations.length + 1;
513     }
514
515     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
516
517     temp[aSize - 1] = aa;
518
519     int i = 0;
520
521     if (aSize > 1)
522     {
523       for (i = 0; i < (aSize - 1); i++)
524       {
525         temp[i] = annotations[i];
526       }
527     }
528
529     annotations = temp;
530   }
531
532   public void setAnnotationIndex(AlignmentAnnotation aa, int index)
533   {
534     if (aa == null || annotations == null || annotations.length - 1 < index)
535     {
536       return;
537     }
538
539     int aSize = annotations.length;
540     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
541
542     temp[index] = aa;
543
544     for (int i = 0; i < aSize; i++)
545     {
546       if (i == index)
547       {
548         continue;
549       }
550
551       if (i < index)
552       {
553         temp[i] = annotations[i];
554       }
555       else
556       {
557         temp[i] = annotations[i - 1];
558       }
559     }
560
561     annotations = temp;
562   }
563
564   /**
565    * DOCUMENT ME!
566    *
567    * @return DOCUMENT ME!
568    */
569   public AlignmentAnnotation[] getAlignmentAnnotation()
570   {
571     return annotations;
572   }
573
574   public void setNucleotide(boolean b)
575   {
576     if (b)
577     {
578       type = NUCLEOTIDE;
579     }
580     else
581     {
582       type = PROTEIN;
583     }
584   }
585
586   public boolean isNucleotide()
587   {
588     if (type == NUCLEOTIDE)
589     {
590       return true;
591     }
592     else
593     {
594       return false;
595     }
596   }
597
598   public void setDataset(Alignment data)
599   {
600     if (dataset == null && data == null)
601     {
602       // Create a new dataset for this alignment.
603       // Can only be done once, if dataset is not null
604       // This will not be performed
605       Sequence[] seqs = new Sequence[getHeight()];
606       SequenceI currentSeq;
607       for (int i = 0; i < getHeight(); i++)
608       {
609         currentSeq = getSequenceAt(i);
610         if (currentSeq.getDatasetSequence() != null)
611         {
612           seqs[i] = (Sequence) currentSeq.getDatasetSequence();
613         }
614         else
615         {
616           seqs[i] = new Sequence(currentSeq.getName(),
617                                  AlignSeq.extractGaps(
618                                      jalview.util.Comparison.GapChars,
619                                      currentSeq.getSequenceAsString()
620                                  ),
621                                  currentSeq.getStart(),
622                                  currentSeq.getEnd());
623           seqs[i].sequenceFeatures = currentSeq.getSequenceFeatures();
624           seqs[i].setDescription(currentSeq.getDescription());
625           getSequenceAt(i).setSequenceFeatures(null);
626           getSequenceAt(i).setDatasetSequence(seqs[i]);
627         }
628       }
629
630       dataset = new Alignment(seqs);
631     }
632     else if (dataset == null && data != null)
633     {
634       dataset = data;
635     }
636   }
637
638   public Alignment getDataset()
639   {
640     return dataset;
641   }
642
643   public boolean padGaps()
644   {
645     boolean modified = false;
646
647     //Remove excess gaps from the end of alignment
648     int maxLength = -1;
649
650     SequenceI current;
651     for (int i = 0; i < sequences.size(); i++)
652     {
653       current = getSequenceAt(i);
654       for (int j = current.getLength(); j > maxLength; j--)
655       {
656         if (j > maxLength && !jalview.util.Comparison.isGap(
657             current.getCharAt(j)))
658         {
659           maxLength = j;
660           break;
661         }
662       }
663     }
664
665     maxLength++;
666
667     int cLength;
668     for (int i = 0; i < sequences.size();
669          i++)
670     {
671       current = getSequenceAt(i);
672       cLength = current.getLength();
673
674       if (cLength < maxLength)
675       {
676         current.insertCharAt(cLength,
677                              maxLength - cLength, gapCharacter);
678         modified = true;
679       }
680       else if (current.getLength() > maxLength)
681       {
682         current.deleteChars(maxLength, current.getLength());
683       }
684     }
685     return modified;
686   }
687
688   public HiddenSequences getHiddenSequences()
689   {
690     return hiddenSequences;
691   }
692
693   public CigarArray getCompactAlignment()
694   {
695     SeqCigar alseqs[] = new SeqCigar[sequences.size()];
696     for (int i = 0; i < sequences.size(); i++)
697     {
698       alseqs[i] = new SeqCigar( (SequenceI) sequences.elementAt(i));
699     }
700     CigarArray cal = new CigarArray(alseqs);
701     cal.addOperation(CigarArray.M, getWidth());
702     return cal;
703   }
704
705 }