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