Merge branch 'bug/JAL-2154projectMappings' into merge/develop_bug/JAL-2154projectMappings
[jalview.git] / src / jalview / datamodel / Alignment.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
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
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.datamodel;
22
23 import jalview.analysis.AlignmentUtils;
24 import jalview.datamodel.AlignedCodonFrame.SequenceToSequenceMapping;
25 import jalview.io.FastaFile;
26 import jalview.util.Comparison;
27 import jalview.util.MessageManager;
28
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.Enumeration;
32 import java.util.HashSet;
33 import java.util.Hashtable;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Set;
37 import java.util.Vector;
38
39 /**
40  * Data structure to hold and manipulate a multiple sequence alignment
41  */
42 /**
43  * @author JimP
44  * 
45  */
46 public class Alignment implements AlignmentI
47 {
48   private Alignment dataset;
49
50   protected List<SequenceI> sequences;
51
52   protected List<SequenceGroup> groups;
53
54   protected char gapCharacter = '-';
55
56   protected int type = NUCLEOTIDE;
57
58   public static final int PROTEIN = 0;
59
60   public static final int NUCLEOTIDE = 1;
61
62   public boolean hasRNAStructure = false;
63
64   public AlignmentAnnotation[] annotations;
65
66   HiddenSequences hiddenSequences;
67
68   public Hashtable alignmentProperties;
69
70   private List<AlignedCodonFrame> codonFrameList;
71
72   private void initAlignment(SequenceI[] seqs)
73   {
74     groups = Collections.synchronizedList(new ArrayList<SequenceGroup>());
75     hiddenSequences = new HiddenSequences(this);
76     codonFrameList = new ArrayList<AlignedCodonFrame>();
77
78     if (Comparison.isNucleotide(seqs))
79     {
80       type = NUCLEOTIDE;
81     }
82     else
83     {
84       type = PROTEIN;
85     }
86
87     sequences = Collections.synchronizedList(new ArrayList<SequenceI>());
88
89     for (int i = 0; i < seqs.length; i++)
90     {
91       sequences.add(seqs[i]);
92     }
93
94   }
95
96   /**
97    * Make a 'copy' alignment - sequences have new copies of features and
98    * annotations, but share the original dataset sequences.
99    */
100   public Alignment(AlignmentI al)
101   {
102     SequenceI[] seqs = al.getSequencesArray();
103     for (int i = 0; i < seqs.length; i++)
104     {
105       seqs[i] = new Sequence(seqs[i]);
106     }
107
108     initAlignment(seqs);
109
110     /*
111      * Share the same dataset sequence mappings (if any). 
112      */
113     if (dataset == null && al.getDataset() == null)
114     {
115       this.setCodonFrames(al.getCodonFrames());
116     }
117   }
118
119   /**
120    * Make an alignment from an array of Sequences.
121    * 
122    * @param sequences
123    */
124   public Alignment(SequenceI[] seqs)
125   {
126     initAlignment(seqs);
127   }
128
129   /**
130    * Make a new alignment from an array of SeqCigars
131    * 
132    * @param seqs
133    *          SeqCigar[]
134    */
135   public Alignment(SeqCigar[] alseqs)
136   {
137     SequenceI[] seqs = SeqCigar.createAlignmentSequences(alseqs,
138             gapCharacter, new ColumnSelection(), null);
139     initAlignment(seqs);
140   }
141
142   /**
143    * Make a new alignment from an CigarArray JBPNote - can only do this when
144    * compactAlignment does not contain hidden regions. JBPNote - must also check
145    * that compactAlignment resolves to a set of SeqCigars - or construct them
146    * appropriately.
147    * 
148    * @param compactAlignment
149    *          CigarArray
150    */
151   public static AlignmentI createAlignment(CigarArray compactAlignment)
152   {
153     throw new Error(
154             MessageManager
155                     .getString("error.alignment_cigararray_not_implemented"));
156     // this(compactAlignment.refCigars);
157   }
158
159   @Override
160   public List<SequenceI> getSequences()
161   {
162     return sequences;
163   }
164
165   @Override
166   public List<SequenceI> getSequences(
167           Map<SequenceI, SequenceCollectionI> hiddenReps)
168   {
169     // TODO: in jalview 2.8 we don't do anything with hiddenreps - fix design to
170     // work on this.
171     return sequences;
172   }
173
174   @Override
175   public SequenceI[] getSequencesArray()
176   {
177     if (sequences == null)
178     {
179       return null;
180     }
181     synchronized (sequences)
182     {
183       return sequences.toArray(new SequenceI[sequences.size()]);
184     }
185   }
186
187   /**
188    * Returns a map of lists of sequences keyed by sequence name.
189    * 
190    * @return
191    */
192   @Override
193   public Map<String, List<SequenceI>> getSequencesByName()
194   {
195     return AlignmentUtils.getSequencesByName(this);
196   }
197
198   /**
199    * DOCUMENT ME!
200    * 
201    * @param i
202    *          DOCUMENT ME!
203    * 
204    * @return DOCUMENT ME!
205    */
206   @Override
207   public SequenceI getSequenceAt(int i)
208   {
209     synchronized (sequences)
210     {
211       if (i > -1 && i < sequences.size())
212       {
213         return sequences.get(i);
214       }
215     }
216     return null;
217   }
218
219   /**
220    * Adds a sequence to the alignment. Recalculates maxLength and size.
221    * 
222    * @param snew
223    */
224   @Override
225   public void addSequence(SequenceI snew)
226   {
227     if (dataset != null)
228     {
229
230       // maintain dataset integrity
231       SequenceI dsseq = snew.getDatasetSequence();
232       if (dsseq == null)
233       {
234         // derive new sequence
235         SequenceI adding = snew.deriveSequence();
236         snew = adding;
237         dsseq = snew.getDatasetSequence();
238       }
239       if (getDataset().findIndex(dsseq) == -1)
240       {
241         getDataset().addSequence(dsseq);
242       }
243
244     }
245     if (sequences == null)
246     {
247       initAlignment(new SequenceI[] { snew });
248     }
249     else
250     {
251       synchronized (sequences)
252       {
253         sequences.add(snew);
254       }
255     }
256     if (hiddenSequences != null)
257     {
258       hiddenSequences.adjustHeightSequenceAdded();
259     }
260   }
261
262   @Override
263   public SequenceI replaceSequenceAt(int i, SequenceI snew)
264   {
265     synchronized (sequences)
266     {
267       if (sequences.size() > i)
268       {
269         return sequences.set(i, snew);
270
271       }
272       else
273       {
274         sequences.add(snew);
275         hiddenSequences.adjustHeightSequenceAdded();
276       }
277       return null;
278     }
279   }
280
281   /**
282    * DOCUMENT ME!
283    * 
284    * @return DOCUMENT ME!
285    */
286   @Override
287   public List<SequenceGroup> getGroups()
288   {
289     return groups;
290   }
291
292   @Override
293   public void finalize()
294   {
295     if (getDataset() != null)
296     {
297       getDataset().removeAlignmentRef();
298     }
299
300     dataset = null;
301     sequences = null;
302     groups = null;
303     annotations = null;
304     hiddenSequences = null;
305   }
306
307   /**
308    * decrement the alignmentRefs counter by one and call finalize if it goes to
309    * zero.
310    */
311   private void removeAlignmentRef()
312   {
313     if (--alignmentRefs == 0)
314     {
315       finalize();
316     }
317   }
318
319   /**
320    * DOCUMENT ME!
321    * 
322    * @param s
323    *          DOCUMENT ME!
324    */
325   @Override
326   public void deleteSequence(SequenceI s)
327   {
328     deleteSequence(findIndex(s));
329   }
330
331   /**
332    * DOCUMENT ME!
333    * 
334    * @param i
335    *          DOCUMENT ME!
336    */
337   @Override
338   public void deleteSequence(int i)
339   {
340     if (i > -1 && i < getHeight())
341     {
342       synchronized (sequences)
343       {
344         sequences.remove(i);
345         hiddenSequences.adjustHeightSequenceDeleted(i);
346       }
347     }
348   }
349
350   /*
351    * (non-Javadoc)
352    * 
353    * @see jalview.datamodel.AlignmentI#findGroup(jalview.datamodel.SequenceI)
354    */
355   @Override
356   public SequenceGroup findGroup(SequenceI s)
357   {
358     synchronized (groups)
359     {
360       for (int i = 0; i < this.groups.size(); i++)
361       {
362         SequenceGroup sg = groups.get(i);
363
364         if (sg.getSequences(null).contains(s))
365         {
366           return sg;
367         }
368       }
369     }
370     return null;
371   }
372
373   /*
374    * (non-Javadoc)
375    * 
376    * @see
377    * jalview.datamodel.AlignmentI#findAllGroups(jalview.datamodel.SequenceI)
378    */
379   @Override
380   public SequenceGroup[] findAllGroups(SequenceI s)
381   {
382     ArrayList<SequenceGroup> temp = new ArrayList<SequenceGroup>();
383
384     synchronized (groups)
385     {
386       int gSize = groups.size();
387       for (int i = 0; i < gSize; i++)
388       {
389         SequenceGroup sg = groups.get(i);
390         if (sg == null || sg.getSequences() == null)
391         {
392           this.deleteGroup(sg);
393           gSize--;
394           continue;
395         }
396
397         if (sg.getSequences().contains(s))
398         {
399           temp.add(sg);
400         }
401       }
402     }
403     SequenceGroup[] ret = new SequenceGroup[temp.size()];
404     return temp.toArray(ret);
405   }
406
407   /**    */
408   @Override
409   public void addGroup(SequenceGroup sg)
410   {
411     synchronized (groups)
412     {
413       if (!groups.contains(sg))
414       {
415         if (hiddenSequences.getSize() > 0)
416         {
417           int i, iSize = sg.getSize();
418           for (i = 0; i < iSize; i++)
419           {
420             if (!sequences.contains(sg.getSequenceAt(i)))
421             {
422               sg.deleteSequence(sg.getSequenceAt(i), false);
423               iSize--;
424               i--;
425             }
426           }
427
428           if (sg.getSize() < 1)
429           {
430             return;
431           }
432         }
433         sg.setContext(this);
434         groups.add(sg);
435       }
436     }
437   }
438
439   /**
440    * remove any annotation that references gp
441    * 
442    * @param gp
443    *          (if null, removes all group associated annotation)
444    */
445   private void removeAnnotationForGroup(SequenceGroup gp)
446   {
447     if (annotations == null || annotations.length == 0)
448     {
449       return;
450     }
451     // remove annotation very quickly
452     AlignmentAnnotation[] t, todelete = new AlignmentAnnotation[annotations.length], tokeep = new AlignmentAnnotation[annotations.length];
453     int i, p, k;
454     if (gp == null)
455     {
456       for (i = 0, p = 0, k = 0; i < annotations.length; i++)
457       {
458         if (annotations[i].groupRef != null)
459         {
460           todelete[p++] = annotations[i];
461         }
462         else
463         {
464           tokeep[k++] = annotations[i];
465         }
466       }
467     }
468     else
469     {
470       for (i = 0, p = 0, k = 0; i < annotations.length; i++)
471       {
472         if (annotations[i].groupRef == gp)
473         {
474           todelete[p++] = annotations[i];
475         }
476         else
477         {
478           tokeep[k++] = annotations[i];
479         }
480       }
481     }
482     if (p > 0)
483     {
484       // clear out the group associated annotation.
485       for (i = 0; i < p; i++)
486       {
487         unhookAnnotation(todelete[i]);
488         todelete[i] = null;
489       }
490       t = new AlignmentAnnotation[k];
491       for (i = 0; i < k; i++)
492       {
493         t[i] = tokeep[i];
494       }
495       annotations = t;
496     }
497   }
498
499   @Override
500   public void deleteAllGroups()
501   {
502     synchronized (groups)
503     {
504       if (annotations != null)
505       {
506         removeAnnotationForGroup(null);
507       }
508       for (SequenceGroup sg : groups)
509       {
510         sg.setContext(null);
511       }
512       groups.clear();
513     }
514   }
515
516   /**    */
517   @Override
518   public void deleteGroup(SequenceGroup g)
519   {
520     synchronized (groups)
521     {
522       if (groups.contains(g))
523       {
524         removeAnnotationForGroup(g);
525         groups.remove(g);
526         g.setContext(null);
527       }
528     }
529   }
530
531   /**    */
532   @Override
533   public SequenceI findName(String name)
534   {
535     return findName(name, false);
536   }
537
538   /*
539    * (non-Javadoc)
540    * 
541    * @see jalview.datamodel.AlignmentI#findName(java.lang.String, boolean)
542    */
543   @Override
544   public SequenceI findName(String token, boolean b)
545   {
546     return findName(null, token, b);
547   }
548
549   /*
550    * (non-Javadoc)
551    * 
552    * @see jalview.datamodel.AlignmentI#findName(SequenceI, java.lang.String,
553    * boolean)
554    */
555   @Override
556   public SequenceI findName(SequenceI startAfter, String token, boolean b)
557   {
558
559     int i = 0;
560     SequenceI sq = null;
561     String sqname = null;
562     if (startAfter != null)
563     {
564       // try to find the sequence in the alignment
565       boolean matched = false;
566       while (i < sequences.size())
567       {
568         if (getSequenceAt(i++) == startAfter)
569         {
570           matched = true;
571           break;
572         }
573       }
574       if (!matched)
575       {
576         i = 0;
577       }
578     }
579     while (i < sequences.size())
580     {
581       sq = getSequenceAt(i);
582       sqname = sq.getName();
583       if (sqname.equals(token) // exact match
584               || (b && // allow imperfect matches - case varies
585               (sqname.equalsIgnoreCase(token))))
586       {
587         return getSequenceAt(i);
588       }
589
590       i++;
591     }
592
593     return null;
594   }
595
596   @Override
597   public SequenceI[] findSequenceMatch(String name)
598   {
599     Vector matches = new Vector();
600     int i = 0;
601
602     while (i < sequences.size())
603     {
604       if (getSequenceAt(i).getName().equals(name))
605       {
606         matches.addElement(getSequenceAt(i));
607       }
608       i++;
609     }
610
611     SequenceI[] result = new SequenceI[matches.size()];
612     for (i = 0; i < result.length; i++)
613     {
614       result[i] = (SequenceI) matches.elementAt(i);
615     }
616
617     return result;
618
619   }
620
621   /*
622    * (non-Javadoc)
623    * 
624    * @see jalview.datamodel.AlignmentI#findIndex(jalview.datamodel.SequenceI)
625    */
626   @Override
627   public int findIndex(SequenceI s)
628   {
629     int i = 0;
630
631     while (i < sequences.size())
632     {
633       if (s == getSequenceAt(i))
634       {
635         return i;
636       }
637
638       i++;
639     }
640
641     return -1;
642   }
643
644   /*
645    * (non-Javadoc)
646    * 
647    * @see
648    * jalview.datamodel.AlignmentI#findIndex(jalview.datamodel.SearchResults)
649    */
650   @Override
651   public int findIndex(SearchResults results)
652   {
653     int i = 0;
654
655     while (i < sequences.size())
656     {
657       if (results.involvesSequence(getSequenceAt(i)))
658       {
659         return i;
660       }
661       i++;
662     }
663     return -1;
664   }
665
666   /**
667    * DOCUMENT ME!
668    * 
669    * @return DOCUMENT ME!
670    */
671   @Override
672   public int getHeight()
673   {
674     return sequences.size();
675   }
676
677   /**
678    * DOCUMENT ME!
679    * 
680    * @return DOCUMENT ME!
681    */
682   @Override
683   public int getWidth()
684   {
685     int maxLength = -1;
686
687     for (int i = 0; i < sequences.size(); i++)
688     {
689       if (getSequenceAt(i).getLength() > maxLength)
690       {
691         maxLength = getSequenceAt(i).getLength();
692       }
693     }
694
695     return maxLength;
696   }
697
698   /**
699    * DOCUMENT ME!
700    * 
701    * @param gc
702    *          DOCUMENT ME!
703    */
704   @Override
705   public void setGapCharacter(char gc)
706   {
707     gapCharacter = gc;
708     synchronized (sequences)
709     {
710       for (SequenceI seq : sequences)
711       {
712         seq.setSequence(seq.getSequenceAsString().replace('.', gc)
713                 .replace('-', gc).replace(' ', gc));
714       }
715     }
716   }
717
718   /**
719    * DOCUMENT ME!
720    * 
721    * @return DOCUMENT ME!
722    */
723   @Override
724   public char getGapCharacter()
725   {
726     return gapCharacter;
727   }
728
729   /*
730    * (non-Javadoc)
731    * 
732    * @see jalview.datamodel.AlignmentI#isAligned()
733    */
734   @Override
735   public boolean isAligned()
736   {
737     return isAligned(false);
738   }
739
740   /*
741    * (non-Javadoc)
742    * 
743    * @see jalview.datamodel.AlignmentI#isAligned(boolean)
744    */
745   @Override
746   public boolean isAligned(boolean includeHidden)
747   {
748     int width = getWidth();
749     if (hiddenSequences == null || hiddenSequences.getSize() == 0)
750     {
751       includeHidden = true; // no hidden sequences to check against.
752     }
753     for (int i = 0; i < sequences.size(); i++)
754     {
755       if (includeHidden || !hiddenSequences.isHidden(getSequenceAt(i)))
756       {
757         if (getSequenceAt(i).getLength() != width)
758         {
759           return false;
760         }
761       }
762     }
763
764     return true;
765   }
766
767   /**
768    * Delete all annotations, including auto-calculated if the flag is set true.
769    * Returns true if at least one annotation was deleted, else false.
770    * 
771    * @param includingAutoCalculated
772    * @return
773    */
774   @Override
775   public boolean deleteAllAnnotations(boolean includingAutoCalculated)
776   {
777     boolean result = false;
778     for (AlignmentAnnotation alan : getAlignmentAnnotation())
779     {
780       if (!alan.autoCalculated || includingAutoCalculated)
781       {
782         deleteAnnotation(alan);
783         result = true;
784       }
785     }
786     return result;
787   }
788
789   /*
790    * (non-Javadoc)
791    * 
792    * @seejalview.datamodel.AlignmentI#deleteAnnotation(jalview.datamodel.
793    * AlignmentAnnotation)
794    */
795   @Override
796   public boolean deleteAnnotation(AlignmentAnnotation aa)
797   {
798     return deleteAnnotation(aa, true);
799   }
800
801   @Override
802   public boolean deleteAnnotation(AlignmentAnnotation aa, boolean unhook)
803   {
804     int aSize = 1;
805
806     if (annotations != null)
807     {
808       aSize = annotations.length;
809     }
810
811     if (aSize < 1)
812     {
813       return false;
814     }
815
816     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize - 1];
817
818     boolean swap = false;
819     int tIndex = 0;
820
821     for (int i = 0; i < aSize; i++)
822     {
823       if (annotations[i] == aa)
824       {
825         swap = true;
826         continue;
827       }
828       if (tIndex < temp.length)
829       {
830         temp[tIndex++] = annotations[i];
831       }
832     }
833
834     if (swap)
835     {
836       annotations = temp;
837       if (unhook)
838       {
839         unhookAnnotation(aa);
840       }
841     }
842     return swap;
843   }
844
845   /**
846    * remove any object references associated with this annotation
847    * 
848    * @param aa
849    */
850   private void unhookAnnotation(AlignmentAnnotation aa)
851   {
852     if (aa.sequenceRef != null)
853     {
854       aa.sequenceRef.removeAlignmentAnnotation(aa);
855     }
856     if (aa.groupRef != null)
857     {
858       // probably need to do more here in the future (post 2.5.0)
859       aa.groupRef = null;
860     }
861   }
862
863   /*
864    * (non-Javadoc)
865    * 
866    * @seejalview.datamodel.AlignmentI#addAnnotation(jalview.datamodel.
867    * AlignmentAnnotation)
868    */
869   @Override
870   public void addAnnotation(AlignmentAnnotation aa)
871   {
872     addAnnotation(aa, -1);
873   }
874
875   /*
876    * (non-Javadoc)
877    * 
878    * @seejalview.datamodel.AlignmentI#addAnnotation(jalview.datamodel.
879    * AlignmentAnnotation, int)
880    */
881   @Override
882   public void addAnnotation(AlignmentAnnotation aa, int pos)
883   {
884     if (aa.getRNAStruc() != null)
885     {
886       hasRNAStructure = true;
887     }
888
889     int aSize = 1;
890     if (annotations != null)
891     {
892       aSize = annotations.length + 1;
893     }
894
895     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
896     int i = 0;
897     if (pos == -1 || pos >= aSize)
898     {
899       temp[aSize - 1] = aa;
900     }
901     else
902     {
903       temp[pos] = aa;
904     }
905     if (aSize > 1)
906     {
907       int p = 0;
908       for (i = 0; i < (aSize - 1); i++, p++)
909       {
910         if (p == pos)
911         {
912           p++;
913         }
914         if (p < temp.length)
915         {
916           temp[p] = annotations[i];
917         }
918       }
919     }
920
921     annotations = temp;
922   }
923
924   @Override
925   public void setAnnotationIndex(AlignmentAnnotation aa, int index)
926   {
927     if (aa == null || annotations == null || annotations.length - 1 < index)
928     {
929       return;
930     }
931
932     int aSize = annotations.length;
933     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
934
935     temp[index] = aa;
936
937     for (int i = 0; i < aSize; i++)
938     {
939       if (i == index)
940       {
941         continue;
942       }
943
944       if (i < index)
945       {
946         temp[i] = annotations[i];
947       }
948       else
949       {
950         temp[i] = annotations[i - 1];
951       }
952     }
953
954     annotations = temp;
955   }
956
957   @Override
958   /**
959    * returns all annotation on the alignment
960    */
961   public AlignmentAnnotation[] getAlignmentAnnotation()
962   {
963     return annotations;
964   }
965
966   @Override
967   public void setNucleotide(boolean b)
968   {
969     if (b)
970     {
971       type = NUCLEOTIDE;
972     }
973     else
974     {
975       type = PROTEIN;
976     }
977   }
978
979   @Override
980   public boolean isNucleotide()
981   {
982     if (type == NUCLEOTIDE)
983     {
984       return true;
985     }
986     else
987     {
988       return false;
989     }
990   }
991
992   @Override
993   public boolean hasRNAStructure()
994   {
995     // TODO can it happen that structure is removed from alignment?
996     return hasRNAStructure;
997   }
998
999   @Override
1000   public void setDataset(AlignmentI data)
1001   {
1002     if (dataset == null && data == null)
1003     {
1004       createDatasetAlignment();
1005     }
1006     else if (dataset == null && data != null)
1007     {
1008       if (!(data instanceof Alignment))
1009       {
1010         throw new Error(
1011                 "Implementation Error: jalview.datamodel.Alignment does not yet support other implementations of AlignmentI as its dataset reference");
1012       }
1013       dataset = (Alignment) data;
1014       for (int i = 0; i < getHeight(); i++)
1015       {
1016         SequenceI currentSeq = getSequenceAt(i);
1017         SequenceI dsq = currentSeq.getDatasetSequence();
1018         if (dsq == null)
1019         {
1020           dsq = currentSeq.createDatasetSequence();
1021           dataset.addSequence(dsq);
1022         }
1023         else
1024         {
1025           while (dsq.getDatasetSequence() != null)
1026           {
1027             dsq = dsq.getDatasetSequence();
1028           }
1029           if (dataset.findIndex(dsq) == -1)
1030           {
1031             dataset.addSequence(dsq);
1032           }
1033         }
1034       }
1035     }
1036     dataset.addAlignmentRef();
1037   }
1038
1039   /**
1040    * add dataset sequences to seq for currentSeq and any sequences it references
1041    */
1042   private void resolveAndAddDatasetSeq(SequenceI currentSeq,
1043           Set<SequenceI> seqs, boolean createDatasetSequence)
1044   {
1045     if (currentSeq.getDatasetSequence() != null)
1046     {
1047       currentSeq = currentSeq.getDatasetSequence();
1048     }
1049     else
1050     {
1051       if (createDatasetSequence)
1052       {
1053         currentSeq = currentSeq.createDatasetSequence();
1054       }
1055     }
1056     if (seqs.contains(currentSeq))
1057     {
1058       return;
1059     }
1060     List<SequenceI> toProcess = new ArrayList<SequenceI>();
1061     toProcess.add(currentSeq);
1062     while (toProcess.size() > 0)
1063     {
1064       // use a queue ?
1065       SequenceI curDs = toProcess.remove(0);
1066       if (seqs.contains(curDs))
1067       {
1068         continue;
1069       }
1070       seqs.add(curDs);
1071       // iterate over database references, making sure we add forward referenced
1072       // sequences
1073       if (curDs.getDBRefs() != null)
1074       {
1075         for (DBRefEntry dbr : curDs.getDBRefs())
1076         {
1077           if (dbr.getMap() != null && dbr.getMap().getTo() != null)
1078           {
1079             if (dbr.getMap().getTo().getDatasetSequence() != null)
1080             {
1081               throw new Error("Implementation error: Map.getTo() for dbref"
1082                       + dbr + " is not a dataset sequence.");
1083               // TODO: if this happens, could also rewrite the reference to
1084               // point to new dataset sequence
1085             }
1086             // we recurse to add all forward references to dataset sequences via
1087             // DBRefs/etc
1088             toProcess.add(dbr.getMap().getTo());
1089           }
1090         }
1091       }
1092     }
1093   }
1094
1095   /**
1096    * Creates a new dataset for this alignment. Can only be done once - if
1097    * dataset is not null this will not be performed.
1098    */
1099   public void createDatasetAlignment()
1100   {
1101     if (dataset != null)
1102     {
1103       return;
1104     }
1105     // try to avoid using SequenceI.equals at this stage, it will be expensive
1106     Set<SequenceI> seqs = new jalview.util.LinkedIdentityHashSet<SequenceI>();
1107
1108     for (int i = 0; i < getHeight(); i++)
1109     {
1110       SequenceI currentSeq = getSequenceAt(i);
1111       resolveAndAddDatasetSeq(currentSeq, seqs, true);
1112     }
1113
1114     // verify all mappings are in dataset
1115     for (AlignedCodonFrame cf : codonFrameList)
1116     {
1117       for (SequenceToSequenceMapping ssm : cf.getMappings())
1118       {
1119         if (!seqs.contains(ssm.getFromSeq()))
1120         {
1121           resolveAndAddDatasetSeq(ssm.getFromSeq(), seqs, false);
1122         }
1123         if (!seqs.contains(ssm.getMapping().getTo()))
1124         {
1125           resolveAndAddDatasetSeq(ssm.getMapping().getTo(), seqs, false);
1126         }
1127       }
1128     }
1129     // finally construct dataset
1130     dataset = new Alignment(seqs.toArray(new SequenceI[seqs.size()]));
1131     // move mappings to the dataset alignment
1132     dataset.codonFrameList = this.codonFrameList;
1133     this.codonFrameList = null;
1134   }
1135
1136   /**
1137    * reference count for number of alignments referencing this one.
1138    */
1139   int alignmentRefs = 0;
1140
1141   /**
1142    * increase reference count to this alignment.
1143    */
1144   private void addAlignmentRef()
1145   {
1146     alignmentRefs++;
1147   }
1148
1149   @Override
1150   public Alignment getDataset()
1151   {
1152     return dataset;
1153   }
1154
1155   @Override
1156   public boolean padGaps()
1157   {
1158     boolean modified = false;
1159
1160     // Remove excess gaps from the end of alignment
1161     int maxLength = -1;
1162
1163     SequenceI current;
1164     for (int i = 0; i < sequences.size(); i++)
1165     {
1166       current = getSequenceAt(i);
1167       for (int j = current.getLength(); j > maxLength; j--)
1168       {
1169         if (j > maxLength
1170                 && !jalview.util.Comparison.isGap(current.getCharAt(j)))
1171         {
1172           maxLength = j;
1173           break;
1174         }
1175       }
1176     }
1177
1178     maxLength++;
1179
1180     int cLength;
1181     for (int i = 0; i < sequences.size(); i++)
1182     {
1183       current = getSequenceAt(i);
1184       cLength = current.getLength();
1185
1186       if (cLength < maxLength)
1187       {
1188         current.insertCharAt(cLength, maxLength - cLength, gapCharacter);
1189         modified = true;
1190       }
1191       else if (current.getLength() > maxLength)
1192       {
1193         current.deleteChars(maxLength, current.getLength());
1194       }
1195     }
1196     return modified;
1197   }
1198
1199   /**
1200    * Justify the sequences to the left or right by deleting and inserting gaps
1201    * before the initial residue or after the terminal residue
1202    * 
1203    * @param right
1204    *          true if alignment padded to right, false to justify to left
1205    * @return true if alignment was changed
1206    */
1207   @Override
1208   public boolean justify(boolean right)
1209   {
1210     boolean modified = false;
1211
1212     // Remove excess gaps from the end of alignment
1213     int maxLength = -1;
1214     int ends[] = new int[sequences.size() * 2];
1215     SequenceI current;
1216     for (int i = 0; i < sequences.size(); i++)
1217     {
1218       current = getSequenceAt(i);
1219       // This should really be a sequence method
1220       ends[i * 2] = current.findIndex(current.getStart());
1221       ends[i * 2 + 1] = current.findIndex(current.getStart()
1222               + current.getLength());
1223       boolean hitres = false;
1224       for (int j = 0, rs = 0, ssiz = current.getLength(); j < ssiz; j++)
1225       {
1226         if (!jalview.util.Comparison.isGap(current.getCharAt(j)))
1227         {
1228           if (!hitres)
1229           {
1230             ends[i * 2] = j;
1231             hitres = true;
1232           }
1233           else
1234           {
1235             ends[i * 2 + 1] = j;
1236             if (j - ends[i * 2] > maxLength)
1237             {
1238               maxLength = j - ends[i * 2];
1239             }
1240           }
1241         }
1242       }
1243     }
1244
1245     maxLength++;
1246     // now edit the flanking gaps to justify to either left or right
1247     int cLength, extent, diff;
1248     for (int i = 0; i < sequences.size(); i++)
1249     {
1250       current = getSequenceAt(i);
1251
1252       cLength = 1 + ends[i * 2 + 1] - ends[i * 2];
1253       diff = maxLength - cLength; // number of gaps to indent
1254       extent = current.getLength();
1255       if (right)
1256       {
1257         // right justify
1258         if (extent > ends[i * 2 + 1])
1259         {
1260           current.deleteChars(ends[i * 2 + 1] + 1, extent);
1261           modified = true;
1262         }
1263         if (ends[i * 2] > diff)
1264         {
1265           current.deleteChars(0, ends[i * 2] - diff);
1266           modified = true;
1267         }
1268         else
1269         {
1270           if (ends[i * 2] < diff)
1271           {
1272             current.insertCharAt(0, diff - ends[i * 2], gapCharacter);
1273             modified = true;
1274           }
1275         }
1276       }
1277       else
1278       {
1279         // left justify
1280         if (ends[i * 2] > 0)
1281         {
1282           current.deleteChars(0, ends[i * 2]);
1283           modified = true;
1284           ends[i * 2 + 1] -= ends[i * 2];
1285           extent -= ends[i * 2];
1286         }
1287         if (extent > maxLength)
1288         {
1289           current.deleteChars(maxLength + 1, extent);
1290           modified = true;
1291         }
1292         else
1293         {
1294           if (extent < maxLength)
1295           {
1296             current.insertCharAt(extent, maxLength - extent, gapCharacter);
1297             modified = true;
1298           }
1299         }
1300       }
1301     }
1302     return modified;
1303   }
1304
1305   @Override
1306   public HiddenSequences getHiddenSequences()
1307   {
1308     return hiddenSequences;
1309   }
1310
1311   @Override
1312   public CigarArray getCompactAlignment()
1313   {
1314     synchronized (sequences)
1315     {
1316       SeqCigar alseqs[] = new SeqCigar[sequences.size()];
1317       int i = 0;
1318       for (SequenceI seq : sequences)
1319       {
1320         alseqs[i++] = new SeqCigar(seq);
1321       }
1322       CigarArray cal = new CigarArray(alseqs);
1323       cal.addOperation(CigarArray.M, getWidth());
1324       return cal;
1325     }
1326   }
1327
1328   @Override
1329   public void setProperty(Object key, Object value)
1330   {
1331     if (alignmentProperties == null)
1332     {
1333       alignmentProperties = new Hashtable();
1334     }
1335
1336     alignmentProperties.put(key, value);
1337   }
1338
1339   @Override
1340   public Object getProperty(Object key)
1341   {
1342     if (alignmentProperties != null)
1343     {
1344       return alignmentProperties.get(key);
1345     }
1346     else
1347     {
1348       return null;
1349     }
1350   }
1351
1352   @Override
1353   public Hashtable getProperties()
1354   {
1355     return alignmentProperties;
1356   }
1357
1358   /**
1359    * Adds the given mapping to the stored set. Note this may be held on the
1360    * dataset alignment.
1361    */
1362   @Override
1363   public void addCodonFrame(AlignedCodonFrame codons)
1364   {
1365     List<AlignedCodonFrame> acfs = getCodonFrames();
1366     if (codons != null && acfs != null && !acfs.contains(codons))
1367     {
1368       acfs.add(codons);
1369     }
1370   }
1371
1372   /*
1373    * (non-Javadoc)
1374    * 
1375    * @see
1376    * jalview.datamodel.AlignmentI#getCodonFrame(jalview.datamodel.SequenceI)
1377    */
1378   @Override
1379   public List<AlignedCodonFrame> getCodonFrame(SequenceI seq)
1380   {
1381     if (seq == null)
1382     {
1383       return null;
1384     }
1385     List<AlignedCodonFrame> cframes = new ArrayList<AlignedCodonFrame>();
1386     for (AlignedCodonFrame acf : getCodonFrames())
1387     {
1388       if (acf.involvesSequence(seq))
1389       {
1390         cframes.add(acf);
1391       }
1392     }
1393     return cframes;
1394   }
1395
1396   /**
1397    * Sets the codon frame mappings (replacing any existing mappings). Note the
1398    * mappings are set on the dataset alignment instead if there is one.
1399    * 
1400    * @see jalview.datamodel.AlignmentI#setCodonFrames()
1401    */
1402   @Override
1403   public void setCodonFrames(List<AlignedCodonFrame> acfs)
1404   {
1405     if (dataset != null)
1406     {
1407       dataset.setCodonFrames(acfs);
1408     }
1409     else
1410     {
1411       this.codonFrameList = acfs;
1412     }
1413   }
1414
1415   /**
1416    * Returns the set of codon frame mappings. Any changes to the returned set
1417    * will affect the alignment. The mappings are held on (and read from) the
1418    * dataset alignment if there is one.
1419    * 
1420    * @see jalview.datamodel.AlignmentI#getCodonFrames()
1421    */
1422   @Override
1423   public List<AlignedCodonFrame> getCodonFrames()
1424   {
1425     // TODO: Fix this method to fix failing AlignedCodonFrame tests
1426     // this behaviour is currently incorrect. method should return codon frames
1427     // for just the alignment,
1428     // selected from dataset
1429     return dataset != null ? dataset.getCodonFrames() : codonFrameList;
1430   }
1431
1432   /**
1433    * Removes the given mapping from the stored set. Note that the mappings are
1434    * held on the dataset alignment if there is one.
1435    */
1436   @Override
1437   public boolean removeCodonFrame(AlignedCodonFrame codons)
1438   {
1439     List<AlignedCodonFrame> acfs = getCodonFrames();
1440     if (codons == null || acfs == null)
1441     {
1442       return false;
1443     }
1444     return acfs.remove(codons);
1445   }
1446
1447   @Override
1448   public void append(AlignmentI toappend)
1449   {
1450     // TODO JAL-1270 needs test coverage
1451     // currently tested for use in jalview.gui.SequenceFetcher
1452     boolean samegap = toappend.getGapCharacter() == getGapCharacter();
1453     char oldc = toappend.getGapCharacter();
1454     boolean hashidden = toappend.getHiddenSequences() != null
1455             && toappend.getHiddenSequences().hiddenSequences != null;
1456     // get all sequences including any hidden ones
1457     List<SequenceI> sqs = (hashidden) ? toappend.getHiddenSequences()
1458             .getFullAlignment().getSequences() : toappend.getSequences();
1459     if (sqs != null)
1460     {
1461       // avoid self append deadlock by
1462       List<SequenceI> toappendsq = new ArrayList<SequenceI>();
1463       synchronized (sqs)
1464       {
1465         for (SequenceI addedsq : sqs)
1466         {
1467           if (!samegap)
1468           {
1469             char[] oldseq = addedsq.getSequence();
1470             for (int c = 0; c < oldseq.length; c++)
1471             {
1472               if (oldseq[c] == oldc)
1473               {
1474                 oldseq[c] = gapCharacter;
1475               }
1476             }
1477           }
1478           toappendsq.add(addedsq);
1479         }
1480       }
1481       for (SequenceI addedsq : toappendsq)
1482       {
1483         addSequence(addedsq);
1484       }
1485     }
1486     AlignmentAnnotation[] alan = toappend.getAlignmentAnnotation();
1487     for (int a = 0; alan != null && a < alan.length; a++)
1488     {
1489       addAnnotation(alan[a]);
1490     }
1491
1492     // use add method
1493     getCodonFrames().addAll(toappend.getCodonFrames());
1494
1495     List<SequenceGroup> sg = toappend.getGroups();
1496     if (sg != null)
1497     {
1498       for (SequenceGroup _sg : sg)
1499       {
1500         addGroup(_sg);
1501       }
1502     }
1503     if (toappend.getHiddenSequences() != null)
1504     {
1505       HiddenSequences hs = toappend.getHiddenSequences();
1506       if (hiddenSequences == null)
1507       {
1508         hiddenSequences = new HiddenSequences(this);
1509       }
1510       if (hs.hiddenSequences != null)
1511       {
1512         for (int s = 0; s < hs.hiddenSequences.length; s++)
1513         {
1514           // hide the newly appended sequence in the alignment
1515           if (hs.hiddenSequences[s] != null)
1516           {
1517             hiddenSequences.hideSequence(hs.hiddenSequences[s]);
1518           }
1519         }
1520       }
1521     }
1522     if (toappend.getProperties() != null)
1523     {
1524       // we really can't do very much here - just try to concatenate strings
1525       // where property collisions occur.
1526       Enumeration key = toappend.getProperties().keys();
1527       while (key.hasMoreElements())
1528       {
1529         Object k = key.nextElement();
1530         Object ourval = this.getProperty(k);
1531         Object toapprop = toappend.getProperty(k);
1532         if (ourval != null)
1533         {
1534           if (ourval.getClass().equals(toapprop.getClass())
1535                   && !ourval.equals(toapprop))
1536           {
1537             if (ourval instanceof String)
1538             {
1539               // append strings
1540               this.setProperty(k, ((String) ourval) + "; "
1541                       + ((String) toapprop));
1542             }
1543             else
1544             {
1545               if (ourval instanceof Vector)
1546               {
1547                 // append vectors
1548                 Enumeration theirv = ((Vector) toapprop).elements();
1549                 while (theirv.hasMoreElements())
1550                 {
1551                   ((Vector) ourval).addElement(theirv);
1552                 }
1553               }
1554             }
1555           }
1556         }
1557         else
1558         {
1559           // just add new property directly
1560           setProperty(k, toapprop);
1561         }
1562
1563       }
1564     }
1565   }
1566
1567   @Override
1568   public AlignmentAnnotation findOrCreateAnnotation(String name,
1569           String calcId, boolean autoCalc, SequenceI seqRef,
1570           SequenceGroup groupRef)
1571   {
1572     assert (name != null);
1573     if (annotations != null)
1574     {
1575       for (AlignmentAnnotation annot : getAlignmentAnnotation())
1576       {
1577         if (annot.autoCalculated == autoCalc && (name.equals(annot.label))
1578                 && (calcId == null || annot.getCalcId().equals(calcId))
1579                 && annot.sequenceRef == seqRef
1580                 && annot.groupRef == groupRef)
1581         {
1582           return annot;
1583         }
1584       }
1585     }
1586     AlignmentAnnotation annot = new AlignmentAnnotation(name, name,
1587             new Annotation[1], 0f, 0f, AlignmentAnnotation.BAR_GRAPH);
1588     annot.hasText = false;
1589     annot.setCalcId(new String(calcId));
1590     annot.autoCalculated = autoCalc;
1591     if (seqRef != null)
1592     {
1593       annot.setSequenceRef(seqRef);
1594     }
1595     annot.groupRef = groupRef;
1596     addAnnotation(annot);
1597
1598     return annot;
1599   }
1600
1601   @Override
1602   public Iterable<AlignmentAnnotation> findAnnotation(String calcId)
1603   {
1604     ArrayList<AlignmentAnnotation> aa = new ArrayList<AlignmentAnnotation>();
1605     for (AlignmentAnnotation a : getAlignmentAnnotation())
1606     {
1607       if (a.getCalcId() == calcId
1608               || (a.getCalcId() != null && calcId != null && a.getCalcId()
1609                       .equals(calcId)))
1610       {
1611         aa.add(a);
1612       }
1613     }
1614     return aa;
1615   }
1616
1617   /**
1618    * Returns an iterable collection of any annotations that match on given
1619    * sequence ref, calcId and label (ignoring null values).
1620    */
1621   @Override
1622   public Iterable<AlignmentAnnotation> findAnnotations(SequenceI seq,
1623           String calcId, String label)
1624   {
1625     ArrayList<AlignmentAnnotation> aa = new ArrayList<AlignmentAnnotation>();
1626     for (AlignmentAnnotation ann : getAlignmentAnnotation())
1627     {
1628       if (ann.getCalcId() != null && ann.getCalcId().equals(calcId)
1629               && ann.sequenceRef != null && ann.sequenceRef == seq
1630               && ann.label != null && ann.label.equals(label))
1631       {
1632         aa.add(ann);
1633       }
1634     }
1635     return aa;
1636   }
1637
1638   @Override
1639   public void moveSelectedSequencesByOne(SequenceGroup sg,
1640           Map<SequenceI, SequenceCollectionI> map, boolean up)
1641   {
1642     synchronized (sequences)
1643     {
1644       if (up)
1645       {
1646
1647         for (int i = 1, iSize = sequences.size(); i < iSize; i++)
1648         {
1649           SequenceI seq = sequences.get(i);
1650           if (!sg.getSequences(map).contains(seq))
1651           {
1652             continue;
1653           }
1654
1655           SequenceI temp = sequences.get(i - 1);
1656           if (sg.getSequences(null).contains(temp))
1657           {
1658             continue;
1659           }
1660
1661           sequences.set(i, temp);
1662           sequences.set(i - 1, seq);
1663         }
1664       }
1665       else
1666       {
1667         for (int i = sequences.size() - 2; i > -1; i--)
1668         {
1669           SequenceI seq = sequences.get(i);
1670           if (!sg.getSequences(map).contains(seq))
1671           {
1672             continue;
1673           }
1674
1675           SequenceI temp = sequences.get(i + 1);
1676           if (sg.getSequences(map).contains(temp))
1677           {
1678             continue;
1679           }
1680
1681           sequences.set(i, temp);
1682           sequences.set(i + 1, seq);
1683         }
1684       }
1685
1686     }
1687   }
1688
1689   @Override
1690   public void validateAnnotation(AlignmentAnnotation alignmentAnnotation)
1691   {
1692     alignmentAnnotation.validateRangeAndDisplay();
1693     if (isNucleotide() && alignmentAnnotation.isValidStruc())
1694     {
1695       hasRNAStructure = true;
1696     }
1697   }
1698
1699   private SequenceI seqrep = null;
1700
1701   /**
1702    * 
1703    * @return the representative sequence for this group
1704    */
1705   @Override
1706   public SequenceI getSeqrep()
1707   {
1708     return seqrep;
1709   }
1710
1711   /**
1712    * set the representative sequence for this group. Note - this affects the
1713    * interpretation of the Hidereps attribute.
1714    * 
1715    * @param seqrep
1716    *          the seqrep to set (null means no sequence representative)
1717    */
1718   @Override
1719   public void setSeqrep(SequenceI seqrep)
1720   {
1721     this.seqrep = seqrep;
1722   }
1723
1724   /**
1725    * 
1726    * @return true if group has a sequence representative
1727    */
1728   @Override
1729   public boolean hasSeqrep()
1730   {
1731     return seqrep != null;
1732   }
1733
1734   @Override
1735   public int getEndRes()
1736   {
1737     return getWidth() - 1;
1738   }
1739
1740   @Override
1741   public int getStartRes()
1742   {
1743     return 0;
1744   }
1745
1746   /*
1747    * In the case of AlignmentI - returns the dataset for the alignment, if set
1748    * (non-Javadoc)
1749    * 
1750    * @see jalview.datamodel.AnnotatedCollectionI#getContext()
1751    */
1752   @Override
1753   public AnnotatedCollectionI getContext()
1754   {
1755     return dataset;
1756   }
1757
1758   /**
1759    * Align this alignment like the given (mapped) one.
1760    */
1761   @Override
1762   public int alignAs(AlignmentI al)
1763   {
1764     /*
1765      * Currently retains unmapped gaps (in introns), regaps mapped regions
1766      * (exons)
1767      */
1768     return alignAs(al, false, true);
1769   }
1770
1771   /**
1772    * Align this alignment 'the same as' the given one. Mapped sequences only are
1773    * realigned. If both of the same type (nucleotide/protein) then align both
1774    * identically. If this is nucleotide and the other is protein, make 3 gaps
1775    * for each gap in the protein sequences. If this is protein and the other is
1776    * nucleotide, insert a gap for each 3 gaps (or part thereof) between
1777    * nucleotide bases. If this is protein and the other is nucleotide, gaps
1778    * protein to match the relative ordering of codons in the nucleotide.
1779    * 
1780    * Parameters control whether gaps in exon (mapped) and intron (unmapped)
1781    * regions are preserved. Gaps that connect introns to exons are treated
1782    * conservatively, i.e. only preserved if both intron and exon gaps are
1783    * preserved. TODO: check caveats below where the implementation fails
1784    * 
1785    * @param al
1786    *          - must have same dataset, and sequences in al must have equivalent
1787    *          dataset sequence and start/end bounds under given mapping
1788    * @param preserveMappedGaps
1789    *          if true, gaps within and between mapped codons are preserved
1790    * @param preserveUnmappedGaps
1791    *          if true, gaps within and between unmapped codons are preserved
1792    */
1793   // @Override
1794   public int alignAs(AlignmentI al, boolean preserveMappedGaps,
1795           boolean preserveUnmappedGaps)
1796   {
1797     // TODO should this method signature be the one in the interface?
1798     // JBPComment - yes - neither flag is used, so should be deleted.
1799     boolean thisIsNucleotide = this.isNucleotide();
1800     boolean thatIsProtein = !al.isNucleotide();
1801     if (!thatIsProtein && !thisIsNucleotide)
1802     {
1803       return AlignmentUtils.alignProteinAsDna(this, al);
1804     }
1805     else if (thatIsProtein && thisIsNucleotide)
1806     {
1807       return AlignmentUtils.alignCdsAsProtein(this, al);
1808     }
1809     return AlignmentUtils.alignAs(this, al);
1810   }
1811
1812   /**
1813    * Returns the alignment in Fasta format. Behaviour of this method is not
1814    * guaranteed between versions.
1815    */
1816   @Override
1817   public String toString()
1818   {
1819     return new FastaFile().print(getSequencesArray());
1820   }
1821
1822   /**
1823    * Returns the set of distinct sequence names. No ordering is guaranteed.
1824    */
1825   @Override
1826   public Set<String> getSequenceNames()
1827   {
1828     Set<String> names = new HashSet<String>();
1829     for (SequenceI seq : getSequences())
1830     {
1831       names.add(seq.getName());
1832     }
1833     return names;
1834   }
1835
1836   @Override
1837   public boolean hasValidSequence()
1838   {
1839     boolean hasValidSeq = false;
1840     for (SequenceI seq : getSequences())
1841     {
1842       if ((seq.getEnd() - seq.getStart()) > 0)
1843       {
1844         hasValidSeq = true;
1845         break;
1846       }
1847     }
1848     return hasValidSeq;
1849   }
1850
1851   /**
1852    * Update any mappings to 'virtual' sequences to compatible real ones, if
1853    * present in the added sequences. Returns a count of mappings updated.
1854    * 
1855    * @param seqs
1856    * @return
1857    */
1858   @Override
1859   public int realiseMappings(List<SequenceI> seqs)
1860   {
1861     int count = 0;
1862     for (SequenceI seq : seqs)
1863     {
1864       for (AlignedCodonFrame mapping : getCodonFrames())
1865       {
1866         count += mapping.realiseWith(seq);
1867       }
1868     }
1869     return count;
1870   }
1871
1872   /**
1873    * Returns the first AlignedCodonFrame that has a mapping between the given
1874    * dataset sequences
1875    * 
1876    * @param mapFrom
1877    * @param mapTo
1878    * @return
1879    */
1880   @Override
1881   public AlignedCodonFrame getMapping(SequenceI mapFrom, SequenceI mapTo)
1882   {
1883     for (AlignedCodonFrame acf : getCodonFrames())
1884     {
1885       if (acf.getAaForDnaSeq(mapFrom) == mapTo)
1886       {
1887         return acf;
1888       }
1889     }
1890     return null;
1891   }
1892
1893   @Override
1894   public int[] getVisibleStartAndEndIndex(List<int[]> hiddenCols)
1895   {
1896     int[] alignmentStartEnd = new int[] { 0, getWidth() - 1 };
1897     int startPos = alignmentStartEnd[0];
1898     int endPos = alignmentStartEnd[1];
1899
1900     int[] lowestRange = new int[] { -1, -1 };
1901     int[] higestRange = new int[] { -1, -1 };
1902
1903     for (int[] hiddenCol : hiddenCols)
1904     {
1905       lowestRange = (hiddenCol[0] <= startPos) ? hiddenCol : lowestRange;
1906       higestRange = (hiddenCol[1] >= endPos) ? hiddenCol : higestRange;
1907     }
1908
1909     if (lowestRange[0] == -1 && lowestRange[1] == -1)
1910     {
1911       startPos = alignmentStartEnd[0];
1912     }
1913     else
1914     {
1915       startPos = lowestRange[1] + 1;
1916     }
1917
1918     if (higestRange[0] == -1 && higestRange[1] == -1)
1919     {
1920       endPos = alignmentStartEnd[1];
1921     }
1922     else
1923     {
1924       endPos = higestRange[0] - 1;
1925     }
1926     return new int[] { startPos, endPos };
1927   }
1928 }