Clover icon

Coverage Report

  1. Project Clover database Mon Sep 2 2024 17:57:51 BST
  2. Package jalview.datamodel

File Alignment.java

 

Coverage histogram

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

Code metrics

354
622
98
1
2,125
1,552
318
0.51
6.35
98
3.24

Classes

Class Line # Actions
Alignment 51 622 318
0.644320364.4%
 

Contributing tests

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