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