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