Jalview 2.6 source licence
[jalview.git] / src / jalview / datamodel / Alignment.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.6)
3  * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
10  * 
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.datamodel;
19
20 import java.util.*;
21
22 import jalview.analysis.*;
23
24 /**
25  * Data structure to hold and manipulate a multiple sequence alignment
26  */
27 /**
28  * @author JimP
29  * 
30  */
31 public class Alignment implements AlignmentI
32 {
33   protected Alignment dataset;
34
35   protected Vector sequences;
36
37   protected Vector groups = new Vector();
38
39   protected char gapCharacter = '-';
40
41   protected int type = NUCLEOTIDE;
42
43   public static final int PROTEIN = 0;
44
45   public static final int NUCLEOTIDE = 1;
46
47   /** DOCUMENT ME!! */
48   public AlignmentAnnotation[] annotations;
49
50   HiddenSequences hiddenSequences = new HiddenSequences(this);
51
52   public Hashtable alignmentProperties;
53
54   private void initAlignment(SequenceI[] seqs)
55   {
56     int i = 0;
57
58     if (jalview.util.Comparison.isNucleotide(seqs))
59     {
60       type = NUCLEOTIDE;
61     }
62     else
63     {
64       type = PROTEIN;
65     }
66
67     sequences = new Vector();
68
69     for (i = 0; i < seqs.length; i++)
70     {
71       sequences.addElement(seqs[i]);
72     }
73
74   }
75
76   /**
77    * Make an alignment from an array of Sequences.
78    * 
79    * @param sequences
80    */
81   public Alignment(SequenceI[] seqs)
82   {
83     initAlignment(seqs);
84   }
85
86   /**
87    * Make a new alignment from an array of SeqCigars
88    * 
89    * @param seqs
90    *          SeqCigar[]
91    */
92   public Alignment(SeqCigar[] alseqs)
93   {
94     SequenceI[] seqs = SeqCigar.createAlignmentSequences(alseqs,
95             gapCharacter, new ColumnSelection(), null);
96     initAlignment(seqs);
97   }
98
99   /**
100    * Make a new alignment from an CigarArray JBPNote - can only do this when
101    * compactAlignment does not contain hidden regions. JBPNote - must also check
102    * that compactAlignment resolves to a set of SeqCigars - or construct them
103    * appropriately.
104    * 
105    * @param compactAlignment
106    *          CigarArray
107    */
108   public static AlignmentI createAlignment(CigarArray compactAlignment)
109   {
110     throw new Error("Alignment(CigarArray) not yet implemented");
111     // this(compactAlignment.refCigars);
112   }
113
114   /**
115    * DOCUMENT ME!
116    * 
117    * @return DOCUMENT ME!
118    */
119   public Vector getSequences()
120   {
121     return sequences;
122   }
123
124   public SequenceI[] getSequencesArray()
125   {
126     if (sequences == null)
127       return null;
128     SequenceI[] reply = new SequenceI[sequences.size()];
129     for (int i = 0; i < sequences.size(); i++)
130     {
131       reply[i] = (SequenceI) sequences.elementAt(i);
132     }
133     return reply;
134   }
135
136   /**
137    * DOCUMENT ME!
138    * 
139    * @param i
140    *          DOCUMENT ME!
141    * 
142    * @return DOCUMENT ME!
143    */
144   public SequenceI getSequenceAt(int i)
145   {
146     if (i < sequences.size())
147     {
148       return (SequenceI) sequences.elementAt(i);
149     }
150
151     return null;
152   }
153
154   /**
155    * Adds a sequence to the alignment. Recalculates maxLength and size.
156    * 
157    * @param snew
158    */
159   public void addSequence(SequenceI snew)
160   {
161     if (dataset != null)
162     {
163       // maintain dataset integrity
164       if (snew.getDatasetSequence() != null)
165       {
166         getDataset().addSequence(snew.getDatasetSequence());
167       }
168       else
169       {
170         // derive new sequence
171         SequenceI adding = snew.deriveSequence();
172         getDataset().addSequence(adding.getDatasetSequence());
173         snew = adding;
174       }
175     }
176     if (sequences == null)
177     {
178       initAlignment(new SequenceI[]
179       { snew });
180     }
181     else
182     {
183       sequences.addElement(snew);
184     }
185     if (hiddenSequences != null)
186       hiddenSequences.adjustHeightSequenceAdded();
187   }
188
189   /**
190    * Adds a sequence to the alignment. Recalculates maxLength and size.
191    * 
192    * @param snew
193    */
194   public void setSequenceAt(int i, SequenceI snew)
195   {
196     SequenceI oldseq = getSequenceAt(i);
197     deleteSequence(oldseq);
198
199     sequences.setElementAt(snew, i);
200   }
201
202   /**
203    * DOCUMENT ME!
204    * 
205    * @return DOCUMENT ME!
206    */
207   public Vector getGroups()
208   {
209     return groups;
210   }
211
212   public void finalize()
213   {
214     if (getDataset() != null)
215       getDataset().removeAlignmentRef();
216
217     dataset = null;
218     sequences = null;
219     groups = null;
220     annotations = null;
221     hiddenSequences = null;
222   }
223
224   /**
225    * decrement the alignmentRefs counter by one and call finalize if it goes to
226    * zero.
227    */
228   private void removeAlignmentRef()
229   {
230     if (--alignmentRefs == 0)
231     {
232       finalize();
233     }
234   }
235
236   /**
237    * DOCUMENT ME!
238    * 
239    * @param s
240    *          DOCUMENT ME!
241    */
242   public void deleteSequence(SequenceI s)
243   {
244     deleteSequence(findIndex(s));
245   }
246
247   /**
248    * DOCUMENT ME!
249    * 
250    * @param i
251    *          DOCUMENT ME!
252    */
253   public void deleteSequence(int i)
254   {
255     if (i > -1 && i < getHeight())
256     {
257       sequences.removeElementAt(i);
258       hiddenSequences.adjustHeightSequenceDeleted(i);
259     }
260   }
261
262   /**    */
263   public SequenceGroup findGroup(SequenceI s)
264   {
265     for (int i = 0; i < this.groups.size(); i++)
266     {
267       SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
268
269       if (sg.getSequences(null).contains(s))
270       {
271         return sg;
272       }
273     }
274
275     return null;
276   }
277
278   /**
279    * DOCUMENT ME!
280    * 
281    * @param s
282    *          DOCUMENT ME!
283    * 
284    * @return DOCUMENT ME!
285    */
286   public SequenceGroup[] findAllGroups(SequenceI s)
287   {
288     Vector temp = new Vector();
289
290     int gSize = groups.size();
291     for (int i = 0; i < gSize; i++)
292     {
293       SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
294       if (sg == null || sg.getSequences(null) == null)
295       {
296         this.deleteGroup(sg);
297         gSize--;
298         continue;
299       }
300
301       if (sg.getSequences(null).contains(s))
302       {
303         temp.addElement(sg);
304       }
305     }
306
307     SequenceGroup[] ret = new SequenceGroup[temp.size()];
308
309     for (int i = 0; i < temp.size(); i++)
310     {
311       ret[i] = (SequenceGroup) temp.elementAt(i);
312     }
313
314     return ret;
315   }
316
317   /**    */
318   public void addGroup(SequenceGroup sg)
319   {
320     if (!groups.contains(sg))
321     {
322       if (hiddenSequences.getSize() > 0)
323       {
324         int i, iSize = sg.getSize();
325         for (i = 0; i < iSize; i++)
326         {
327           if (!sequences.contains(sg.getSequenceAt(i)))
328           {
329             sg.deleteSequence(sg.getSequenceAt(i), false);
330             iSize--;
331             i--;
332           }
333         }
334
335         if (sg.getSize() < 1)
336         {
337           return;
338         }
339       }
340
341       groups.addElement(sg);
342     }
343   }
344
345   /**
346    * remove any annotation that references gp
347    * 
348    * @param gp
349    *          (if null, removes all group associated annotation)
350    */
351   private void removeAnnotationForGroup(SequenceGroup gp)
352   {
353     if (annotations == null || annotations.length == 0)
354     {
355       return;
356     }
357     // remove annotation very quickly
358     AlignmentAnnotation[] t, todelete = new AlignmentAnnotation[annotations.length], tokeep = new AlignmentAnnotation[annotations.length];
359     int i, p, k;
360     if (gp == null)
361     {
362       for (i = 0, p = 0, k = 0; i < annotations.length; i++)
363       {
364         if (annotations[i].groupRef != null)
365         {
366           todelete[p++] = annotations[i];
367         }
368         else
369         {
370           tokeep[k++] = annotations[i];
371         }
372       }
373     }
374     else
375     {
376       for (i = 0, p = 0, k = 0; i < annotations.length; i++)
377       {
378         if (annotations[i].groupRef == gp)
379         {
380           todelete[p++] = annotations[i];
381         }
382         else
383         {
384           tokeep[k++] = annotations[i];
385         }
386       }
387     }
388     if (p > 0)
389     {
390       // clear out the group associated annotation.
391       for (i = 0; i < p; i++)
392       {
393         unhookAnnotation(todelete[i]);
394         todelete[i] = null;
395       }
396       t = new AlignmentAnnotation[k];
397       for (i = 0; i < k; i++)
398       {
399         t[i] = tokeep[i];
400       }
401       annotations = t;
402     }
403   }
404
405   public void deleteAllGroups()
406   {
407     if (annotations != null)
408     {
409       removeAnnotationForGroup(null);
410     }
411     groups.removeAllElements();
412   }
413
414   /**    */
415   public void deleteGroup(SequenceGroup g)
416   {
417     if (groups.contains(g))
418     {
419       removeAnnotationForGroup(g);
420       groups.removeElement(g);
421     }
422   }
423
424   /**    */
425   public SequenceI findName(String name)
426   {
427     return findName(name, false);
428   }
429
430   /*
431    * (non-Javadoc)
432    * 
433    * @see jalview.datamodel.AlignmentI#findName(java.lang.String, boolean)
434    */
435   public SequenceI findName(String token, boolean b)
436   {
437     return findName(null, token, b);
438   }
439
440   /*
441    * (non-Javadoc)
442    * 
443    * @see jalview.datamodel.AlignmentI#findName(SequenceI, java.lang.String,
444    * boolean)
445    */
446   public SequenceI findName(SequenceI startAfter, String token, boolean b)
447   {
448
449     int i = 0;
450     SequenceI sq = null;
451     String sqname = null;
452     if (startAfter != null)
453     {
454       // try to find the sequence in the alignment
455       boolean matched = false;
456       while (i < sequences.size())
457       {
458         if (getSequenceAt(i++) == startAfter)
459         {
460           matched = true;
461           break;
462         }
463       }
464       if (!matched)
465       {
466         i = 0;
467       }
468     }
469     while (i < sequences.size())
470     {
471       sq = getSequenceAt(i);
472       sqname = sq.getName();
473       if (sqname.equals(token) // exact match
474               || (b && // allow imperfect matches - case varies
475               (sqname.equalsIgnoreCase(token))))
476       {
477         return getSequenceAt(i);
478       }
479
480       i++;
481     }
482
483     return null;
484   }
485
486   public SequenceI[] findSequenceMatch(String name)
487   {
488     Vector matches = new Vector();
489     int i = 0;
490
491     while (i < sequences.size())
492     {
493       if (getSequenceAt(i).getName().equals(name))
494       {
495         matches.addElement(getSequenceAt(i));
496       }
497       i++;
498     }
499
500     SequenceI[] result = new SequenceI[matches.size()];
501     for (i = 0; i < result.length; i++)
502     {
503       result[i] = (SequenceI) matches.elementAt(i);
504     }
505
506     return result;
507
508   }
509
510   /*
511    * (non-Javadoc)
512    * 
513    * @see jalview.datamodel.AlignmentI#findIndex(jalview.datamodel.SequenceI)
514    */
515   public int findIndex(SequenceI s)
516   {
517     int i = 0;
518
519     while (i < sequences.size())
520     {
521       if (s == getSequenceAt(i))
522       {
523         return i;
524       }
525
526       i++;
527     }
528
529     return -1;
530   }
531
532   /*
533    * (non-Javadoc)
534    * 
535    * @see
536    * jalview.datamodel.AlignmentI#findIndex(jalview.datamodel.SearchResults)
537    */
538   public int findIndex(SearchResults results)
539   {
540     int i = 0;
541
542     while (i < sequences.size())
543     {
544       if (results.involvesSequence(getSequenceAt(i)))
545       {
546         return i;
547       }
548       i++;
549     }
550     return -1;
551   }
552
553   /**
554    * DOCUMENT ME!
555    * 
556    * @return DOCUMENT ME!
557    */
558   public int getHeight()
559   {
560     return sequences.size();
561   }
562
563   /**
564    * DOCUMENT ME!
565    * 
566    * @return DOCUMENT ME!
567    */
568   public int getWidth()
569   {
570     int maxLength = -1;
571
572     for (int i = 0; i < sequences.size(); i++)
573     {
574       if (getSequenceAt(i).getLength() > maxLength)
575       {
576         maxLength = getSequenceAt(i).getLength();
577       }
578     }
579
580     return maxLength;
581   }
582
583   /**
584    * DOCUMENT ME!
585    * 
586    * @param gc
587    *          DOCUMENT ME!
588    */
589   public void setGapCharacter(char gc)
590   {
591     gapCharacter = gc;
592
593     for (int i = 0; i < sequences.size(); i++)
594     {
595       Sequence seq = (Sequence) sequences.elementAt(i);
596       seq.setSequence(seq.getSequenceAsString().replace('.', gc).replace(
597               '-', gc).replace(' ', gc));
598     }
599   }
600
601   /**
602    * DOCUMENT ME!
603    * 
604    * @return DOCUMENT ME!
605    */
606   public char getGapCharacter()
607   {
608     return gapCharacter;
609   }
610
611   /*
612    * (non-Javadoc)
613    * 
614    * @see jalview.datamodel.AlignmentI#isAligned()
615    */
616   public boolean isAligned()
617   {
618     return isAligned(false);
619   }
620
621   /*
622    * (non-Javadoc)
623    * 
624    * @see jalview.datamodel.AlignmentI#isAligned(boolean)
625    */
626   public boolean isAligned(boolean includeHidden)
627   {
628     int width = getWidth();
629     if (hiddenSequences == null || hiddenSequences.getSize() == 0)
630     {
631       includeHidden = true; // no hidden sequences to check against.
632     }
633     for (int i = 0; i < sequences.size(); i++)
634     {
635       if (includeHidden || !hiddenSequences.isHidden(getSequenceAt(i)))
636       {
637         if (getSequenceAt(i).getLength() != width)
638         {
639           return false;
640         }
641       }
642     }
643
644     return true;
645   }
646
647   /*
648    * (non-Javadoc)
649    * 
650    * @seejalview.datamodel.AlignmentI#deleteAnnotation(jalview.datamodel.
651    * AlignmentAnnotation)
652    */
653   public boolean deleteAnnotation(AlignmentAnnotation aa)
654   {
655     int aSize = 1;
656
657     if (annotations != null)
658     {
659       aSize = annotations.length;
660     }
661
662     if (aSize < 1)
663     {
664       return false;
665     }
666
667     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize - 1];
668
669     boolean swap = false;
670     int tIndex = 0;
671
672     for (int i = 0; i < aSize; i++)
673     {
674       if (annotations[i] == aa)
675       {
676         swap = true;
677         continue;
678       }
679       if (tIndex < temp.length)
680         temp[tIndex++] = annotations[i];
681     }
682
683     if (swap)
684     {
685       annotations = temp;
686       unhookAnnotation(aa);
687     }
688     return swap;
689   }
690
691   /**
692    * remove any object references associated with this annotation
693    * 
694    * @param aa
695    */
696   private void unhookAnnotation(AlignmentAnnotation aa)
697   {
698     if (aa.sequenceRef != null)
699     {
700       aa.sequenceRef.removeAlignmentAnnotation(aa);
701     }
702     if (aa.groupRef != null)
703     {
704       // probably need to do more here in the future (post 2.5.0)
705       aa.groupRef = null;
706     }
707   }
708
709   /*
710    * (non-Javadoc)
711    * 
712    * @seejalview.datamodel.AlignmentI#addAnnotation(jalview.datamodel.
713    * AlignmentAnnotation)
714    */
715   public void addAnnotation(AlignmentAnnotation aa)
716   {
717     addAnnotation(aa, -1);
718   }
719
720   /*
721    * (non-Javadoc)
722    * 
723    * @seejalview.datamodel.AlignmentI#addAnnotation(jalview.datamodel.
724    * AlignmentAnnotation, int)
725    */
726   public void addAnnotation(AlignmentAnnotation aa, int pos)
727   {
728     int aSize = 1;
729     if (annotations != null)
730     {
731       aSize = annotations.length + 1;
732     }
733
734     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
735     int i = 0;
736     if (pos == -1 || pos >= aSize)
737     {
738       temp[aSize - 1] = aa;
739     }
740     else
741     {
742       temp[pos] = aa;
743     }
744     if (aSize > 1)
745     {
746       int p = 0;
747       for (i = 0; i < (aSize - 1); i++, p++)
748       {
749         if (p == pos)
750         {
751           p++;
752         }
753         if (p < temp.length)
754         {
755           temp[p] = annotations[i];
756         }
757       }
758     }
759
760     annotations = temp;
761   }
762
763   public void setAnnotationIndex(AlignmentAnnotation aa, int index)
764   {
765     if (aa == null || annotations == null || annotations.length - 1 < index)
766     {
767       return;
768     }
769
770     int aSize = annotations.length;
771     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
772
773     temp[index] = aa;
774
775     for (int i = 0; i < aSize; i++)
776     {
777       if (i == index)
778       {
779         continue;
780       }
781
782       if (i < index)
783       {
784         temp[i] = annotations[i];
785       }
786       else
787       {
788         temp[i] = annotations[i - 1];
789       }
790     }
791
792     annotations = temp;
793   }
794
795   /**
796    * DOCUMENT ME!
797    * 
798    * @return DOCUMENT ME!
799    */
800   public AlignmentAnnotation[] getAlignmentAnnotation()
801   {
802     return annotations;
803   }
804
805   public void setNucleotide(boolean b)
806   {
807     if (b)
808     {
809       type = NUCLEOTIDE;
810     }
811     else
812     {
813       type = PROTEIN;
814     }
815   }
816
817   public boolean isNucleotide()
818   {
819     if (type == NUCLEOTIDE)
820     {
821       return true;
822     }
823     else
824     {
825       return false;
826     }
827   }
828
829   public void setDataset(Alignment data)
830   {
831     if (dataset == null && data == null)
832     {
833       // Create a new dataset for this alignment.
834       // Can only be done once, if dataset is not null
835       // This will not be performed
836       SequenceI[] seqs = new SequenceI[getHeight()];
837       SequenceI currentSeq;
838       for (int i = 0; i < getHeight(); i++)
839       {
840         currentSeq = getSequenceAt(i);
841         if (currentSeq.getDatasetSequence() != null)
842         {
843           seqs[i] = (Sequence) currentSeq.getDatasetSequence();
844         }
845         else
846         {
847           seqs[i] = currentSeq.createDatasetSequence();
848         }
849       }
850
851       dataset = new Alignment(seqs);
852     }
853     else if (dataset == null && data != null)
854     {
855       dataset = data;
856     }
857     dataset.addAlignmentRef();
858   }
859
860   /**
861    * reference count for number of alignments referencing this one.
862    */
863   int alignmentRefs = 0;
864
865   /**
866    * increase reference count to this alignment.
867    */
868   private void addAlignmentRef()
869   {
870     alignmentRefs++;
871   }
872
873   public Alignment getDataset()
874   {
875     return dataset;
876   }
877
878   public boolean padGaps()
879   {
880     boolean modified = false;
881
882     // Remove excess gaps from the end of alignment
883     int maxLength = -1;
884
885     SequenceI current;
886     for (int i = 0; i < sequences.size(); i++)
887     {
888       current = getSequenceAt(i);
889       for (int j = current.getLength(); j > maxLength; j--)
890       {
891         if (j > maxLength
892                 && !jalview.util.Comparison.isGap(current.getCharAt(j)))
893         {
894           maxLength = j;
895           break;
896         }
897       }
898     }
899
900     maxLength++;
901
902     int cLength;
903     for (int i = 0; i < sequences.size(); i++)
904     {
905       current = getSequenceAt(i);
906       cLength = current.getLength();
907
908       if (cLength < maxLength)
909       {
910         current.insertCharAt(cLength, maxLength - cLength, gapCharacter);
911         modified = true;
912       }
913       else if (current.getLength() > maxLength)
914       {
915         current.deleteChars(maxLength, current.getLength());
916       }
917     }
918     return modified;
919   }
920
921   /**
922    * Justify the sequences to the left or right by deleting and inserting gaps
923    * before the initial residue or after the terminal residue
924    * 
925    * @param right
926    *          true if alignment padded to right, false to justify to left
927    * @return true if alignment was changed
928    */
929   public boolean justify(boolean right)
930   {
931     boolean modified = false;
932
933     // Remove excess gaps from the end of alignment
934     int maxLength = -1;
935     int ends[] = new int[sequences.size() * 2];
936     SequenceI current;
937     for (int i = 0; i < sequences.size(); i++)
938     {
939       current = getSequenceAt(i);
940       // This should really be a sequence method
941       ends[i * 2] = current.findIndex(current.getStart());
942       ends[i * 2 + 1] = current.findIndex(current.getStart()
943               + current.getLength());
944       boolean hitres = false;
945       for (int j = 0, rs = 0, ssiz = current.getLength(); j < ssiz; j++)
946       {
947         if (!jalview.util.Comparison.isGap(current.getCharAt(j)))
948         {
949           if (!hitres)
950           {
951             ends[i * 2] = j;
952             hitres = true;
953           }
954           else
955           {
956             ends[i * 2 + 1] = j;
957             if (j - ends[i * 2] > maxLength)
958             {
959               maxLength = j - ends[i * 2];
960             }
961           }
962         }
963       }
964     }
965
966     maxLength++;
967     // now edit the flanking gaps to justify to either left or right
968     int cLength, extent, diff;
969     for (int i = 0; i < sequences.size(); i++)
970     {
971       current = getSequenceAt(i);
972
973       cLength = 1 + ends[i * 2 + 1] - ends[i * 2];
974       diff = maxLength - cLength; // number of gaps to indent
975       extent = current.getLength();
976       if (right)
977       {
978         // right justify
979         if (extent > ends[i * 2 + 1])
980         {
981           current.deleteChars(ends[i * 2 + 1] + 1, extent);
982           modified = true;
983         }
984         if (ends[i * 2] > diff)
985         {
986           current.deleteChars(0, ends[i * 2] - diff);
987           modified = true;
988         }
989         else
990         {
991           if (ends[i * 2] < diff)
992           {
993             current.insertCharAt(0, diff - ends[i * 2], gapCharacter);
994             modified = true;
995           }
996         }
997       }
998       else
999       {
1000         // left justify
1001         if (ends[i * 2] > 0)
1002         {
1003           current.deleteChars(0, ends[i * 2]);
1004           modified = true;
1005           ends[i * 2 + 1] -= ends[i * 2];
1006           extent -= ends[i * 2];
1007         }
1008         if (extent > maxLength)
1009         {
1010           current.deleteChars(maxLength + 1, extent);
1011           modified = true;
1012         }
1013         else
1014         {
1015           if (extent < maxLength)
1016           {
1017             current.insertCharAt(extent, maxLength - extent, gapCharacter);
1018             modified = true;
1019           }
1020         }
1021       }
1022     }
1023     return modified;
1024   }
1025
1026   public HiddenSequences getHiddenSequences()
1027   {
1028     return hiddenSequences;
1029   }
1030
1031   public CigarArray getCompactAlignment()
1032   {
1033     SeqCigar alseqs[] = new SeqCigar[sequences.size()];
1034     for (int i = 0; i < sequences.size(); i++)
1035     {
1036       alseqs[i] = new SeqCigar((SequenceI) sequences.elementAt(i));
1037     }
1038     CigarArray cal = new CigarArray(alseqs);
1039     cal.addOperation(CigarArray.M, getWidth());
1040     return cal;
1041   }
1042
1043   public void setProperty(Object key, Object value)
1044   {
1045     if (alignmentProperties == null)
1046       alignmentProperties = new Hashtable();
1047
1048     alignmentProperties.put(key, value);
1049   }
1050
1051   public Object getProperty(Object key)
1052   {
1053     if (alignmentProperties != null)
1054       return alignmentProperties.get(key);
1055     else
1056       return null;
1057   }
1058
1059   public Hashtable getProperties()
1060   {
1061     return alignmentProperties;
1062   }
1063
1064   AlignedCodonFrame[] codonFrameList = null;
1065
1066   /*
1067    * (non-Javadoc)
1068    * 
1069    * @see
1070    * jalview.datamodel.AlignmentI#addCodonFrame(jalview.datamodel.AlignedCodonFrame
1071    * )
1072    */
1073   public void addCodonFrame(AlignedCodonFrame codons)
1074   {
1075     if (codons == null)
1076       return;
1077     if (codonFrameList == null)
1078     {
1079       codonFrameList = new AlignedCodonFrame[]
1080       { codons };
1081       return;
1082     }
1083     AlignedCodonFrame[] t = new AlignedCodonFrame[codonFrameList.length + 1];
1084     System.arraycopy(codonFrameList, 0, t, 0, codonFrameList.length);
1085     t[codonFrameList.length] = codons;
1086     codonFrameList = t;
1087   }
1088
1089   /*
1090    * (non-Javadoc)
1091    * 
1092    * @see jalview.datamodel.AlignmentI#getCodonFrame(int)
1093    */
1094   public AlignedCodonFrame getCodonFrame(int index)
1095   {
1096     return codonFrameList[index];
1097   }
1098
1099   /*
1100    * (non-Javadoc)
1101    * 
1102    * @see
1103    * jalview.datamodel.AlignmentI#getCodonFrame(jalview.datamodel.SequenceI)
1104    */
1105   public AlignedCodonFrame[] getCodonFrame(SequenceI seq)
1106   {
1107     if (seq == null || codonFrameList == null)
1108       return null;
1109     Vector cframes = new Vector();
1110     for (int f = 0; f < codonFrameList.length; f++)
1111     {
1112       if (codonFrameList[f].involvesSequence(seq))
1113         cframes.addElement(codonFrameList[f]);
1114     }
1115     if (cframes.size() == 0)
1116       return null;
1117     AlignedCodonFrame[] cfr = new AlignedCodonFrame[cframes.size()];
1118     cframes.copyInto(cfr);
1119     return cfr;
1120   }
1121
1122   /*
1123    * (non-Javadoc)
1124    * 
1125    * @see jalview.datamodel.AlignmentI#getCodonFrames()
1126    */
1127   public AlignedCodonFrame[] getCodonFrames()
1128   {
1129     return codonFrameList;
1130   }
1131
1132   /*
1133    * (non-Javadoc)
1134    * 
1135    * @seejalview.datamodel.AlignmentI#removeCodonFrame(jalview.datamodel.
1136    * AlignedCodonFrame)
1137    */
1138   public boolean removeCodonFrame(AlignedCodonFrame codons)
1139   {
1140     if (codons == null || codonFrameList == null)
1141       return false;
1142     boolean removed = false;
1143     int i = 0, iSize = codonFrameList.length;
1144     while (i < iSize)
1145     {
1146       if (codonFrameList[i] == codons)
1147       {
1148         removed = true;
1149         if (i + 1 < iSize)
1150         {
1151           System.arraycopy(codonFrameList, i + 1, codonFrameList, i, iSize
1152                   - i - 1);
1153         }
1154         iSize--;
1155       }
1156       else
1157       {
1158         i++;
1159       }
1160     }
1161     return removed;
1162   }
1163
1164   public void append(AlignmentI toappend)
1165   {
1166     // TODO test this method for a future 2.5 release
1167     // currently tested for use in jalview.gui.SequenceFetcher
1168     boolean samegap = toappend.getGapCharacter() == getGapCharacter();
1169     char oldc = toappend.getGapCharacter();
1170     boolean hashidden = toappend.getHiddenSequences() != null
1171             && toappend.getHiddenSequences().hiddenSequences != null;
1172     // get all sequences including any hidden ones
1173     Vector sqs = (hashidden) ? toappend.getHiddenSequences()
1174             .getFullAlignment().getSequences() : toappend.getSequences();
1175     if (sqs != null)
1176     {
1177       Enumeration sq = sqs.elements();
1178       while (sq.hasMoreElements())
1179       {
1180         SequenceI addedsq = (SequenceI) sq.nextElement();
1181         if (!samegap)
1182         {
1183           char[] oldseq = addedsq.getSequence();
1184           for (int c = 0; c < oldseq.length; c++)
1185           {
1186             if (oldseq[c] == oldc)
1187             {
1188               oldseq[c] = gapCharacter;
1189             }
1190           }
1191         }
1192         addSequence(addedsq);
1193       }
1194     }
1195     AlignmentAnnotation[] alan = toappend.getAlignmentAnnotation();
1196     for (int a = 0; alan != null && a < alan.length; a++)
1197     {
1198       addAnnotation(alan[a]);
1199     }
1200     AlignedCodonFrame[] acod = toappend.getCodonFrames();
1201     for (int a = 0; acod != null && a < acod.length; a++)
1202     {
1203       this.addCodonFrame(acod[a]);
1204     }
1205     Vector sg = toappend.getGroups();
1206     if (sg != null)
1207     {
1208       Enumeration el = sg.elements();
1209       while (el.hasMoreElements())
1210       {
1211         addGroup((SequenceGroup) el.nextElement());
1212       }
1213     }
1214     if (toappend.getHiddenSequences() != null)
1215     {
1216       HiddenSequences hs = toappend.getHiddenSequences();
1217       if (hiddenSequences == null)
1218       {
1219         hiddenSequences = new HiddenSequences(this);
1220       }
1221       if (hs.hiddenSequences != null)
1222       {
1223         for (int s = 0; s < hs.hiddenSequences.length; s++)
1224         {
1225           // hide the newly appended sequence in the alignment
1226           if (hs.hiddenSequences[s] != null)
1227           {
1228             hiddenSequences.hideSequence(hs.hiddenSequences[s]);
1229           }
1230         }
1231       }
1232     }
1233     if (toappend.getProperties() != null)
1234     {
1235       // we really can't do very much here - just try to concatenate strings
1236       // where property collisions occur.
1237       Enumeration key = toappend.getProperties().keys();
1238       while (key.hasMoreElements())
1239       {
1240         Object k = key.nextElement();
1241         Object ourval = this.getProperty(k);
1242         Object toapprop = toappend.getProperty(k);
1243         if (ourval != null)
1244         {
1245           if (ourval.getClass().equals(toapprop.getClass())
1246                   && !ourval.equals(toapprop))
1247           {
1248             if (ourval instanceof String)
1249             {
1250               // append strings
1251               this.setProperty(k, ((String) ourval) + "; "
1252                       + ((String) toapprop));
1253             }
1254             else
1255             {
1256               if (ourval instanceof Vector)
1257               {
1258                 // append vectors
1259                 Enumeration theirv = ((Vector) toapprop).elements();
1260                 while (theirv.hasMoreElements())
1261                 {
1262                   ((Vector) ourval).addElement(theirv);
1263                 }
1264               }
1265             }
1266           }
1267         }
1268         else
1269         {
1270           // just add new property directly
1271           setProperty(k, toapprop);
1272         }
1273
1274       }
1275     }
1276   }
1277
1278 }