copy constructors and AlignmentAnnotation sequenceRef mechanisms for pasting sequence...
[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     int aSize = 1;
470
471     if (annotations != null)
472     {
473       aSize = annotations.length;
474     }
475
476     if (aSize < 1)
477     {
478       return;
479     }
480
481     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize - 1];
482
483     int tIndex = 0;
484
485     for (int i = 0; i < aSize; i++)
486     {
487       if (annotations[i] == aa)
488       {
489         continue;
490       }
491
492       temp[tIndex] = annotations[i];
493       tIndex++;
494     }
495
496     annotations = temp;
497   }
498
499   /**
500    * DOCUMENT ME!
501    *
502    * @param aa DOCUMENT ME!
503    */
504   public void addAnnotation(AlignmentAnnotation aa)
505   {
506     int aSize = 1;
507     if (annotations != null)
508     {
509       aSize = annotations.length + 1;
510     }
511
512     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
513
514     temp[aSize - 1] = aa;
515
516     int i = 0;
517
518     if (aSize > 1)
519     {
520       for (i = 0; i < (aSize - 1); i++)
521       {
522         temp[i] = annotations[i];
523       }
524     }
525
526     annotations = temp;
527   }
528
529   public void setAnnotationIndex(AlignmentAnnotation aa, int index)
530   {
531     if (aa == null || annotations == null || annotations.length - 1 < index)
532     {
533       return;
534     }
535
536     int aSize = annotations.length;
537     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
538
539     temp[index] = aa;
540
541     for (int i = 0; i < aSize; i++)
542     {
543       if (i == index)
544       {
545         continue;
546       }
547
548       if (i < index)
549       {
550         temp[i] = annotations[i];
551       }
552       else
553       {
554         temp[i] = annotations[i - 1];
555       }
556     }
557
558     annotations = temp;
559   }
560
561   /**
562    * DOCUMENT ME!
563    *
564    * @return DOCUMENT ME!
565    */
566   public AlignmentAnnotation[] getAlignmentAnnotation()
567   {
568     return annotations;
569   }
570
571   public void setNucleotide(boolean b)
572   {
573     if (b)
574     {
575       type = NUCLEOTIDE;
576     }
577     else
578     {
579       type = PROTEIN;
580     }
581   }
582
583   public boolean isNucleotide()
584   {
585     if (type == NUCLEOTIDE)
586     {
587       return true;
588     }
589     else
590     {
591       return false;
592     }
593   }
594
595   public void setDataset(Alignment data)
596   {
597     if (dataset == null && data == null)
598     {
599       // Create a new dataset for this alignment.
600       // Can only be done once, if dataset is not null
601       // This will not be performed
602       Sequence[] seqs = new Sequence[getHeight()];
603       SequenceI currentSeq;
604       for (int i = 0; i < getHeight(); i++)
605       {
606         currentSeq = getSequenceAt(i);
607         if (currentSeq.getDatasetSequence() != null)
608         {
609           seqs[i] = (Sequence) currentSeq.getDatasetSequence();
610         }
611         else
612         {
613           seqs[i] = new Sequence(currentSeq.getName(),
614                                  AlignSeq.extractGaps(
615                                      jalview.util.Comparison.GapChars,
616                                      currentSeq.getSequenceAsString()
617                                  ),
618                                  currentSeq.getStart(),
619                                  currentSeq.getEnd());
620           seqs[i].sequenceFeatures = currentSeq.getSequenceFeatures();
621           seqs[i].setDescription(currentSeq.getDescription());
622           getSequenceAt(i).setSequenceFeatures(null);
623           getSequenceAt(i).setDatasetSequence(seqs[i]);
624         }
625       }
626
627       dataset = new Alignment(seqs);
628     }
629     else if (dataset == null && data != null)
630     {
631       dataset = data;
632     }
633   }
634
635   public Alignment getDataset()
636   {
637     return dataset;
638   }
639
640   public boolean padGaps()
641   {
642     boolean modified = false;
643
644     //Remove excess gaps from the end of alignment
645     int maxLength = -1;
646
647     SequenceI current;
648     for (int i = 0; i < sequences.size(); i++)
649     {
650       current = getSequenceAt(i);
651       for (int j = current.getLength(); j > maxLength; j--)
652       {
653         if (j > maxLength && !jalview.util.Comparison.isGap(
654             current.getCharAt(j)))
655         {
656           maxLength = j;
657           break;
658         }
659       }
660     }
661
662     maxLength++;
663
664     int cLength;
665     for (int i = 0; i < sequences.size();
666          i++)
667     {
668       current = getSequenceAt(i);
669       cLength = current.getLength();
670
671       if (cLength < maxLength)
672       {
673         current.insertCharAt(cLength,
674                              maxLength - cLength, gapCharacter);
675         modified = true;
676       }
677       else if (current.getLength() > maxLength)
678       {
679         current.deleteChars(maxLength, current.getLength());
680       }
681     }
682     return modified;
683   }
684
685   public HiddenSequences getHiddenSequences()
686   {
687     return hiddenSequences;
688   }
689
690   public CigarArray getCompactAlignment()
691   {
692     SeqCigar alseqs[] = new SeqCigar[sequences.size()];
693     for (int i = 0; i < sequences.size(); i++)
694     {
695       alseqs[i] = new SeqCigar( (SequenceI) sequences.elementAt(i));
696     }
697     CigarArray cal = new CigarArray(alseqs);
698     cal.addOperation(CigarArray.M, getWidth());
699     return cal;
700   }
701
702 }