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