8080fb451a42cf2376a55251a3aef296ae8ecc14
[jalview.git] / src / jalview / datamodel / Alignment.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)
3  * Copyright (C) 2011 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>-1 && 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)
597               .replace('-', 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     return deleteAnnotation(aa, true);
656   }
657   
658   public boolean deleteAnnotation(AlignmentAnnotation aa, boolean unhook)
659   {
660     int aSize = 1;
661
662     if (annotations != null)
663     {
664       aSize = annotations.length;
665     }
666
667     if (aSize < 1)
668     {
669       return false;
670     }
671
672     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize - 1];
673
674     boolean swap = false;
675     int tIndex = 0;
676
677     for (int i = 0; i < aSize; i++)
678     {
679       if (annotations[i] == aa)
680       {
681         swap = true;
682         continue;
683       }
684       if (tIndex < temp.length)
685         temp[tIndex++] = annotations[i];
686     }
687
688     if (swap)
689     {
690       annotations = temp;
691       if (unhook) {
692         unhookAnnotation(aa);
693       }
694     }
695     return swap;
696   }
697
698   /**
699    * remove any object references associated with this annotation
700    * 
701    * @param aa
702    */
703   private void unhookAnnotation(AlignmentAnnotation aa)
704   {
705     if (aa.sequenceRef != null)
706     {
707       aa.sequenceRef.removeAlignmentAnnotation(aa);
708     }
709     if (aa.groupRef != null)
710     {
711       // probably need to do more here in the future (post 2.5.0)
712       aa.groupRef = null;
713     }
714   }
715
716   /*
717    * (non-Javadoc)
718    * 
719    * @seejalview.datamodel.AlignmentI#addAnnotation(jalview.datamodel.
720    * AlignmentAnnotation)
721    */
722   public void addAnnotation(AlignmentAnnotation aa)
723   {
724     addAnnotation(aa, -1);
725   }
726
727   /*
728    * (non-Javadoc)
729    * 
730    * @seejalview.datamodel.AlignmentI#addAnnotation(jalview.datamodel.
731    * AlignmentAnnotation, int)
732    */
733   public void addAnnotation(AlignmentAnnotation aa, int pos)
734   {
735     int aSize = 1;
736     if (annotations != null)
737     {
738       aSize = annotations.length + 1;
739     }
740
741     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
742     int i = 0;
743     if (pos == -1 || pos >= aSize)
744     {
745       temp[aSize - 1] = aa;
746     }
747     else
748     {
749       temp[pos] = aa;
750     }
751     if (aSize > 1)
752     {
753       int p = 0;
754       for (i = 0; i < (aSize - 1); i++, p++)
755       {
756         if (p == pos)
757         {
758           p++;
759         }
760         if (p < temp.length)
761         {
762           temp[p] = annotations[i];
763         }
764       }
765     }
766
767     annotations = temp;
768   }
769
770   public void setAnnotationIndex(AlignmentAnnotation aa, int index)
771   {
772     if (aa == null || annotations == null || annotations.length - 1 < index)
773     {
774       return;
775     }
776
777     int aSize = annotations.length;
778     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
779
780     temp[index] = aa;
781
782     for (int i = 0; i < aSize; i++)
783     {
784       if (i == index)
785       {
786         continue;
787       }
788
789       if (i < index)
790       {
791         temp[i] = annotations[i];
792       }
793       else
794       {
795         temp[i] = annotations[i - 1];
796       }
797     }
798
799     annotations = temp;
800   }
801
802   @Override
803   /**
804    * returns all annotation on the alignment
805    */
806   public AlignmentAnnotation[] getAlignmentAnnotation()
807   {
808     return annotations;
809   }
810
811   public void setNucleotide(boolean b)
812   {
813     if (b)
814     {
815       type = NUCLEOTIDE;
816     }
817     else
818     {
819       type = PROTEIN;
820     }
821   }
822
823   public boolean isNucleotide()
824   {
825     if (type == NUCLEOTIDE)
826     {
827       return true;
828     }
829     else
830     {
831       return false;
832     }
833   }
834
835   public void setDataset(Alignment data)
836   {
837     if (dataset == null && data == null)
838     {
839       // Create a new dataset for this alignment.
840       // Can only be done once, if dataset is not null
841       // This will not be performed
842       SequenceI[] seqs = new SequenceI[getHeight()];
843       SequenceI currentSeq;
844       for (int i = 0; i < getHeight(); i++)
845       {
846         currentSeq = getSequenceAt(i);
847         if (currentSeq.getDatasetSequence() != null)
848         {
849           seqs[i] = (Sequence) currentSeq.getDatasetSequence();
850         }
851         else
852         {
853           seqs[i] = currentSeq.createDatasetSequence();
854         }
855       }
856
857       dataset = new Alignment(seqs);
858     }
859     else if (dataset == null && data != null)
860     {
861       dataset = data;
862     }
863     dataset.addAlignmentRef();
864   }
865
866   /**
867    * reference count for number of alignments referencing this one.
868    */
869   int alignmentRefs = 0;
870
871   /**
872    * increase reference count to this alignment.
873    */
874   private void addAlignmentRef()
875   {
876     alignmentRefs++;
877   }
878
879   public Alignment getDataset()
880   {
881     return dataset;
882   }
883
884   public boolean padGaps()
885   {
886     boolean modified = false;
887
888     // Remove excess gaps from the end of alignment
889     int maxLength = -1;
890
891     SequenceI current;
892     for (int i = 0; i < sequences.size(); i++)
893     {
894       current = getSequenceAt(i);
895       for (int j = current.getLength(); j > maxLength; j--)
896       {
897         if (j > maxLength
898                 && !jalview.util.Comparison.isGap(current.getCharAt(j)))
899         {
900           maxLength = j;
901           break;
902         }
903       }
904     }
905
906     maxLength++;
907
908     int cLength;
909     for (int i = 0; i < sequences.size(); i++)
910     {
911       current = getSequenceAt(i);
912       cLength = current.getLength();
913
914       if (cLength < maxLength)
915       {
916         current.insertCharAt(cLength, maxLength - cLength, gapCharacter);
917         modified = true;
918       }
919       else if (current.getLength() > maxLength)
920       {
921         current.deleteChars(maxLength, current.getLength());
922       }
923     }
924     return modified;
925   }
926
927   /**
928    * Justify the sequences to the left or right by deleting and inserting gaps
929    * before the initial residue or after the terminal residue
930    * 
931    * @param right
932    *          true if alignment padded to right, false to justify to left
933    * @return true if alignment was changed
934    */
935   public boolean justify(boolean right)
936   {
937     boolean modified = false;
938
939     // Remove excess gaps from the end of alignment
940     int maxLength = -1;
941     int ends[] = new int[sequences.size() * 2];
942     SequenceI current;
943     for (int i = 0; i < sequences.size(); i++)
944     {
945       current = getSequenceAt(i);
946       // This should really be a sequence method
947       ends[i * 2] = current.findIndex(current.getStart());
948       ends[i * 2 + 1] = current.findIndex(current.getStart()
949               + current.getLength());
950       boolean hitres = false;
951       for (int j = 0, rs = 0, ssiz = current.getLength(); j < ssiz; j++)
952       {
953         if (!jalview.util.Comparison.isGap(current.getCharAt(j)))
954         {
955           if (!hitres)
956           {
957             ends[i * 2] = j;
958             hitres = true;
959           }
960           else
961           {
962             ends[i * 2 + 1] = j;
963             if (j - ends[i * 2] > maxLength)
964             {
965               maxLength = j - ends[i * 2];
966             }
967           }
968         }
969       }
970     }
971
972     maxLength++;
973     // now edit the flanking gaps to justify to either left or right
974     int cLength, extent, diff;
975     for (int i = 0; i < sequences.size(); i++)
976     {
977       current = getSequenceAt(i);
978
979       cLength = 1 + ends[i * 2 + 1] - ends[i * 2];
980       diff = maxLength - cLength; // number of gaps to indent
981       extent = current.getLength();
982       if (right)
983       {
984         // right justify
985         if (extent > ends[i * 2 + 1])
986         {
987           current.deleteChars(ends[i * 2 + 1] + 1, extent);
988           modified = true;
989         }
990         if (ends[i * 2] > diff)
991         {
992           current.deleteChars(0, ends[i * 2] - diff);
993           modified = true;
994         }
995         else
996         {
997           if (ends[i * 2] < diff)
998           {
999             current.insertCharAt(0, diff - ends[i * 2], gapCharacter);
1000             modified = true;
1001           }
1002         }
1003       }
1004       else
1005       {
1006         // left justify
1007         if (ends[i * 2] > 0)
1008         {
1009           current.deleteChars(0, ends[i * 2]);
1010           modified = true;
1011           ends[i * 2 + 1] -= ends[i * 2];
1012           extent -= ends[i * 2];
1013         }
1014         if (extent > maxLength)
1015         {
1016           current.deleteChars(maxLength + 1, extent);
1017           modified = true;
1018         }
1019         else
1020         {
1021           if (extent < maxLength)
1022           {
1023             current.insertCharAt(extent, maxLength - extent, gapCharacter);
1024             modified = true;
1025           }
1026         }
1027       }
1028     }
1029     return modified;
1030   }
1031
1032   public HiddenSequences getHiddenSequences()
1033   {
1034     return hiddenSequences;
1035   }
1036
1037   public CigarArray getCompactAlignment()
1038   {
1039     SeqCigar alseqs[] = new SeqCigar[sequences.size()];
1040     for (int i = 0; i < sequences.size(); i++)
1041     {
1042       alseqs[i] = new SeqCigar((SequenceI) sequences.elementAt(i));
1043     }
1044     CigarArray cal = new CigarArray(alseqs);
1045     cal.addOperation(CigarArray.M, getWidth());
1046     return cal;
1047   }
1048
1049   public void setProperty(Object key, Object value)
1050   {
1051     if (alignmentProperties == null)
1052       alignmentProperties = new Hashtable();
1053
1054     alignmentProperties.put(key, value);
1055   }
1056
1057   public Object getProperty(Object key)
1058   {
1059     if (alignmentProperties != null)
1060       return alignmentProperties.get(key);
1061     else
1062       return null;
1063   }
1064
1065   public Hashtable getProperties()
1066   {
1067     return alignmentProperties;
1068   }
1069
1070   AlignedCodonFrame[] codonFrameList = null;
1071
1072   /*
1073    * (non-Javadoc)
1074    * 
1075    * @see
1076    * jalview.datamodel.AlignmentI#addCodonFrame(jalview.datamodel.AlignedCodonFrame
1077    * )
1078    */
1079   public void addCodonFrame(AlignedCodonFrame codons)
1080   {
1081     if (codons == null)
1082       return;
1083     if (codonFrameList == null)
1084     {
1085       codonFrameList = new AlignedCodonFrame[]
1086       { codons };
1087       return;
1088     }
1089     AlignedCodonFrame[] t = new AlignedCodonFrame[codonFrameList.length + 1];
1090     System.arraycopy(codonFrameList, 0, t, 0, codonFrameList.length);
1091     t[codonFrameList.length] = codons;
1092     codonFrameList = t;
1093   }
1094
1095   /*
1096    * (non-Javadoc)
1097    * 
1098    * @see jalview.datamodel.AlignmentI#getCodonFrame(int)
1099    */
1100   public AlignedCodonFrame getCodonFrame(int index)
1101   {
1102     return codonFrameList[index];
1103   }
1104
1105   /*
1106    * (non-Javadoc)
1107    * 
1108    * @see
1109    * jalview.datamodel.AlignmentI#getCodonFrame(jalview.datamodel.SequenceI)
1110    */
1111   public AlignedCodonFrame[] getCodonFrame(SequenceI seq)
1112   {
1113     if (seq == null || codonFrameList == null)
1114       return null;
1115     Vector cframes = new Vector();
1116     for (int f = 0; f < codonFrameList.length; f++)
1117     {
1118       if (codonFrameList[f].involvesSequence(seq))
1119         cframes.addElement(codonFrameList[f]);
1120     }
1121     if (cframes.size() == 0)
1122       return null;
1123     AlignedCodonFrame[] cfr = new AlignedCodonFrame[cframes.size()];
1124     cframes.copyInto(cfr);
1125     return cfr;
1126   }
1127
1128   /*
1129    * (non-Javadoc)
1130    * 
1131    * @see jalview.datamodel.AlignmentI#getCodonFrames()
1132    */
1133   public AlignedCodonFrame[] getCodonFrames()
1134   {
1135     return codonFrameList;
1136   }
1137
1138   /*
1139    * (non-Javadoc)
1140    * 
1141    * @seejalview.datamodel.AlignmentI#removeCodonFrame(jalview.datamodel.
1142    * AlignedCodonFrame)
1143    */
1144   public boolean removeCodonFrame(AlignedCodonFrame codons)
1145   {
1146     if (codons == null || codonFrameList == null)
1147       return false;
1148     boolean removed = false;
1149     int i = 0, iSize = codonFrameList.length;
1150     while (i < iSize)
1151     {
1152       if (codonFrameList[i] == codons)
1153       {
1154         removed = true;
1155         if (i + 1 < iSize)
1156         {
1157           System.arraycopy(codonFrameList, i + 1, codonFrameList, i, iSize
1158                   - i - 1);
1159         }
1160         iSize--;
1161       }
1162       else
1163       {
1164         i++;
1165       }
1166     }
1167     return removed;
1168   }
1169
1170   public void append(AlignmentI toappend)
1171   {
1172     // TODO test this method for a future 2.5 release
1173     // currently tested for use in jalview.gui.SequenceFetcher
1174     boolean samegap = toappend.getGapCharacter() == getGapCharacter();
1175     char oldc = toappend.getGapCharacter();
1176     boolean hashidden = toappend.getHiddenSequences() != null
1177             && toappend.getHiddenSequences().hiddenSequences != null;
1178     // get all sequences including any hidden ones
1179     Vector sqs = (hashidden) ? toappend.getHiddenSequences()
1180             .getFullAlignment().getSequences() : toappend.getSequences();
1181     if (sqs != null)
1182     {
1183       Enumeration sq = sqs.elements();
1184       while (sq.hasMoreElements())
1185       {
1186         SequenceI addedsq = (SequenceI) sq.nextElement();
1187         if (!samegap)
1188         {
1189           char[] oldseq = addedsq.getSequence();
1190           for (int c = 0; c < oldseq.length; c++)
1191           {
1192             if (oldseq[c] == oldc)
1193             {
1194               oldseq[c] = gapCharacter;
1195             }
1196           }
1197         }
1198         addSequence(addedsq);
1199       }
1200     }
1201     AlignmentAnnotation[] alan = toappend.getAlignmentAnnotation();
1202     for (int a = 0; alan != null && a < alan.length; a++)
1203     {
1204       addAnnotation(alan[a]);
1205     }
1206     AlignedCodonFrame[] acod = toappend.getCodonFrames();
1207     for (int a = 0; acod != null && a < acod.length; a++)
1208     {
1209       this.addCodonFrame(acod[a]);
1210     }
1211     Vector sg = toappend.getGroups();
1212     if (sg != null)
1213     {
1214       Enumeration el = sg.elements();
1215       while (el.hasMoreElements())
1216       {
1217         addGroup((SequenceGroup) el.nextElement());
1218       }
1219     }
1220     if (toappend.getHiddenSequences() != null)
1221     {
1222       HiddenSequences hs = toappend.getHiddenSequences();
1223       if (hiddenSequences == null)
1224       {
1225         hiddenSequences = new HiddenSequences(this);
1226       }
1227       if (hs.hiddenSequences != null)
1228       {
1229         for (int s = 0; s < hs.hiddenSequences.length; s++)
1230         {
1231           // hide the newly appended sequence in the alignment
1232           if (hs.hiddenSequences[s] != null)
1233           {
1234             hiddenSequences.hideSequence(hs.hiddenSequences[s]);
1235           }
1236         }
1237       }
1238     }
1239     if (toappend.getProperties() != null)
1240     {
1241       // we really can't do very much here - just try to concatenate strings
1242       // where property collisions occur.
1243       Enumeration key = toappend.getProperties().keys();
1244       while (key.hasMoreElements())
1245       {
1246         Object k = key.nextElement();
1247         Object ourval = this.getProperty(k);
1248         Object toapprop = toappend.getProperty(k);
1249         if (ourval != null)
1250         {
1251           if (ourval.getClass().equals(toapprop.getClass())
1252                   && !ourval.equals(toapprop))
1253           {
1254             if (ourval instanceof String)
1255             {
1256               // append strings
1257               this.setProperty(k, ((String) ourval) + "; "
1258                       + ((String) toapprop));
1259             }
1260             else
1261             {
1262               if (ourval instanceof Vector)
1263               {
1264                 // append vectors
1265                 Enumeration theirv = ((Vector) toapprop).elements();
1266                 while (theirv.hasMoreElements())
1267                 {
1268                   ((Vector) ourval).addElement(theirv);
1269                 }
1270               }
1271             }
1272           }
1273         }
1274         else
1275         {
1276           // just add new property directly
1277           setProperty(k, toapprop);
1278         }
1279
1280       }
1281     }
1282   }
1283
1284 }