Clover icon

Coverage Report

  1. Project Clover database Thu Dec 4 2025 16:11:35 GMT
  2. Package jalview.datamodel

File Alignment.java

 

Coverage histogram

../../img/srcFileCovDistChart7.png
30% of files have more coverage

Code metrics

370
649
102
1
2,204
1,617
328
0.51
6.36
102
3.22

Classes

Class Line # Actions
Alignment 51 649 328
0.682426468.2%
 

Contributing tests

This file is covered by 823 tests. .

Source view

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