Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  2. Package jalview.datamodel

File Alignment.java

 

Coverage histogram

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

Code metrics

332
592
93
1
2,035
1,474
293
0.49
6.37
93
3.15

Classes

Class Line # Actions
Alignment 50 592 293
0.636184963.6%
 

Contributing tests

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