f36872ee11724358c02b5a46b5ad4372271073a5
[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     if (sequences==null)
112       return null;
113     SequenceI[] reply = new SequenceI[sequences.size()];
114     for (int i = 0; i < sequences.size(); i++)
115     {
116       reply[i] = (SequenceI) sequences.elementAt(i);
117     }
118     return reply;
119   }
120
121   /**
122    * DOCUMENT ME!
123    *
124    * @param i DOCUMENT ME!
125    *
126    * @return DOCUMENT ME!
127    */
128   public SequenceI getSequenceAt(int i)
129   {
130     if (i < sequences.size())
131     {
132       return (SequenceI) sequences.elementAt(i);
133     }
134
135     return null;
136   }
137
138   /** Adds a sequence to the alignment.  Recalculates maxLength and size.
139    *
140    * @param snew
141    */
142   public void addSequence(SequenceI snew)
143   {
144     if (dataset != null)
145     {
146       // maintain dataset integrity
147       if (snew.getDatasetSequence() != null)
148       {
149         getDataset().addSequence(snew.getDatasetSequence());
150       }
151       else
152       {
153         // derive new sequence
154         SequenceI adding = snew.deriveSequence();
155         getDataset().addSequence(adding.getDatasetSequence());
156         snew = adding;
157       }
158     }
159     if (sequences==null) {
160       initAlignment(new SequenceI[] { snew });
161     } else {
162       sequences.addElement(snew);
163     }
164     if (hiddenSequences!=null)
165       hiddenSequences.adjustHeightSequenceAdded();
166   }
167
168   /** Adds a sequence to the alignment.  Recalculates maxLength and size.
169    *
170    * @param snew
171    */
172   public void setSequenceAt(int i, SequenceI snew)
173   {
174     SequenceI oldseq = getSequenceAt(i);
175     deleteSequence(oldseq);
176
177     sequences.setElementAt(snew, i);
178   }
179
180   /**
181    * DOCUMENT ME!
182    *
183    * @return DOCUMENT ME!
184    */
185   public Vector getGroups()
186   {
187     return groups;
188   }
189
190   public void finalize()
191   {
192     if(getDataset()!=null)
193       getDataset().removeAlignmentRef();
194
195     dataset = null;
196     sequences = null;
197     groups = null;
198     annotations = null;
199     hiddenSequences = null;
200   }
201
202   /**
203    * decrement the alignmentRefs counter by one and call finalize if it goes to zero.
204    */
205   private void removeAlignmentRef()
206   {
207     if (--alignmentRefs==0)
208     {
209       finalize();
210     }
211   }
212
213   /**
214    * DOCUMENT ME!
215    *
216    * @param s DOCUMENT ME!
217    */
218   public void deleteSequence(SequenceI s)
219   {
220     deleteSequence(findIndex(s));
221   }
222
223   /**
224    * DOCUMENT ME!
225    *
226    * @param i DOCUMENT ME!
227    */
228   public void deleteSequence(int i)
229   {
230     if (i > -1 && i < getHeight())
231     {
232       sequences.removeElementAt(i);
233       hiddenSequences.adjustHeightSequenceDeleted(i);
234     }
235   }
236
237   /**    */
238   public SequenceGroup findGroup(SequenceI s)
239   {
240     for (int i = 0; i < this.groups.size(); i++)
241     {
242       SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
243
244       if (sg.getSequences(null).contains(s))
245       {
246         return sg;
247       }
248     }
249
250     return null;
251   }
252
253   /**
254    * DOCUMENT ME!
255    *
256    * @param s DOCUMENT ME!
257    *
258    * @return DOCUMENT ME!
259    */
260   public SequenceGroup[] findAllGroups(SequenceI s)
261   {
262     Vector temp = new Vector();
263
264     int gSize = groups.size();
265     for (int i = 0; i < gSize; i++)
266     {
267       SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
268       if (sg == null || sg.getSequences(null) == null)
269       {
270         this.deleteGroup(sg);
271         gSize--;
272         continue;
273       }
274
275       if (sg.getSequences(null).contains(s))
276       {
277         temp.addElement(sg);
278       }
279     }
280
281     SequenceGroup[] ret = new SequenceGroup[temp.size()];
282
283     for (int i = 0; i < temp.size(); i++)
284     {
285       ret[i] = (SequenceGroup) temp.elementAt(i);
286     }
287
288     return ret;
289   }
290
291   /**    */
292   public void addGroup(SequenceGroup sg)
293   {
294     if (!groups.contains(sg))
295     {
296       if (hiddenSequences.getSize() > 0)
297       {
298         int i, iSize = sg.getSize();
299         for (i = 0; i < iSize; i++)
300         {
301           if (!sequences.contains(sg.getSequenceAt(i)))
302           {
303             sg.deleteSequence(sg.getSequenceAt(i), false);
304             iSize--;
305             i--;
306           }
307         }
308
309         if (sg.getSize() < 1)
310         {
311           return;
312         }
313       }
314
315       groups.addElement(sg);
316     }
317   }
318
319   /**
320    * DOCUMENT ME!
321    */
322   public void deleteAllGroups()
323   {
324     groups.removeAllElements();
325   }
326
327   /**    */
328   public void deleteGroup(SequenceGroup g)
329   {
330     if (groups.contains(g))
331     {
332       groups.removeElement(g);
333     }
334   }
335
336   /**    */
337   public SequenceI findName(String name)
338   {
339     int i = 0;
340
341     while (i < sequences.size())
342     {
343       if (getSequenceAt(i).getName().equals(name))
344       {
345         return getSequenceAt(i);
346       }
347
348       i++;
349     }
350
351     return null;
352   }
353
354   public SequenceI[] findSequenceMatch(String name)
355   {
356     Vector matches = new Vector();
357     int i = 0;
358
359     while (i < sequences.size())
360     {
361       if (getSequenceAt(i).getName().equals(name))
362       {
363         matches.addElement(getSequenceAt(i));
364       }
365       i++;
366     }
367
368     SequenceI[] result = new SequenceI[matches.size()];
369     for (i = 0; i < result.length; i++)
370     {
371       result[i] = (SequenceI) matches.elementAt(i);
372     }
373
374     return result;
375
376   }
377
378   /**    */
379   public int findIndex(SequenceI s)
380   {
381     int i = 0;
382
383     while (i < sequences.size())
384     {
385       if (s == getSequenceAt(i))
386       {
387         return i;
388       }
389
390       i++;
391     }
392
393     return -1;
394   }
395
396   /**
397    * DOCUMENT ME!
398    *
399    * @return DOCUMENT ME!
400    */
401   public int getHeight()
402   {
403     return sequences.size();
404   }
405
406   /**
407    * DOCUMENT ME!
408    *
409    * @return DOCUMENT ME!
410    */
411   public int getWidth()
412   {
413     int maxLength = -1;
414
415     for (int i = 0; i < sequences.size(); i++)
416     {
417       if (getSequenceAt(i).getLength() > maxLength)
418       {
419         maxLength = getSequenceAt(i).getLength();
420       }
421     }
422
423     return maxLength;
424   }
425
426   /**
427    * DOCUMENT ME!
428    *
429    * @param gc DOCUMENT ME!
430    */
431   public void setGapCharacter(char gc)
432   {
433     gapCharacter = gc;
434
435     for (int i = 0; i < sequences.size(); i++)
436     {
437       Sequence seq = (Sequence) sequences.elementAt(i);
438       seq.setSequence(seq.getSequenceAsString()
439                       .replace('.', gc)
440                       .replace('-', gc)
441                       .replace(' ', gc)
442           );
443     }
444   }
445
446   /**
447    * DOCUMENT ME!
448    *
449    * @return DOCUMENT ME!
450    */
451   public char getGapCharacter()
452   {
453     return gapCharacter;
454   }
455
456   /**
457    * DOCUMENT ME!
458    *
459    * @return DOCUMENT ME!
460    */
461   public boolean isAligned()
462   {
463     int width = getWidth();
464
465     for (int i = 0; i < sequences.size(); i++)
466     {
467       if (getSequenceAt(i).getLength() != width)
468       {
469         return false;
470       }
471     }
472
473     return true;
474   }
475   /* (non-Javadoc)
476    * @see jalview.datamodel.AlignmentI#deleteAnnotation(jalview.datamodel.AlignmentAnnotation)
477    */
478   public boolean deleteAnnotation(AlignmentAnnotation aa)
479   {
480     int aSize = 1;
481
482     if (annotations != null)
483     {
484       aSize = annotations.length;
485     }
486
487     if (aSize < 1)
488     {
489       return false;
490     }
491
492     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize - 1];
493
494     boolean swap=false;
495     int tIndex = 0;
496
497     for (int i = 0; i < aSize; i++)
498     {
499       if (annotations[i] == aa)
500       {
501         swap=true;
502         continue;
503       }
504       if (tIndex<temp.length)
505         temp[tIndex++] = annotations[i];
506     }
507
508     if (swap)
509     {
510       annotations = temp;
511       if(aa.sequenceRef!=null)
512         aa.sequenceRef.removeAlignmentAnnotation(aa);
513     }
514     return swap;
515   }
516
517   /**
518    * DOCUMENT ME!
519    *
520    * @param aa DOCUMENT ME!
521    */
522   public void addAnnotation(AlignmentAnnotation aa)
523   {
524     int aSize = 1;
525     if (annotations != null)
526     {
527       aSize = annotations.length + 1;
528     }
529
530     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
531
532     temp[aSize - 1] = aa;
533
534     int i = 0;
535
536     if (aSize > 1)
537     {
538       for (i = 0; i < (aSize - 1); i++)
539       {
540         temp[i] = annotations[i];
541       }
542     }
543
544     annotations = temp;
545   }
546
547   public void setAnnotationIndex(AlignmentAnnotation aa, int index)
548   {
549     if (aa == null || annotations == null || annotations.length - 1 < index)
550     {
551       return;
552     }
553
554     int aSize = annotations.length;
555     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
556
557     temp[index] = aa;
558
559     for (int i = 0; i < aSize; i++)
560     {
561       if (i == index)
562       {
563         continue;
564       }
565
566       if (i < index)
567       {
568         temp[i] = annotations[i];
569       }
570       else
571       {
572         temp[i] = annotations[i - 1];
573       }
574     }
575
576     annotations = temp;
577   }
578
579   /**
580    * DOCUMENT ME!
581    *
582    * @return DOCUMENT ME!
583    */
584   public AlignmentAnnotation[] getAlignmentAnnotation()
585   {
586     return annotations;
587   }
588
589   public void setNucleotide(boolean b)
590   {
591     if (b)
592     {
593       type = NUCLEOTIDE;
594     }
595     else
596     {
597       type = PROTEIN;
598     }
599   }
600
601   public boolean isNucleotide()
602   {
603     if (type == NUCLEOTIDE)
604     {
605       return true;
606     }
607     else
608     {
609       return false;
610     }
611   }
612
613   public void setDataset(Alignment data)
614   {
615     if (dataset == null && data == null)
616     {
617       // Create a new dataset for this alignment.
618       // Can only be done once, if dataset is not null
619       // This will not be performed
620       SequenceI[] seqs = new SequenceI[getHeight()];
621       SequenceI currentSeq;
622       for (int i = 0; i < getHeight(); i++)
623       {
624         currentSeq = getSequenceAt(i);
625         if (currentSeq.getDatasetSequence() != null)
626         {
627           seqs[i] = (Sequence) currentSeq.getDatasetSequence();
628         }
629         else
630         {
631           seqs[i] = currentSeq.createDatasetSequence();
632         }
633       }
634
635       dataset = new Alignment(seqs);
636     }
637     else if (dataset == null && data != null)
638     {
639       dataset = data;
640     }
641     dataset.addAlignmentRef();
642   }
643   /**
644    * reference count for number of alignments referencing this one.
645    */
646   int alignmentRefs=0;
647   /**
648    * increase reference count to this alignment.
649    */
650   private void addAlignmentRef()
651   {
652     alignmentRefs++;
653   }
654
655   public Alignment getDataset()
656   {
657     return dataset;
658   }
659
660   public boolean padGaps()
661   {
662     boolean modified = false;
663
664     //Remove excess gaps from the end of alignment
665     int maxLength = -1;
666
667     SequenceI current;
668     for (int i = 0; i < sequences.size(); i++)
669     {
670       current = getSequenceAt(i);
671       for (int j = current.getLength(); j > maxLength; j--)
672       {
673         if (j > maxLength && !jalview.util.Comparison.isGap(
674             current.getCharAt(j)))
675         {
676           maxLength = j;
677           break;
678         }
679       }
680     }
681
682     maxLength++;
683
684     int cLength;
685     for (int i = 0; i < sequences.size();
686          i++)
687     {
688       current = getSequenceAt(i);
689       cLength = current.getLength();
690
691       if (cLength < maxLength)
692       {
693         current.insertCharAt(cLength,
694                              maxLength - cLength, gapCharacter);
695         modified = true;
696       }
697       else if (current.getLength() > maxLength)
698       {
699         current.deleteChars(maxLength, current.getLength());
700       }
701     }
702     return modified;
703   }
704
705   public HiddenSequences getHiddenSequences()
706   {
707     return hiddenSequences;
708   }
709
710   public CigarArray getCompactAlignment()
711   {
712     SeqCigar alseqs[] = new SeqCigar[sequences.size()];
713     for (int i = 0; i < sequences.size(); i++)
714     {
715       alseqs[i] = new SeqCigar( (SequenceI) sequences.elementAt(i));
716     }
717     CigarArray cal = new CigarArray(alseqs);
718     cal.addOperation(CigarArray.M, getWidth());
719     return cal;
720   }
721
722   public void setProperty(Object key, Object value)
723   {
724     if(alignmentProperties==null)
725       alignmentProperties = new Hashtable();
726
727     alignmentProperties.put(key,value);
728   }
729
730   public Object getProperty(Object key)
731   {
732     if(alignmentProperties!=null)
733       return alignmentProperties.get(key);
734     else
735       return null;
736   }
737
738   public Hashtable getProperties()
739   {
740     return alignmentProperties;
741   }
742   AlignedCodonFrame[] codonFrameList=null;
743   /* (non-Javadoc)
744    * @see jalview.datamodel.AlignmentI#addCodonFrame(jalview.datamodel.AlignedCodonFrame)
745    */
746   public void addCodonFrame(AlignedCodonFrame codons)
747   {
748     if (codons==null)
749       return;
750     if (codonFrameList==null)
751     {
752       codonFrameList = new AlignedCodonFrame[] { codons };
753       return;
754     }
755     AlignedCodonFrame[] t = new AlignedCodonFrame[codonFrameList.length+1];
756     System.arraycopy(codonFrameList, 0, t, 0, codonFrameList.length);
757     t[codonFrameList.length] = codons;
758     codonFrameList = t;
759   }
760
761   /* (non-Javadoc)
762    * @see jalview.datamodel.AlignmentI#getCodonFrame(int)
763    */
764   public AlignedCodonFrame getCodonFrame(int index)
765   {
766     return codonFrameList[index];
767   }
768
769   /* (non-Javadoc)
770    * @see jalview.datamodel.AlignmentI#getCodonFrame(jalview.datamodel.SequenceI)
771    */
772   public AlignedCodonFrame[] getCodonFrame(SequenceI seq)
773   {
774     if (seq==null || codonFrameList==null)
775       return null;
776     Vector cframes=new Vector();
777     for (int f=0;f<codonFrameList.length; f++)
778     {
779       if (codonFrameList[f].involvesSequence(seq))
780         cframes.addElement(codonFrameList[f]);
781     }
782     if (cframes.size()==0)
783       return null;
784     AlignedCodonFrame[] cfr = new AlignedCodonFrame[cframes.size()];
785     cframes.copyInto(cfr);
786     return cfr;
787   }
788
789   /* (non-Javadoc)
790    * @see jalview.datamodel.AlignmentI#getCodonFrames()
791    */
792   public AlignedCodonFrame[] getCodonFrames()
793   {
794     return codonFrameList;
795   }
796
797   /* (non-Javadoc)
798    * @see jalview.datamodel.AlignmentI#removeCodonFrame(jalview.datamodel.AlignedCodonFrame)
799    */
800   public boolean removeCodonFrame(AlignedCodonFrame codons)
801   {
802     if (codons==null || codonFrameList==null)
803       return false;
804     boolean removed=false;
805     int i=0,iSize=codonFrameList.length;
806     while (i<iSize)
807     {
808       if (codonFrameList[i]==codons)
809       {
810         removed=true;
811         if (i+1<iSize)
812         {
813           System.arraycopy(codonFrameList,i+1,codonFrameList, i, iSize-i-1);
814         }
815         iSize--;
816       }
817       else
818       {
819         i++;
820       }
821     }
822     return removed;
823   }
824 }