JAL-2069 spike updated with latest (FeatureTypeSettings)
[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.LinkedIdentityHashSet;
28 import jalview.util.MessageManager;
29
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.Collections;
33 import java.util.Enumeration;
34 import java.util.HashSet;
35 import java.util.Hashtable;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Set;
39 import java.util.Vector;
40
41 /**
42  * Data structure to hold and manipulate a multiple sequence alignment
43  */
44 /**
45  * @author JimP
46  * 
47  */
48 public class Alignment implements AlignmentI
49 {
50   private Alignment dataset;
51
52   protected List<SequenceI> sequences;
53
54   protected List<SequenceGroup> groups;
55
56   protected char gapCharacter = '-';
57
58   private boolean nucleotide = true;
59
60   public boolean hasRNAStructure = false;
61
62   public AlignmentAnnotation[] annotations;
63
64   HiddenSequences hiddenSequences;
65
66   HiddenColumns hiddenCols;
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     hiddenCols = new HiddenColumns();
77     codonFrameList = new ArrayList<>();
78
79     nucleotide = Comparison.isNucleotide(seqs);
80
81     sequences = Collections.synchronizedList(new ArrayList<SequenceI>());
82
83     for (int i = 0; i < seqs.length; i++)
84     {
85       sequences.add(seqs[i]);
86     }
87
88   }
89
90   /**
91    * Make a 'copy' alignment - sequences have new copies of features and
92    * annotations, but share the original dataset sequences.
93    */
94   public Alignment(AlignmentI al)
95   {
96     SequenceI[] seqs = al.getSequencesArray();
97     for (int i = 0; i < seqs.length; i++)
98     {
99       seqs[i] = new Sequence(seqs[i]);
100     }
101
102     initAlignment(seqs);
103
104     /*
105      * Share the same dataset sequence mappings (if any). 
106      */
107     if (dataset == null && al.getDataset() == null)
108     {
109       this.setCodonFrames(al.getCodonFrames());
110     }
111   }
112
113   /**
114    * Make an alignment from an array of Sequences.
115    * 
116    * @param sequences
117    */
118   public Alignment(SequenceI[] seqs)
119   {
120     initAlignment(seqs);
121   }
122
123   /**
124    * Make a new alignment from an array of SeqCigars
125    * 
126    * @param seqs
127    *          SeqCigar[]
128    */
129   public Alignment(SeqCigar[] alseqs)
130   {
131     SequenceI[] seqs = SeqCigar.createAlignmentSequences(alseqs,
132             gapCharacter, new HiddenColumns(), null);
133     initAlignment(seqs);
134   }
135
136   /**
137    * Make a new alignment from an CigarArray JBPNote - can only do this when
138    * compactAlignment does not contain hidden regions. JBPNote - must also check
139    * that compactAlignment resolves to a set of SeqCigars - or construct them
140    * appropriately.
141    * 
142    * @param compactAlignment
143    *          CigarArray
144    */
145   public static AlignmentI createAlignment(CigarArray compactAlignment)
146   {
147     throw new Error(MessageManager
148             .getString("error.alignment_cigararray_not_implemented"));
149     // this(compactAlignment.refCigars);
150   }
151
152   @Override
153   public List<SequenceI> getSequences()
154   {
155     return sequences;
156   }
157
158   @Override
159   public List<SequenceI> getSequences(
160           Map<SequenceI, SequenceCollectionI> hiddenReps)
161   {
162     // TODO: in jalview 2.8 we don't do anything with hiddenreps - fix design to
163     // work on this.
164     return sequences;
165   }
166
167   @Override
168   public SequenceI[] getSequencesArray()
169   {
170     if (sequences == null)
171     {
172       return null;
173     }
174     synchronized (sequences)
175     {
176       return sequences.toArray(new SequenceI[sequences.size()]);
177     }
178   }
179
180   /**
181    * Returns a map of lists of sequences keyed by sequence name.
182    * 
183    * @return
184    */
185   @Override
186   public Map<String, List<SequenceI>> getSequencesByName()
187   {
188     return AlignmentUtils.getSequencesByName(this);
189   }
190
191   @Override
192   public SequenceI getSequenceAt(int i)
193   {
194     synchronized (sequences)
195     {
196       if (i > -1 && i < sequences.size())
197       {
198         return sequences.get(i);
199       }
200     }
201     return null;
202   }
203
204   @Override
205   public SequenceI getSequenceAtAbsoluteIndex(int i)
206   {
207     SequenceI seq = null;
208     if (getHiddenSequences().getSize() > 0)
209     {
210       seq = getHiddenSequences().getHiddenSequence(i);
211       if (seq == null)
212       {
213         // didn't find the sequence in the hidden sequences, get it from the
214         // alignment
215         int index = getHiddenSequences().findIndexWithoutHiddenSeqs(i);
216         seq = getSequenceAt(index);
217       }
218     }
219     else
220     {
221       seq = getSequenceAt(i);
222     }
223     return seq;
224   }
225
226   /**
227    * Adds a sequence to the alignment. Recalculates maxLength and size. Note
228    * this currently does not recalculate whether or not the alignment is
229    * nucleotide, so mixed alignments may have undefined behaviour.
230    * 
231    * @param snew
232    */
233   @Override
234   public void addSequence(SequenceI snew)
235   {
236     if (dataset != null)
237     {
238
239       // maintain dataset integrity
240       SequenceI dsseq = snew.getDatasetSequence();
241       if (dsseq == null)
242       {
243         // derive new sequence
244         SequenceI adding = snew.deriveSequence();
245         snew = adding;
246         dsseq = snew.getDatasetSequence();
247       }
248       if (getDataset().findIndex(dsseq) == -1)
249       {
250         getDataset().addSequence(dsseq);
251       }
252
253     }
254     if (sequences == null)
255     {
256       initAlignment(new SequenceI[] { snew });
257     }
258     else
259     {
260       synchronized (sequences)
261       {
262         sequences.add(snew);
263       }
264     }
265     if (hiddenSequences != null)
266     {
267       hiddenSequences.adjustHeightSequenceAdded();
268     }
269   }
270
271   @Override
272   public SequenceI replaceSequenceAt(int i, SequenceI snew)
273   {
274     synchronized (sequences)
275     {
276       if (sequences.size() > i)
277       {
278         return sequences.set(i, snew);
279
280       }
281       else
282       {
283         sequences.add(snew);
284         hiddenSequences.adjustHeightSequenceAdded();
285       }
286       return null;
287     }
288   }
289
290   /**
291    * DOCUMENT ME!
292    * 
293    * @return DOCUMENT ME!
294    */
295   @Override
296   public List<SequenceGroup> getGroups()
297   {
298     return groups;
299   }
300
301   @Override
302   public void finalize() throws Throwable
303   {
304     if (getDataset() != null)
305     {
306       getDataset().removeAlignmentRef();
307     }
308
309     nullReferences();
310     super.finalize();
311   }
312
313   /**
314    * Defensively nulls out references in case this object is not garbage
315    * collected
316    */
317   void nullReferences()
318   {
319     dataset = null;
320     sequences = null;
321     groups = null;
322     annotations = null;
323     hiddenSequences = null;
324   }
325
326   /**
327    * decrement the alignmentRefs counter by one and null references if it goes
328    * to zero.
329    * 
330    * @throws Throwable
331    */
332   private void removeAlignmentRef() throws Throwable
333   {
334     if (--alignmentRefs == 0)
335     {
336       nullReferences();
337     }
338   }
339
340   @Override
341   public void deleteSequence(SequenceI s)
342   {
343     synchronized (sequences)
344     {
345       deleteSequence(findIndex(s));
346     }
347   }
348
349   @Override
350   public void deleteSequence(int i)
351   {
352     synchronized (sequences)
353     {
354       if (i > -1 && i < getHeight())
355       {
356         sequences.remove(i);
357         hiddenSequences.adjustHeightSequenceDeleted(i);
358       }
359     }
360   }
361
362   @Override
363   public void deleteHiddenSequence(int i)
364   {
365     synchronized (sequences)
366     {
367       if (i > -1 && i < getHeight())
368       {
369         sequences.remove(i);
370       }
371     }
372   }
373
374   /*
375    * (non-Javadoc)
376    * 
377    * @see jalview.datamodel.AlignmentI#findGroup(jalview.datamodel.SequenceI)
378    */
379   @Override
380   public SequenceGroup findGroup(SequenceI seq, int position)
381   {
382     synchronized (groups)
383     {
384       for (SequenceGroup sg : groups)
385       {
386         if (sg.getSequences(null).contains(seq))
387         {
388           if (position >= sg.getStartRes() && position <= sg.getEndRes())
389           {
390             return sg;
391           }
392         }
393       }
394     }
395     return null;
396   }
397
398   /*
399    * (non-Javadoc)
400    * 
401    * @see
402    * jalview.datamodel.AlignmentI#findAllGroups(jalview.datamodel.SequenceI)
403    */
404   @Override
405   public SequenceGroup[] findAllGroups(SequenceI s)
406   {
407     ArrayList<SequenceGroup> temp = new ArrayList<>();
408
409     synchronized (groups)
410     {
411       int gSize = groups.size();
412       for (int i = 0; i < gSize; i++)
413       {
414         SequenceGroup sg = groups.get(i);
415         if (sg == null || sg.getSequences() == null)
416         {
417           this.deleteGroup(sg);
418           gSize--;
419           continue;
420         }
421
422         if (sg.getSequences().contains(s))
423         {
424           temp.add(sg);
425         }
426       }
427     }
428     SequenceGroup[] ret = new SequenceGroup[temp.size()];
429     return temp.toArray(ret);
430   }
431
432   /**    */
433   @Override
434   public void addGroup(SequenceGroup sg)
435   {
436     synchronized (groups)
437     {
438       if (!groups.contains(sg))
439       {
440         if (hiddenSequences.getSize() > 0)
441         {
442           int i, iSize = sg.getSize();
443           for (i = 0; i < iSize; i++)
444           {
445             if (!sequences.contains(sg.getSequenceAt(i)))
446             {
447               sg.deleteSequence(sg.getSequenceAt(i), false);
448               iSize--;
449               i--;
450             }
451           }
452
453           if (sg.getSize() < 1)
454           {
455             return;
456           }
457         }
458         sg.setContext(this, true);
459         groups.add(sg);
460       }
461     }
462   }
463
464   /**
465    * remove any annotation that references gp
466    * 
467    * @param gp
468    *          (if null, removes all group associated annotation)
469    */
470   private void removeAnnotationForGroup(SequenceGroup gp)
471   {
472     if (annotations == null || annotations.length == 0)
473     {
474       return;
475     }
476     // remove annotation very quickly
477     AlignmentAnnotation[] t,
478             todelete = new AlignmentAnnotation[annotations.length],
479             tokeep = new AlignmentAnnotation[annotations.length];
480     int i, p, k;
481     if (gp == null)
482     {
483       for (i = 0, p = 0, k = 0; i < annotations.length; i++)
484       {
485         if (annotations[i].groupRef != null)
486         {
487           todelete[p++] = annotations[i];
488         }
489         else
490         {
491           tokeep[k++] = annotations[i];
492         }
493       }
494     }
495     else
496     {
497       for (i = 0, p = 0, k = 0; i < annotations.length; i++)
498       {
499         if (annotations[i].groupRef == gp)
500         {
501           todelete[p++] = annotations[i];
502         }
503         else
504         {
505           tokeep[k++] = annotations[i];
506         }
507       }
508     }
509     if (p > 0)
510     {
511       // clear out the group associated annotation.
512       for (i = 0; i < p; i++)
513       {
514         unhookAnnotation(todelete[i]);
515         todelete[i] = null;
516       }
517       t = new AlignmentAnnotation[k];
518       for (i = 0; i < k; i++)
519       {
520         t[i] = tokeep[i];
521       }
522       annotations = t;
523     }
524   }
525
526   @Override
527   public void deleteAllGroups()
528   {
529     synchronized (groups)
530     {
531       if (annotations != null)
532       {
533         removeAnnotationForGroup(null);
534       }
535       for (SequenceGroup sg : groups)
536       {
537         sg.setContext(null, false);
538       }
539       groups.clear();
540     }
541   }
542
543   /**    */
544   @Override
545   public void deleteGroup(SequenceGroup g)
546   {
547     synchronized (groups)
548     {
549       if (groups.contains(g))
550       {
551         removeAnnotationForGroup(g);
552         groups.remove(g);
553         g.setContext(null, false);
554       }
555     }
556   }
557
558   /**    */
559   @Override
560   public SequenceI findName(String name)
561   {
562     return findName(name, false);
563   }
564
565   /*
566    * (non-Javadoc)
567    * 
568    * @see jalview.datamodel.AlignmentI#findName(java.lang.String, boolean)
569    */
570   @Override
571   public SequenceI findName(String token, boolean b)
572   {
573     return findName(null, token, b);
574   }
575
576   /*
577    * (non-Javadoc)
578    * 
579    * @see jalview.datamodel.AlignmentI#findName(SequenceI, java.lang.String,
580    * boolean)
581    */
582   @Override
583   public SequenceI findName(SequenceI startAfter, String token, boolean b)
584   {
585
586     int i = 0;
587     SequenceI sq = null;
588     String sqname = null;
589     if (startAfter != null)
590     {
591       // try to find the sequence in the alignment
592       boolean matched = false;
593       while (i < sequences.size())
594       {
595         if (getSequenceAt(i++) == startAfter)
596         {
597           matched = true;
598           break;
599         }
600       }
601       if (!matched)
602       {
603         i = 0;
604       }
605     }
606     while (i < sequences.size())
607     {
608       sq = getSequenceAt(i);
609       sqname = sq.getName();
610       if (sqname.equals(token) // exact match
611               || (b && // allow imperfect matches - case varies
612                       (sqname.equalsIgnoreCase(token))))
613       {
614         return getSequenceAt(i);
615       }
616
617       i++;
618     }
619
620     return null;
621   }
622
623   @Override
624   public SequenceI[] findSequenceMatch(String name)
625   {
626     Vector matches = new Vector();
627     int i = 0;
628
629     while (i < sequences.size())
630     {
631       if (getSequenceAt(i).getName().equals(name))
632       {
633         matches.addElement(getSequenceAt(i));
634       }
635       i++;
636     }
637
638     SequenceI[] result = new SequenceI[matches.size()];
639     for (i = 0; i < result.length; i++)
640     {
641       result[i] = (SequenceI) matches.elementAt(i);
642     }
643
644     return result;
645
646   }
647
648   /*
649    * (non-Javadoc)
650    * 
651    * @see jalview.datamodel.AlignmentI#findIndex(jalview.datamodel.SequenceI)
652    */
653   @Override
654   public int findIndex(SequenceI s)
655   {
656     int i = 0;
657
658     while (i < sequences.size())
659     {
660       if (s == getSequenceAt(i))
661       {
662         return i;
663       }
664
665       i++;
666     }
667
668     return -1;
669   }
670
671   /*
672    * (non-Javadoc)
673    * 
674    * @see
675    * jalview.datamodel.AlignmentI#findIndex(jalview.datamodel.SearchResults)
676    */
677   @Override
678   public int findIndex(SearchResultsI results)
679   {
680     int i = 0;
681
682     while (i < sequences.size())
683     {
684       if (results.involvesSequence(getSequenceAt(i)))
685       {
686         return i;
687       }
688       i++;
689     }
690     return -1;
691   }
692
693   @Override
694   public int getHeight()
695   {
696     return sequences.size();
697   }
698
699   @Override
700   public int getAbsoluteHeight()
701   {
702     return sequences.size() + getHiddenSequences().getSize();
703   }
704
705   @Override
706   public int getWidth()
707   {
708     int maxLength = -1;
709
710     for (int i = 0; i < sequences.size(); i++)
711     {
712       if (getSequenceAt(i).getLength() > maxLength)
713       {
714         maxLength = getSequenceAt(i).getLength();
715       }
716     }
717
718     return maxLength;
719   }
720
721   /**
722    * DOCUMENT ME!
723    * 
724    * @param gc
725    *          DOCUMENT ME!
726    */
727   @Override
728   public void setGapCharacter(char gc)
729   {
730     gapCharacter = gc;
731     synchronized (sequences)
732     {
733       for (SequenceI seq : sequences)
734       {
735         seq.setSequence(seq.getSequenceAsString().replace('.', gc)
736                 .replace('-', gc).replace(' ', gc));
737       }
738     }
739   }
740
741   /**
742    * DOCUMENT ME!
743    * 
744    * @return DOCUMENT ME!
745    */
746   @Override
747   public char getGapCharacter()
748   {
749     return gapCharacter;
750   }
751
752   /*
753    * (non-Javadoc)
754    * 
755    * @see jalview.datamodel.AlignmentI#isAligned()
756    */
757   @Override
758   public boolean isAligned()
759   {
760     return isAligned(false);
761   }
762
763   /*
764    * (non-Javadoc)
765    * 
766    * @see jalview.datamodel.AlignmentI#isAligned(boolean)
767    */
768   @Override
769   public boolean isAligned(boolean includeHidden)
770   {
771     int width = getWidth();
772     if (hiddenSequences == null || hiddenSequences.getSize() == 0)
773     {
774       includeHidden = true; // no hidden sequences to check against.
775     }
776     for (int i = 0; i < sequences.size(); i++)
777     {
778       if (includeHidden || !hiddenSequences.isHidden(getSequenceAt(i)))
779       {
780         if (getSequenceAt(i).getLength() != width)
781         {
782           return false;
783         }
784       }
785     }
786
787     return true;
788   }
789
790   @Override
791   public boolean isHidden(int alignmentIndex)
792   {
793     return (getHiddenSequences().getHiddenSequence(alignmentIndex) != null);
794   }
795
796   /**
797    * Delete all annotations, including auto-calculated if the flag is set true.
798    * Returns true if at least one annotation was deleted, else false.
799    * 
800    * @param includingAutoCalculated
801    * @return
802    */
803   @Override
804   public boolean deleteAllAnnotations(boolean includingAutoCalculated)
805   {
806     boolean result = false;
807     for (AlignmentAnnotation alan : getAlignmentAnnotation())
808     {
809       if (!alan.autoCalculated || includingAutoCalculated)
810       {
811         deleteAnnotation(alan);
812         result = true;
813       }
814     }
815     return result;
816   }
817
818   /*
819    * (non-Javadoc)
820    * 
821    * @seejalview.datamodel.AlignmentI#deleteAnnotation(jalview.datamodel.
822    * AlignmentAnnotation)
823    */
824   @Override
825   public boolean deleteAnnotation(AlignmentAnnotation aa)
826   {
827     return deleteAnnotation(aa, true);
828   }
829
830   @Override
831   public boolean deleteAnnotation(AlignmentAnnotation aa, boolean unhook)
832   {
833     int aSize = 1;
834
835     if (annotations != null)
836     {
837       aSize = annotations.length;
838     }
839
840     if (aSize < 1)
841     {
842       return false;
843     }
844
845     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize - 1];
846
847     boolean swap = false;
848     int tIndex = 0;
849
850     for (int i = 0; i < aSize; i++)
851     {
852       if (annotations[i] == aa)
853       {
854         swap = true;
855         continue;
856       }
857       if (tIndex < temp.length)
858       {
859         temp[tIndex++] = annotations[i];
860       }
861     }
862
863     if (swap)
864     {
865       annotations = temp;
866       if (unhook)
867       {
868         unhookAnnotation(aa);
869       }
870     }
871     return swap;
872   }
873
874   /**
875    * remove any object references associated with this annotation
876    * 
877    * @param aa
878    */
879   private void unhookAnnotation(AlignmentAnnotation aa)
880   {
881     if (aa.sequenceRef != null)
882     {
883       aa.sequenceRef.removeAlignmentAnnotation(aa);
884     }
885     if (aa.groupRef != null)
886     {
887       // probably need to do more here in the future (post 2.5.0)
888       aa.groupRef = null;
889     }
890   }
891
892   /*
893    * (non-Javadoc)
894    * 
895    * @seejalview.datamodel.AlignmentI#addAnnotation(jalview.datamodel.
896    * AlignmentAnnotation)
897    */
898   @Override
899   public void addAnnotation(AlignmentAnnotation aa)
900   {
901     addAnnotation(aa, -1);
902   }
903
904   /*
905    * (non-Javadoc)
906    * 
907    * @seejalview.datamodel.AlignmentI#addAnnotation(jalview.datamodel.
908    * AlignmentAnnotation, int)
909    */
910   @Override
911   public void addAnnotation(AlignmentAnnotation aa, int pos)
912   {
913     if (aa.getRNAStruc() != null)
914     {
915       hasRNAStructure = true;
916     }
917
918     int aSize = 1;
919     if (annotations != null)
920     {
921       aSize = annotations.length + 1;
922     }
923
924     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
925     int i = 0;
926     if (pos == -1 || pos >= aSize)
927     {
928       temp[aSize - 1] = aa;
929     }
930     else
931     {
932       temp[pos] = aa;
933     }
934     if (aSize > 1)
935     {
936       int p = 0;
937       for (i = 0; i < (aSize - 1); i++, p++)
938       {
939         if (p == pos)
940         {
941           p++;
942         }
943         if (p < temp.length)
944         {
945           temp[p] = annotations[i];
946         }
947       }
948     }
949
950     annotations = temp;
951   }
952
953   @Override
954   public void setAnnotationIndex(AlignmentAnnotation aa, int index)
955   {
956     if (aa == null || annotations == null || annotations.length - 1 < index)
957     {
958       return;
959     }
960
961     int aSize = annotations.length;
962     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
963
964     temp[index] = aa;
965
966     for (int i = 0; i < aSize; i++)
967     {
968       if (i == index)
969       {
970         continue;
971       }
972
973       if (i < index)
974       {
975         temp[i] = annotations[i];
976       }
977       else
978       {
979         temp[i] = annotations[i - 1];
980       }
981     }
982
983     annotations = temp;
984   }
985
986   @Override
987   /**
988    * returns all annotation on the alignment
989    */
990   public AlignmentAnnotation[] getAlignmentAnnotation()
991   {
992     return annotations;
993   }
994
995   @Override
996   public boolean isNucleotide()
997   {
998     return nucleotide;
999   }
1000
1001   @Override
1002   public boolean hasRNAStructure()
1003   {
1004     // TODO can it happen that structure is removed from alignment?
1005     return hasRNAStructure;
1006   }
1007
1008   @Override
1009   public void setDataset(AlignmentI data)
1010   {
1011     if (dataset == null && data == null)
1012     {
1013       createDatasetAlignment();
1014     }
1015     else if (dataset == null && data != null)
1016     {
1017       if (data == this)
1018       {
1019         throw new IllegalArgumentException("Circular dataset reference");
1020       }
1021       if (!(data instanceof Alignment))
1022       {
1023         throw new Error(
1024                 "Implementation Error: jalview.datamodel.Alignment does not yet support other implementations of AlignmentI as its dataset reference");
1025       }
1026       dataset = (Alignment) data;
1027       for (int i = 0; i < getHeight(); i++)
1028       {
1029         SequenceI currentSeq = getSequenceAt(i);
1030         SequenceI dsq = currentSeq.getDatasetSequence();
1031         if (dsq == null)
1032         {
1033           dsq = currentSeq.createDatasetSequence();
1034           dataset.addSequence(dsq);
1035         }
1036         else
1037         {
1038           while (dsq.getDatasetSequence() != null)
1039           {
1040             dsq = dsq.getDatasetSequence();
1041           }
1042           if (dataset.findIndex(dsq) == -1)
1043           {
1044             dataset.addSequence(dsq);
1045           }
1046         }
1047       }
1048     }
1049     dataset.addAlignmentRef();
1050   }
1051
1052   /**
1053    * add dataset sequences to seq for currentSeq and any sequences it references
1054    */
1055   private void resolveAndAddDatasetSeq(SequenceI currentSeq,
1056           Set<SequenceI> seqs, boolean createDatasetSequence)
1057   {
1058     SequenceI alignedSeq = currentSeq;
1059     if (currentSeq.getDatasetSequence() != null)
1060     {
1061       currentSeq = currentSeq.getDatasetSequence();
1062     }
1063     else
1064     {
1065       if (createDatasetSequence)
1066       {
1067         currentSeq = currentSeq.createDatasetSequence();
1068       }
1069     }
1070
1071     List<SequenceI> toProcess = new ArrayList<>();
1072     toProcess.add(currentSeq);
1073     while (toProcess.size() > 0)
1074     {
1075       // use a queue ?
1076       SequenceI curDs = toProcess.remove(0);
1077
1078       if (!seqs.add(curDs))
1079       {
1080         continue;
1081       }
1082       // iterate over database references, making sure we add forward referenced
1083       // sequences
1084       if (curDs.getDBRefs() != null)
1085       {
1086         for (DBRefEntry dbr : curDs.getDBRefs())
1087         {
1088           if (dbr.getMap() != null && dbr.getMap().getTo() != null)
1089           {
1090             if (dbr.getMap().getTo() == alignedSeq)
1091             {
1092               /*
1093                * update mapping to be to the newly created dataset sequence
1094                */
1095               dbr.getMap().setTo(currentSeq);
1096             }
1097             if (dbr.getMap().getTo().getDatasetSequence() != null)
1098             {
1099               throw new Error("Implementation error: Map.getTo() for dbref "
1100                       + dbr + " from " + curDs.getName()
1101                       + " is not a dataset sequence.");
1102             }
1103             // we recurse to add all forward references to dataset sequences via
1104             // DBRefs/etc
1105             toProcess.add(dbr.getMap().getTo());
1106           }
1107         }
1108       }
1109     }
1110   }
1111
1112   /**
1113    * Creates a new dataset for this alignment. Can only be done once - if
1114    * dataset is not null this will not be performed.
1115    */
1116   public void createDatasetAlignment()
1117   {
1118     if (dataset != null)
1119     {
1120       return;
1121     }
1122     // try to avoid using SequenceI.equals at this stage, it will be expensive
1123     Set<SequenceI> seqs = new LinkedIdentityHashSet<>();
1124
1125     for (int i = 0; i < getHeight(); i++)
1126     {
1127       SequenceI currentSeq = getSequenceAt(i);
1128       resolveAndAddDatasetSeq(currentSeq, seqs, true);
1129     }
1130
1131     // verify all mappings are in dataset
1132     for (AlignedCodonFrame cf : codonFrameList)
1133     {
1134       for (SequenceToSequenceMapping ssm : cf.getMappings())
1135       {
1136         if (!seqs.contains(ssm.getFromSeq()))
1137         {
1138           resolveAndAddDatasetSeq(ssm.getFromSeq(), seqs, false);
1139         }
1140         if (!seqs.contains(ssm.getMapping().getTo()))
1141         {
1142           resolveAndAddDatasetSeq(ssm.getMapping().getTo(), seqs, false);
1143         }
1144       }
1145     }
1146     // finally construct dataset
1147     dataset = new Alignment(seqs.toArray(new SequenceI[seqs.size()]));
1148     // move mappings to the dataset alignment
1149     dataset.codonFrameList = this.codonFrameList;
1150     this.codonFrameList = null;
1151   }
1152
1153   /**
1154    * reference count for number of alignments referencing this one.
1155    */
1156   int alignmentRefs = 0;
1157
1158   /**
1159    * increase reference count to this alignment.
1160    */
1161   private void addAlignmentRef()
1162   {
1163     alignmentRefs++;
1164   }
1165
1166   @Override
1167   public Alignment getDataset()
1168   {
1169     return dataset;
1170   }
1171
1172   @Override
1173   public boolean padGaps()
1174   {
1175     boolean modified = false;
1176
1177     // Remove excess gaps from the end of alignment
1178     int maxLength = -1;
1179
1180     SequenceI current;
1181     for (int i = 0; i < sequences.size(); i++)
1182     {
1183       current = getSequenceAt(i);
1184       for (int j = current.getLength(); j > maxLength; j--)
1185       {
1186         if (j > maxLength
1187                 && !jalview.util.Comparison.isGap(current.getCharAt(j)))
1188         {
1189           maxLength = j;
1190           break;
1191         }
1192       }
1193     }
1194
1195     maxLength++;
1196
1197     int cLength;
1198     for (int i = 0; i < sequences.size(); i++)
1199     {
1200       current = getSequenceAt(i);
1201       cLength = current.getLength();
1202
1203       if (cLength < maxLength)
1204       {
1205         current.insertCharAt(cLength, maxLength - cLength, gapCharacter);
1206         modified = true;
1207       }
1208       else if (current.getLength() > maxLength)
1209       {
1210         current.deleteChars(maxLength, current.getLength());
1211       }
1212     }
1213     return modified;
1214   }
1215
1216   /**
1217    * Justify the sequences to the left or right by deleting and inserting gaps
1218    * before the initial residue or after the terminal residue
1219    * 
1220    * @param right
1221    *          true if alignment padded to right, false to justify to left
1222    * @return true if alignment was changed
1223    */
1224   @Override
1225   public boolean justify(boolean right)
1226   {
1227     boolean modified = false;
1228
1229     // Remove excess gaps from the end of alignment
1230     int maxLength = -1;
1231     int ends[] = new int[sequences.size() * 2];
1232     SequenceI current;
1233     for (int i = 0; i < sequences.size(); i++)
1234     {
1235       current = getSequenceAt(i);
1236       // This should really be a sequence method
1237       ends[i * 2] = current.findIndex(current.getStart());
1238       ends[i * 2 + 1] = current
1239               .findIndex(current.getStart() + current.getLength());
1240       boolean hitres = false;
1241       for (int j = 0, rs = 0, ssiz = current.getLength(); j < ssiz; j++)
1242       {
1243         if (!jalview.util.Comparison.isGap(current.getCharAt(j)))
1244         {
1245           if (!hitres)
1246           {
1247             ends[i * 2] = j;
1248             hitres = true;
1249           }
1250           else
1251           {
1252             ends[i * 2 + 1] = j;
1253             if (j - ends[i * 2] > maxLength)
1254             {
1255               maxLength = j - ends[i * 2];
1256             }
1257           }
1258         }
1259       }
1260     }
1261
1262     maxLength++;
1263     // now edit the flanking gaps to justify to either left or right
1264     int cLength, extent, diff;
1265     for (int i = 0; i < sequences.size(); i++)
1266     {
1267       current = getSequenceAt(i);
1268
1269       cLength = 1 + ends[i * 2 + 1] - ends[i * 2];
1270       diff = maxLength - cLength; // number of gaps to indent
1271       extent = current.getLength();
1272       if (right)
1273       {
1274         // right justify
1275         if (extent > ends[i * 2 + 1])
1276         {
1277           current.deleteChars(ends[i * 2 + 1] + 1, extent);
1278           modified = true;
1279         }
1280         if (ends[i * 2] > diff)
1281         {
1282           current.deleteChars(0, ends[i * 2] - diff);
1283           modified = true;
1284         }
1285         else
1286         {
1287           if (ends[i * 2] < diff)
1288           {
1289             current.insertCharAt(0, diff - ends[i * 2], gapCharacter);
1290             modified = true;
1291           }
1292         }
1293       }
1294       else
1295       {
1296         // left justify
1297         if (ends[i * 2] > 0)
1298         {
1299           current.deleteChars(0, ends[i * 2]);
1300           modified = true;
1301           ends[i * 2 + 1] -= ends[i * 2];
1302           extent -= ends[i * 2];
1303         }
1304         if (extent > maxLength)
1305         {
1306           current.deleteChars(maxLength + 1, extent);
1307           modified = true;
1308         }
1309         else
1310         {
1311           if (extent < maxLength)
1312           {
1313             current.insertCharAt(extent, maxLength - extent, gapCharacter);
1314             modified = true;
1315           }
1316         }
1317       }
1318     }
1319     return modified;
1320   }
1321
1322   @Override
1323   public HiddenSequences getHiddenSequences()
1324   {
1325     return hiddenSequences;
1326   }
1327
1328   @Override
1329   public HiddenColumns getHiddenColumns()
1330   {
1331     return hiddenCols;
1332   }
1333
1334   @Override
1335   public CigarArray getCompactAlignment()
1336   {
1337     synchronized (sequences)
1338     {
1339       SeqCigar alseqs[] = new SeqCigar[sequences.size()];
1340       int i = 0;
1341       for (SequenceI seq : sequences)
1342       {
1343         alseqs[i++] = new SeqCigar(seq);
1344       }
1345       CigarArray cal = new CigarArray(alseqs);
1346       cal.addOperation(CigarArray.M, getWidth());
1347       return cal;
1348     }
1349   }
1350
1351   @Override
1352   public void setProperty(Object key, Object value)
1353   {
1354     if (alignmentProperties == null)
1355     {
1356       alignmentProperties = new Hashtable();
1357     }
1358
1359     alignmentProperties.put(key, value);
1360   }
1361
1362   @Override
1363   public Object getProperty(Object key)
1364   {
1365     if (alignmentProperties != null)
1366     {
1367       return alignmentProperties.get(key);
1368     }
1369     else
1370     {
1371       return null;
1372     }
1373   }
1374
1375   @Override
1376   public Hashtable getProperties()
1377   {
1378     return alignmentProperties;
1379   }
1380
1381   /**
1382    * Adds the given mapping to the stored set. Note this may be held on the
1383    * dataset alignment.
1384    */
1385   @Override
1386   public void addCodonFrame(AlignedCodonFrame codons)
1387   {
1388     List<AlignedCodonFrame> acfs = getCodonFrames();
1389     if (codons != null && acfs != null && !acfs.contains(codons))
1390     {
1391       acfs.add(codons);
1392     }
1393   }
1394
1395   /*
1396    * (non-Javadoc)
1397    * 
1398    * @see
1399    * jalview.datamodel.AlignmentI#getCodonFrame(jalview.datamodel.SequenceI)
1400    */
1401   @Override
1402   public List<AlignedCodonFrame> getCodonFrame(SequenceI seq)
1403   {
1404     if (seq == null)
1405     {
1406       return null;
1407     }
1408     List<AlignedCodonFrame> cframes = new ArrayList<>();
1409     for (AlignedCodonFrame acf : getCodonFrames())
1410     {
1411       if (acf.involvesSequence(seq))
1412       {
1413         cframes.add(acf);
1414       }
1415     }
1416     return cframes;
1417   }
1418
1419   /**
1420    * Sets the codon frame mappings (replacing any existing mappings). Note the
1421    * mappings are set on the dataset alignment instead if there is one.
1422    * 
1423    * @see jalview.datamodel.AlignmentI#setCodonFrames()
1424    */
1425   @Override
1426   public void setCodonFrames(List<AlignedCodonFrame> acfs)
1427   {
1428     if (dataset != null)
1429     {
1430       dataset.setCodonFrames(acfs);
1431     }
1432     else
1433     {
1434       this.codonFrameList = acfs;
1435     }
1436   }
1437
1438   /**
1439    * Returns the set of codon frame mappings. Any changes to the returned set
1440    * will affect the alignment. The mappings are held on (and read from) the
1441    * dataset alignment if there is one.
1442    * 
1443    * @see jalview.datamodel.AlignmentI#getCodonFrames()
1444    */
1445   @Override
1446   public List<AlignedCodonFrame> getCodonFrames()
1447   {
1448     // TODO: Fix this method to fix failing AlignedCodonFrame tests
1449     // this behaviour is currently incorrect. method should return codon frames
1450     // for just the alignment,
1451     // selected from dataset
1452     return dataset != null ? dataset.getCodonFrames() : codonFrameList;
1453   }
1454
1455   /**
1456    * Removes the given mapping from the stored set. Note that the mappings are
1457    * held on the dataset alignment if there is one.
1458    */
1459   @Override
1460   public boolean removeCodonFrame(AlignedCodonFrame codons)
1461   {
1462     List<AlignedCodonFrame> acfs = getCodonFrames();
1463     if (codons == null || acfs == null)
1464     {
1465       return false;
1466     }
1467     return acfs.remove(codons);
1468   }
1469
1470   @Override
1471   public void append(AlignmentI toappend)
1472   {
1473     // TODO JAL-1270 needs test coverage
1474     // currently tested for use in jalview.gui.SequenceFetcher
1475     char oldc = toappend.getGapCharacter();
1476     boolean samegap = oldc == getGapCharacter();
1477     boolean hashidden = toappend.getHiddenSequences() != null
1478             && toappend.getHiddenSequences().hiddenSequences != null;
1479     // get all sequences including any hidden ones
1480     List<SequenceI> sqs = (hashidden)
1481             ? toappend.getHiddenSequences().getFullAlignment()
1482                     .getSequences()
1483             : toappend.getSequences();
1484     if (sqs != null)
1485     {
1486       // avoid self append deadlock by
1487       List<SequenceI> toappendsq = new ArrayList<>();
1488       synchronized (sqs)
1489       {
1490         for (SequenceI addedsq : sqs)
1491         {
1492           if (!samegap)
1493           {
1494             addedsq.replace(oldc, gapCharacter);
1495           }
1496           toappendsq.add(addedsq);
1497         }
1498       }
1499       for (SequenceI addedsq : toappendsq)
1500       {
1501         addSequence(addedsq);
1502       }
1503     }
1504     AlignmentAnnotation[] alan = toappend.getAlignmentAnnotation();
1505     for (int a = 0; alan != null && a < alan.length; a++)
1506     {
1507       addAnnotation(alan[a]);
1508     }
1509
1510     // use add method
1511     getCodonFrames().addAll(toappend.getCodonFrames());
1512
1513     List<SequenceGroup> sg = toappend.getGroups();
1514     if (sg != null)
1515     {
1516       for (SequenceGroup _sg : sg)
1517       {
1518         addGroup(_sg);
1519       }
1520     }
1521     if (toappend.getHiddenSequences() != null)
1522     {
1523       HiddenSequences hs = toappend.getHiddenSequences();
1524       if (hiddenSequences == null)
1525       {
1526         hiddenSequences = new HiddenSequences(this);
1527       }
1528       if (hs.hiddenSequences != null)
1529       {
1530         for (int s = 0; s < hs.hiddenSequences.length; s++)
1531         {
1532           // hide the newly appended sequence in the alignment
1533           if (hs.hiddenSequences[s] != null)
1534           {
1535             hiddenSequences.hideSequence(hs.hiddenSequences[s]);
1536           }
1537         }
1538       }
1539     }
1540     if (toappend.getProperties() != null)
1541     {
1542       // we really can't do very much here - just try to concatenate strings
1543       // where property collisions occur.
1544       Enumeration key = toappend.getProperties().keys();
1545       while (key.hasMoreElements())
1546       {
1547         Object k = key.nextElement();
1548         Object ourval = this.getProperty(k);
1549         Object toapprop = toappend.getProperty(k);
1550         if (ourval != null)
1551         {
1552           if (ourval.getClass().equals(toapprop.getClass())
1553                   && !ourval.equals(toapprop))
1554           {
1555             if (ourval instanceof String)
1556             {
1557               // append strings
1558               this.setProperty(k,
1559                       ((String) ourval) + "; " + ((String) toapprop));
1560             }
1561             else
1562             {
1563               if (ourval instanceof Vector)
1564               {
1565                 // append vectors
1566                 Enumeration theirv = ((Vector) toapprop).elements();
1567                 while (theirv.hasMoreElements())
1568                 {
1569                   ((Vector) ourval).addElement(theirv);
1570                 }
1571               }
1572             }
1573           }
1574         }
1575         else
1576         {
1577           // just add new property directly
1578           setProperty(k, toapprop);
1579         }
1580
1581       }
1582     }
1583   }
1584
1585   @Override
1586   public AlignmentAnnotation findOrCreateAnnotation(String name,
1587           String calcId, boolean autoCalc, SequenceI seqRef,
1588           SequenceGroup groupRef)
1589   {
1590     if (annotations != null)
1591     {
1592       for (AlignmentAnnotation annot : getAlignmentAnnotation())
1593       {
1594         if (annot.autoCalculated == autoCalc && (name.equals(annot.label))
1595                 && (calcId == null || annot.getCalcId().equals(calcId))
1596                 && annot.sequenceRef == seqRef
1597                 && annot.groupRef == groupRef)
1598         {
1599           return annot;
1600         }
1601       }
1602     }
1603     AlignmentAnnotation annot = new AlignmentAnnotation(name, name,
1604             new Annotation[1], 0f, 0f, AlignmentAnnotation.BAR_GRAPH);
1605     annot.hasText = false;
1606     annot.setCalcId(new String(calcId));
1607     annot.autoCalculated = autoCalc;
1608     if (seqRef != null)
1609     {
1610       annot.setSequenceRef(seqRef);
1611     }
1612     annot.groupRef = groupRef;
1613     addAnnotation(annot);
1614
1615     return annot;
1616   }
1617
1618   @Override
1619   public Iterable<AlignmentAnnotation> findAnnotation(String calcId)
1620   {
1621     AlignmentAnnotation[] alignmentAnnotation = getAlignmentAnnotation();
1622     if (alignmentAnnotation != null)
1623     {
1624       return AlignmentAnnotation.findAnnotation(
1625               Arrays.asList(getAlignmentAnnotation()), calcId);
1626     }
1627     return Arrays.asList(new AlignmentAnnotation[] {});
1628   }
1629
1630   @Override
1631   public Iterable<AlignmentAnnotation> findAnnotations(SequenceI seq,
1632           String calcId, String label)
1633   {
1634     return AlignmentAnnotation.findAnnotations(
1635             Arrays.asList(getAlignmentAnnotation()), seq, calcId, label);
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(), true);
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<>();
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 void setHiddenColumns(HiddenColumns cols)
1895   {
1896     hiddenCols = cols;
1897   }
1898 }