Clover icon

Coverage Report

  1. Project Clover database Mon Nov 18 2024 09:38:20 GMT
  2. Package jalview.datamodel

File Alignment.java

 

Coverage histogram

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

Code metrics

356
624
98
1
2,132
1,556
320
0.51
6.37
98
3.27

Classes

Class Line # Actions
Alignment 51 624 320
0.6048237760.5%
 

Contributing tests

This file is covered by 354 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  1028 toggle private void initAlignment(SequenceI[] seqs)
76    {
77  1028 groups = Collections.synchronizedList(new ArrayList<SequenceGroup>());
78  1028 hiddenSequences = new HiddenSequences(this);
79  1028 hiddenCols = new HiddenColumns();
80  1028 codonFrameList = new ArrayList<>();
81   
82  1028 nucleotide = Comparison.isNucleotide(seqs);
83   
84  1028 sequences = Collections.synchronizedList(new ArrayList<SequenceI>());
85   
86  23377 for (int i = 0; i < seqs.length; i++)
87    {
88  22349 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  8 toggle public Alignment(AlignmentI al)
98    {
99  8 SequenceI[] seqs = al.getSequencesArray();
100  31 for (int i = 0; i < seqs.length; i++)
101    {
102  23 seqs[i] = new Sequence(seqs[i]);
103    }
104   
105  8 initAlignment(seqs);
106   
107    /*
108    * Share the same dataset sequence mappings (if any).
109    */
110  8 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  1018 toggle public Alignment(SequenceI[] seqs)
122    {
123  1018 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  57388 toggle @Override
156    public List<SequenceI> getSequences()
157    {
158  57388 return sequences;
159    }
160   
 
161  11 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  11 return sequences;
168    }
169   
 
170  26643 toggle @Override
171    public SequenceI[] getSequencesArray()
172    {
173  26643 if (sequences == null)
174    {
175  0 return null;
176    }
177  26643 synchronized (sequences)
178    {
179  26643 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  50729 toggle @Override
195    public SequenceI getSequenceAt(int i)
196    {
197  50729 synchronized (sequences)
198    {
199   
200  50729 if (i > -1 && i < sequences.size())
201    {
202  50727 return sequences.get(i);
203    }
204    }
205   
206  2 return null;
207    }
208   
 
209  182 toggle @Override
210    public SequenceI getSequenceAtAbsoluteIndex(int i)
211    {
212  182 SequenceI seq = null;
213  182 if (getHiddenSequences().getSize() > 0)
214    {
215  0 seq = getHiddenSequences().getHiddenSequence(i);
216  0 if (seq == null)
217    {
218    // didn't find the sequence in the hidden sequences, get it from the
219    // alignment
220  0 int index = getHiddenSequences().findIndexWithoutHiddenSeqs(i);
221  0 seq = getSequenceAt(index);
222    }
223    }
224    else
225    {
226  182 seq = getSequenceAt(i);
227    }
228  182 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  89 toggle @Override
239    public void addSequence(SequenceI snew)
240    {
241  89 if (dataset != null)
242    {
243   
244    // maintain dataset integrity
245  26 SequenceI dsseq = snew.getDatasetSequence();
246  26 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  26 if (getDataset().findIndex(dsseq) == -1)
254    {
255  23 getDataset().addSequence(dsseq);
256    }
257   
258    }
259  89 if (sequences == null)
260    {
261  0 initAlignment(new SequenceI[] { snew });
262    }
263    else
264    {
265  89 synchronized (sequences)
266    {
267  89 sequences.add(snew);
268    }
269    }
270  89 if (hiddenSequences != null)
271    {
272  89 hiddenSequences.adjustHeightSequenceAdded();
273    }
274    }
275   
 
276  0 toggle @Override
277    public SequenceI replaceSequenceAt(int i, SequenceI snew)
278    {
279  0 synchronized (sequences)
280    {
281  0 if (sequences.size() > i)
282    {
283  0 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  1281 toggle @Override
301    public List<SequenceGroup> getGroups()
302    {
303  1281 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  31 toggle @Override
351    public void deleteSequence(SequenceI s)
352    {
353  31 synchronized (sequences)
354    {
355  31 deleteSequence(findIndex(s));
356    }
357    }
358   
 
359  36 toggle @Override
360    public void deleteSequence(int i)
361    {
362  36 synchronized (sequences)
363    {
364  36 if (i > -1 && i < getHeight())
365    {
366  34 sequences.remove(i);
367  34 hiddenSequences.adjustHeightSequenceDeleted(i);
368    }
369    }
370    }
371   
 
372  45 toggle @Override
373    public void deleteHiddenSequence(int i)
374    {
375  45 synchronized (sequences)
376    {
377  45 if (i > -1 && i < getHeight())
378    {
379  45 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  4660 toggle @Override
415    public SequenceGroup[] findAllGroups(SequenceI s)
416    {
417  4660 ArrayList<SequenceGroup> temp = new ArrayList<>();
418   
419  4660 synchronized (groups)
420    {
421  4660 int gSize = groups.size();
422  4670 for (int i = 0; i < gSize; i++)
423    {
424  10 SequenceGroup sg = groups.get(i);
425  10 if (sg == null || sg.getSequences() == null)
426    {
427  0 this.deleteGroup(sg);
428  0 gSize--;
429  0 continue;
430    }
431   
432  10 if (sg.getSequences().contains(s))
433    {
434  3 temp.add(sg);
435    }
436    }
437    }
438  4660 SequenceGroup[] ret = new SequenceGroup[temp.size()];
439  4660 return temp.toArray(ret);
440    }
441   
442    /** */
 
443  36 toggle @Override
444    public void addGroup(SequenceGroup sg)
445    {
446  36 synchronized (groups)
447    {
448  36 if (!groups.contains(sg))
449    {
450  30 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  30 sg.setContext(this, true);
469  30 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  45 toggle @Override
570    public SequenceI findName(String name)
571    {
572  45 return findName(name, false);
573    }
574   
575    /*
576    * (non-Javadoc)
577    *
578    * @see jalview.datamodel.AlignmentI#findName(java.lang.String, boolean)
579    */
 
580  45 toggle @Override
581    public SequenceI findName(String token, boolean b)
582    {
583  45 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  48 toggle @Override
593    public SequenceI findName(SequenceI startAfter, String token, boolean b)
594    {
595   
596  48 int i = 0;
597  48 SequenceI sq = null;
598  48 String sqname = null;
599  48 int nseq = sequences.size();
600  48 if (startAfter != null)
601    {
602    // try to find the sequence in the alignment
603  3 boolean matched = false;
604  3 while (i < nseq)
605    {
606  3 if (getSequenceAt(i++) == startAfter)
607    {
608  3 matched = true;
609  3 break;
610    }
611    }
612  3 if (!matched)
613    {
614  0 i = 0;
615    }
616    }
617  620 while (i < nseq)
618    {
619  617 sq = getSequenceAt(i);
620  617 sqname = sq.getName();
621  617 if (sqname.equals(token) // exact match
622    || (b && // allow imperfect matches - case varies
623    (sqname.equalsIgnoreCase(token))))
624    {
625  45 return getSequenceAt(i);
626    }
627   
628  572 i++;
629    }
630   
631  3 return null;
632    }
633   
 
634  11 toggle @Override
635    public SequenceI[] findSequenceMatch(String name)
636    {
637  11 Vector matches = new Vector();
638  11 int i = 0;
639   
640  64 while (i < sequences.size())
641    {
642  53 if (getSequenceAt(i).getName().equals(name))
643    {
644  5 matches.addElement(getSequenceAt(i));
645    }
646  53 i++;
647    }
648   
649  11 SequenceI[] result = new SequenceI[matches.size()];
650  16 for (i = 0; i < result.length; i++)
651    {
652  5 result[i] = (SequenceI) matches.elementAt(i);
653    }
654   
655  11 return result;
656   
657    }
658   
659    /*
660    * (non-Javadoc)
661    *
662    * @see jalview.datamodel.AlignmentI#findIndex(jalview.datamodel.SequenceI)
663    */
 
664  561 toggle @Override
665    public int findIndex(SequenceI s)
666    {
667  561 int i = 0;
668   
669  2922 while (i < sequences.size())
670    {
671  2882 if (s == getSequenceAt(i))
672    {
673  521 return i;
674    }
675   
676  2361 i++;
677    }
678   
679  40 return -1;
680    }
681   
682    /*
683    * (non-Javadoc)
684    *
685    * @see
686    * jalview.datamodel.AlignmentI#findIndex(jalview.datamodel.SearchResults)
687    */
 
688  4 toggle @Override
689    public int findIndex(SearchResultsI results)
690    {
691  4 int i = 0;
692   
693  11 while (i < sequences.size())
694    {
695  11 if (results.involvesSequence(getSequenceAt(i)))
696    {
697  4 return i;
698    }
699  7 i++;
700    }
701  0 return -1;
702    }
703   
 
704  12416 toggle @Override
705    public int getHeight()
706    {
707  12416 return sequences.size();
708    }
709   
 
710  10 toggle @Override
711    public int getAbsoluteHeight()
712    {
713  10 return sequences.size() + getHiddenSequences().getSize();
714    }
715   
 
716  5097 toggle @Override
717    public int getWidth()
718    {
719  5097 int maxLength = -1;
720   
721  32755 for (int i = 0; i < sequences.size(); i++)
722    {
723  27659 maxLength = Math.max(maxLength, getSequenceAt(i).getLength());
724    }
725  5097 return maxLength;
726    }
727   
 
728  1832 toggle @Override
729    public int getVisibleWidth()
730    {
731  1832 int w = getWidth();
732  1832 if (hiddenCols != null)
733    {
734  1832 w -= hiddenCols.getSize();
735    }
736  1832 return w;
737    }
738   
739    /**
740    * DOCUMENT ME!
741    *
742    * @param gc
743    * DOCUMENT ME!
744    */
 
745  301 toggle @Override
746    public void setGapCharacter(char gc)
747    {
748  301 gapCharacter = gc;
749  301 synchronized (sequences)
750    {
751  301 for (SequenceI seq : sequences)
752    {
753  1597 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  319 toggle @Override
765    public char getGapCharacter()
766    {
767  319 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  29 toggle @Override
849    public boolean deleteAnnotation(AlignmentAnnotation aa, boolean unhook)
850    {
851  29 int aSize = 1;
852   
853  29 if (annotations != null)
854    {
855  29 aSize = annotations.length;
856    }
857   
858  29 if (aSize < 1)
859    {
860  0 return false;
861    }
862   
863  29 AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize - 1];
864   
865  29 boolean swap = false;
866  29 int tIndex = 0;
867   
868  125 for (int i = 0; i < aSize; i++)
869    {
870  96 if (annotations[i] == aa)
871    {
872  29 swap = true;
873  29 continue;
874    }
875  67 if (tIndex < temp.length)
876    {
877  67 temp[tIndex++] = annotations[i];
878    }
879    }
880   
881  29 if (swap)
882    {
883  29 annotations = temp;
884  29 if (unhook)
885    {
886  7 unhookAnnotation(aa);
887    }
888    }
889  29 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  1521 toggle @Override
917    public void addAnnotation(AlignmentAnnotation aa)
918    {
919  1521 addAnnotation(aa, -1);
920    }
921   
922    /*
923    * (non-Javadoc)
924    *
925    * @seejalview.datamodel.AlignmentI#addAnnotation(jalview.datamodel.
926    * AlignmentAnnotation, int)
927    */
 
928  1543 toggle @Override
929    public void addAnnotation(AlignmentAnnotation aa, int pos)
930    {
931  1543 if (aa.getRNAStruc() != null)
932    {
933  242 hasRNAStructure = true;
934    }
935   
936  1543 int aSize = 1;
937  1543 if (annotations != null)
938    {
939  1201 aSize = annotations.length + 1;
940    }
941   
942  1543 AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
943  1543 int i = 0;
944  1543 if (pos == -1 || pos >= aSize)
945    {
946  1521 temp[aSize - 1] = aa;
947    }
948    else
949    {
950  22 temp[pos] = aa;
951    }
952  1543 if (aSize > 1)
953    {
954  1196 int p = 0;
955  8862 for (i = 0; i < (aSize - 1); i++, p++)
956    {
957  7666 if (p == pos)
958    {
959  0 p++;
960    }
961  7666 if (p < temp.length)
962    {
963  7666 temp[p] = annotations[i];
964    }
965    }
966    }
967   
968  1543 annotations = temp;
969    }
970   
 
971  8 toggle @Override
972    public void setAnnotationIndex(AlignmentAnnotation aa, int index)
973    {
974  8 if (aa == null || annotations == null || annotations.length - 1 < index)
975    {
976  0 return;
977    }
978   
979  8 int aSize = annotations.length;
980  8 AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
981   
982  8 temp[index] = aa;
983   
984  16 for (int i = 0; i < aSize; i++)
985    {
986  8 if (i == index)
987    {
988  8 continue;
989    }
990   
991  0 if (i < index)
992    {
993  0 temp[i] = annotations[i];
994    }
995    else
996    {
997  0 temp[i] = annotations[i - 1];
998    }
999    }
1000   
1001  8 annotations = temp;
1002    }
1003   
 
1004  9872 toggle @Override
1005    /**
1006    * returns all annotation on the alignment
1007    */
1008    public AlignmentAnnotation[] getAlignmentAnnotation()
1009    {
1010  9872 return annotations;
1011    }
1012   
 
1013  7400 toggle @Override
1014    public boolean isNucleotide()
1015    {
1016  7400 return nucleotide;
1017    }
1018   
 
1019  468 toggle @Override
1020    public boolean hasRNAStructure()
1021    {
1022    // TODO can it happen that structure is removed from alignment?
1023  468 return hasRNAStructure;
1024    }
1025   
 
1026  411 toggle @Override
1027    public void setDataset(AlignmentI data)
1028    {
1029  411 if (dataset == null && data == null)
1030    {
1031  378 createDatasetAlignment();
1032    }
1033  33 else if (dataset == null && data != null)
1034    {
1035  14 if (data == this)
1036    {
1037  1 throw new IllegalArgumentException("Circular dataset reference");
1038    }
1039  13 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  13 dataset = (Alignment) data;
1045  104 for (int i = 0; i < getHeight(); i++)
1046    {
1047  91 SequenceI currentSeq = getSequenceAt(i);
1048  91 SequenceI dsq = currentSeq.getDatasetSequence();
1049  91 if (dsq == null)
1050    {
1051  0 dsq = currentSeq.createDatasetSequence();
1052  0 dataset.addSequence(dsq);
1053    }
1054    else
1055    {
1056  91 while (dsq.getDatasetSequence() != null)
1057    {
1058  0 dsq = dsq.getDatasetSequence();
1059    }
1060  91 if (dataset.findIndex(dsq) == -1)
1061    {
1062  0 dataset.addSequence(dsq);
1063    }
1064    }
1065    }
1066    }
1067  410 dataset.addAlignmentRef();
1068    }
1069   
1070    /**
1071    * add dataset sequences to seq for currentSeq and any sequences it references
1072    */
 
1073  1823 toggle private void resolveAndAddDatasetSeq(SequenceI currentSeq,
1074    Set<SequenceI> seqs, boolean createDatasetSequence)
1075    {
1076  1823 SequenceI alignedSeq = currentSeq;
1077  1823 if (currentSeq.getDatasetSequence() != null)
1078    {
1079  436 currentSeq = currentSeq.getDatasetSequence();
1080    }
1081    else
1082    {
1083  1387 if (createDatasetSequence)
1084    {
1085  1387 currentSeq = currentSeq.createDatasetSequence();
1086    }
1087    }
1088   
1089  1823 List<SequenceI> toProcess = new ArrayList<>();
1090  1823 toProcess.add(currentSeq);
1091  3652 while (toProcess.size() > 0)
1092    {
1093    // use a queue ?
1094  1829 SequenceI curDs = toProcess.remove(0);
1095   
1096  1829 if (!seqs.add(curDs))
1097    {
1098  109 continue;
1099    }
1100    // iterate over database references, making sure we add forward referenced
1101    // sequences
1102  1720 if (curDs.getDBRefs() != null)
1103    {
1104  139 for (DBRefEntry dbr : curDs.getDBRefs())
1105    {
1106  242 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  394 toggle public void createDatasetAlignment()
1135    {
1136  394 if (dataset != null)
1137    {
1138  1 return;
1139    }
1140    // try to avoid using SequenceI.equals at this stage, it will be expensive
1141  393 Set<SequenceI> seqs = new LinkedIdentityHashSet<>();
1142   
1143  2216 for (int i = 0; i < getHeight(); i++)
1144    {
1145  1823 SequenceI currentSeq = getSequenceAt(i);
1146  1823 resolveAndAddDatasetSeq(currentSeq, seqs, true);
1147    }
1148   
1149    // verify all mappings are in dataset
1150  393 for (AlignedCodonFrame cf : codonFrameList)
1151    {
1152  2 for (SequenceToSequenceMapping ssm : cf.getMappings())
1153    {
1154  0 if (!seqs.contains(ssm.getFromSeq()))
1155    {
1156  0 resolveAndAddDatasetSeq(ssm.getFromSeq(), seqs, false);
1157    }
1158  0 if (!seqs.contains(ssm.getMapping().getTo()))
1159    {
1160  0 resolveAndAddDatasetSeq(ssm.getMapping().getTo(), seqs, false);
1161    }
1162    }
1163    }
1164    // finally construct dataset
1165  393 dataset = new Alignment(seqs.toArray(new SequenceI[seqs.size()]));
1166    // move mappings to the dataset alignment
1167  393 dataset.codonFrameList = this.codonFrameList;
1168  393 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  410 toggle private void addAlignmentRef()
1180    {
1181  410 alignmentRefs++;
1182    }
1183   
 
1184  1158 toggle @Override
1185    public Alignment getDataset()
1186    {
1187  1158 return dataset;
1188    }
1189   
 
1190  262 toggle @Override
1191    public boolean padGaps()
1192    {
1193  262 boolean modified = false;
1194   
1195    // Remove excess gaps from the end of alignment
1196  262 int maxLength = -1;
1197   
1198  262 SequenceI current;
1199  262 int nseq = sequences.size();
1200  962 for (int i = 0; i < nseq; i++)
1201    {
1202  700 current = getSequenceAt(i);
1203  1588 for (int j = current.getLength(); j > maxLength; j--)
1204    {
1205  1180 if (j > maxLength
1206    && !jalview.util.Comparison.isGap(current.getCharAt(j)))
1207    {
1208  292 maxLength = j;
1209  292 break;
1210    }
1211    }
1212    }
1213   
1214  262 maxLength++;
1215   
1216  262 int cLength;
1217  962 for (int i = 0; i < nseq; i++)
1218    {
1219  700 current = getSequenceAt(i);
1220  700 cLength = current.getLength();
1221   
1222  700 if (cLength < maxLength)
1223    {
1224  31 current.insertCharAt(cLength, maxLength - cLength, gapCharacter);
1225  31 modified = true;
1226    }
1227  669 else if (current.getLength() > maxLength)
1228    {
1229  2 current.deleteChars(maxLength, current.getLength());
1230    }
1231    }
1232  262 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  1362 toggle @Override
1342    public HiddenSequences getHiddenSequences()
1343    {
1344  1362 return hiddenSequences;
1345    }
1346   
 
1347  7618 toggle @Override
1348    public HiddenColumns getHiddenColumns()
1349    {
1350  7618 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  101 toggle @Override
1371    public void setProperty(Object key, Object value)
1372    {
1373  101 if (alignmentProperties == null)
1374    {
1375  13 alignmentProperties = new Hashtable();
1376    }
1377   
1378  101 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  86 toggle @Override
1395    public Hashtable getProperties()
1396    {
1397  86 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  68 toggle @Override
1405    public void addCodonFrame(AlignedCodonFrame codons)
1406    {
1407  68 List<AlignedCodonFrame> acfs = getCodonFrames();
1408  68 if (codons != null && acfs != null && !acfs.contains(codons))
1409    {
1410  66 acfs.add(codons);
1411    }
1412    }
1413   
1414    /*
1415    * (non-Javadoc)
1416    *
1417    * @see
1418    * jalview.datamodel.AlignmentI#getCodonFrame(jalview.datamodel.SequenceI)
1419    */
 
1420  37 toggle @Override
1421    public List<AlignedCodonFrame> getCodonFrame(SequenceI seq)
1422    {
1423  37 if (seq == null)
1424    {
1425  0 return null;
1426    }
1427  37 List<AlignedCodonFrame> cframes = new ArrayList<>();
1428  37 for (AlignedCodonFrame acf : getCodonFrames())
1429    {
1430  89 if (acf.involvesSequence(seq))
1431    {
1432  30 cframes.add(acf);
1433    }
1434    }
1435  37 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  42 toggle @Override
1445    public void setCodonFrames(List<AlignedCodonFrame> acfs)
1446    {
1447  42 if (dataset != null)
1448    {
1449  19 dataset.setCodonFrames(acfs);
1450    }
1451    else
1452    {
1453  23 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  2567 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  2567 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  1 toggle @Override
1605    public AlignmentAnnotation findOrCreateAnnotation(String name,
1606    String calcId, boolean autoCalc, SequenceI seqRef,
1607    SequenceGroup groupRef)
1608    {
1609  1 if (annotations != null)
1610    {
1611  0 for (AlignmentAnnotation annot : getAlignmentAnnotation())
1612    {
1613  0 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  1 AlignmentAnnotation annot = new AlignmentAnnotation(name, name,
1623    new Annotation[1], 0f, 0f, AlignmentAnnotation.BAR_GRAPH);
1624  1 annot.hasText = false;
1625  1 if (calcId != null)
1626    {
1627  0 annot.setCalcId(new String(calcId));
1628    }
1629  1 annot.autoCalculated = autoCalc;
1630  1 if (seqRef != null)
1631    {
1632  1 annot.setSequenceRef(seqRef);
1633    }
1634  1 annot.groupRef = groupRef;
1635  1 addAnnotation(annot);
1636   
1637  1 return annot;
1638    }
1639   
 
1640  225 toggle @Override
1641    public Iterable<AlignmentAnnotation> findAnnotation(String calcId)
1642    {
1643  225 AlignmentAnnotation[] alignmentAnnotation = getAlignmentAnnotation();
1644  225 if (alignmentAnnotation != null)
1645    {
1646  221 return AlignmentAnnotation.findAnnotation(
1647    Arrays.asList(getAlignmentAnnotation()), calcId);
1648    }
1649  4 return Arrays.asList(new AlignmentAnnotation[] {});
1650    }
1651   
 
1652  37 toggle @Override
1653    public Iterable<AlignmentAnnotation> findAnnotations(SequenceI seq,
1654    String calcId, String label)
1655    {
1656  37 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  2202 toggle @Override
1730    public SequenceI getSeqrep()
1731    {
1732  2202 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  3 toggle @Override
1743    public void setSeqrep(SequenceI seqrep)
1744    {
1745  3 this.seqrep = seqrep;
1746    }
1747   
1748    /**
1749    *
1750    * @return true if group has a sequence representative
1751    */
 
1752  3375 toggle @Override
1753    public boolean hasSeqrep()
1754    {
1755  3375 return seqrep != null;
1756    }
1757   
 
1758  21 toggle @Override
1759    public int getEndRes()
1760    {
1761  21 return getWidth() - 1;
1762    }
1763   
 
1764  21 toggle @Override
1765    public int getStartRes()
1766    {
1767  21 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  204 toggle @Override
1777    public AnnotatedCollectionI getContext()
1778    {
1779  204 return dataset;
1780    }
1781   
1782    /**
1783    * Align this alignment like the given (mapped) one.
1784    */
 
1785  2 toggle @Override
1786    public int alignAs(AlignmentI al)
1787    {
1788    /*
1789    * Currently retains unmapped gaps (in introns), regaps mapped regions
1790    * (exons)
1791    */
1792  2 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  8 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  8 boolean thisIsNucleotide = this.isNucleotide();
1824  8 boolean thatIsProtein = !al.isNucleotide();
1825  8 if (!thatIsProtein && !thisIsNucleotide)
1826    {
1827  1 return AlignmentUtils.alignProteinAsDna(this, al);
1828    }
1829  7 else if (thatIsProtein && thisIsNucleotide)
1830    {
1831  4 return AlignmentUtils.alignCdsAsProtein(this, al);
1832    }
1833  3 else if (thatIsProtein && !thisIsNucleotide)
1834    {
1835    // honour sense we were called - align this according to al
1836  2 return AlignmentUtils.alignAs(this, al);
1837    }
1838    // otherwise doing a DNA to DNA alignment - so use legacy obverse sense
1839  1 return AlignmentUtils.alignAs(this,al);
1840    }
1841   
1842    /**
1843    * Returns the alignment in Fasta format. Behaviour of this method is not
1844    * guaranteed between versions.
1845    */
 
1846  3 toggle @Override
1847    public String toString()
1848    {
1849  3 return new FastaFile().print(getSequencesArray(), true);
1850    }
1851   
1852    /**
1853    * Returns the set of distinct sequence names. No ordering is guaranteed.
1854    */
 
1855  0 toggle @Override
1856    public Set<String> getSequenceNames()
1857    {
1858  0 Set<String> names = new HashSet<>();
1859  0 for (SequenceI seq : getSequences())
1860    {
1861  0 names.add(seq.getName());
1862    }
1863  0 return names;
1864    }
1865   
 
1866  176 toggle @Override
1867    public boolean hasValidSequence()
1868    {
1869  176 boolean hasValidSeq = false;
1870  176 for (SequenceI seq : getSequences())
1871    {
1872  176 if ((seq.getEnd() - seq.getStart()) > 0)
1873    {
1874  176 hasValidSeq = true;
1875  176 break;
1876    }
1877    }
1878  176 return hasValidSeq;
1879    }
1880   
1881    /**
1882    * Update any mappings to 'virtual' sequences to compatible real ones, if
1883    * present in the added sequences. Returns a count of mappings updated.
1884    *
1885    * @param seqs
1886    * @return
1887    */
 
1888  13 toggle @Override
1889    public int realiseMappings(List<SequenceI> seqs)
1890    {
1891  13 int count = 0;
1892  13 for (SequenceI seq : seqs)
1893    {
1894  16 for (AlignedCodonFrame mapping : getCodonFrames())
1895    {
1896  0 count += mapping.realiseWith(seq);
1897    }
1898    }
1899  13 return count;
1900    }
1901   
1902    /**
1903    * Returns the first AlignedCodonFrame that has a mapping between the given
1904    * dataset sequences
1905    *
1906    * @param mapFrom
1907    * @param mapTo
1908    * @return
1909    */
 
1910  0 toggle @Override
1911    public AlignedCodonFrame getMapping(SequenceI mapFrom, SequenceI mapTo)
1912    {
1913  0 for (AlignedCodonFrame acf : getCodonFrames())
1914    {
1915  0 if (acf.getAaForDnaSeq(mapFrom) == mapTo)
1916    {
1917  0 return acf;
1918    }
1919    }
1920  0 return null;
1921    }
1922   
 
1923  38 toggle @Override
1924    public boolean setHiddenColumns(HiddenColumns cols)
1925    {
1926  38 boolean changed = cols == null ? hiddenCols != null
1927    : !cols.equals(hiddenCols);
1928  38 hiddenCols = cols;
1929  38 return changed;
1930    }
1931   
 
1932  0 toggle @Override
1933    public void setupJPredAlignment()
1934    {
1935  0 SequenceI repseq = getSequenceAt(0);
1936  0 setSeqrep(repseq);
1937  0 HiddenColumns cs = new HiddenColumns();
1938  0 cs.hideList(repseq.getInsertions());
1939  0 setHiddenColumns(cs);
1940    }
1941   
 
1942  2 toggle @Override
1943    public HiddenColumns propagateInsertions(SequenceI profileseq,
1944    AlignmentView input)
1945    {
1946  2 int profsqpos = 0;
1947   
1948  2 char gc = getGapCharacter();
1949  2 Object[] alandhidden = input.getAlignmentAndHiddenColumns(gc);
1950  2 HiddenColumns nview = (HiddenColumns) alandhidden[1];
1951  2 SequenceI origseq = ((SequenceI[]) alandhidden[0])[profsqpos];
1952  2 return propagateInsertions(profileseq, origseq, nview);
1953    }
1954   
1955    /**
1956    *
1957    * @param profileseq
1958    * sequence in al which corresponds to origseq
1959    * @param al
1960    * alignment which is to have gaps inserted into it
1961    * @param origseq
1962    * sequence corresponding to profileseq which defines gap map for
1963    * modifying al
1964    */
 
1965  2 toggle private HiddenColumns propagateInsertions(SequenceI profileseq,
1966    SequenceI origseq, HiddenColumns hc)
1967    {
1968    // take the set of hidden columns, and the set of gaps in origseq,
1969    // and remove all the hidden gaps from hiddenColumns
1970   
1971    // first get the gaps as a Bitset
1972    // then calculate hidden ^ not(gap)
1973  2 BitSet gaps = origseq.gapBitset();
1974  2 hc.andNot(gaps);
1975   
1976    // for each sequence in the alignment, except the profile sequence,
1977    // insert gaps corresponding to each hidden region but where each hidden
1978    // column region is shifted backwards by the number of preceding visible
1979    // gaps update hidden columns at the same time
1980  2 HiddenColumns newhidden = new HiddenColumns();
1981   
1982  2 int numGapsBefore = 0;
1983  2 int gapPosition = 0;
1984  2 Iterator<int[]> it = hc.iterator();
1985  4 while (it.hasNext())
1986    {
1987  2 int[] region = it.next();
1988   
1989    // get region coordinates accounting for gaps
1990    // we can rely on gaps not being *in* hidden regions because we already
1991    // removed those
1992  26 while (gapPosition < region[0])
1993    {
1994  24 gapPosition++;
1995  24 if (gaps.get(gapPosition))
1996    {
1997  8 numGapsBefore++;
1998    }
1999    }
2000   
2001  2 int left = region[0] - numGapsBefore;
2002  2 int right = region[1] - numGapsBefore;
2003   
2004  2 newhidden.hideColumns(left, right);
2005  2 padGaps(left, right, profileseq);
2006    }
2007  2 return newhidden;
2008    }
2009   
2010    /**
2011    * Pad gaps in all sequences in alignment except profileseq
2012    *
2013    * @param left
2014    * position of first gap to insert
2015    * @param right
2016    * position of last gap to insert
2017    * @param profileseq
2018    * sequence not to pad
2019    */
 
2020  2 toggle private void padGaps(int left, int right, SequenceI profileseq)
2021    {
2022  2 char gc = getGapCharacter();
2023   
2024    // make a string with number of gaps = length of hidden region
2025  2 StringBuilder sb = new StringBuilder();
2026  7 for (int g = 0; g < right - left + 1; g++)
2027    {
2028  5 sb.append(gc);
2029    }
2030   
2031    // loop over the sequences and pad with gaps where required
2032  22 for (int s = 0, ns = getHeight(); s < ns; s++)
2033    {
2034  20 SequenceI sqobj = getSequenceAt(s);
2035  20 if ((sqobj != profileseq) && (sqobj.getLength() >= left))
2036    {
2037  15 String sq = sqobj.getSequenceAsString();
2038  15 sqobj.setSequence(
2039    sq.substring(0, left) + sb.toString() + sq.substring(left));
2040    }
2041    }
2042    }
2043   
2044    ////
2045    //// Contact Matrix Holder Boilerplate
2046    ////
2047    ContactMapHolder cmholder = new ContactMapHolder();
2048   
 
2049  13 toggle @Override
2050    public Collection<ContactMatrixI> getContactMaps()
2051    {
2052  13 return cmholder.getContactMaps();
2053    }
2054   
 
2055  1 toggle @Override
2056    public ContactMatrixI getContactMatrixFor(AlignmentAnnotation _aa)
2057    {
2058  1 ContactMatrixI cm = cmholder.getContactMatrixFor(_aa);
2059  1 if (cm == null && _aa.groupRef != null)
2060    {
2061  0 cm = _aa.groupRef.getContactMatrixFor(_aa);
2062    }
2063  1 if (cm == null && _aa.sequenceRef != null)
2064    {
2065  1 cm = _aa.sequenceRef.getContactMatrixFor(_aa);
2066  1 if (cm == null && _aa.sequenceRef.getDatasetSequence() != null)
2067    {
2068    // TODO fix up this logic and unify with getContactListFor
2069  0 cm = _aa.sequenceRef.getDatasetSequence().getContactMatrixFor(_aa);
2070    }
2071    }
2072  1 return cm;
2073    }
2074   
 
2075  4 toggle @Override
2076    public ContactListI getContactListFor(AlignmentAnnotation _aa, int column)
2077    {
2078  4 if (_aa.annotations == null || column >= _aa.annotations.length
2079    || column < 0)
2080    {
2081  1 return null;
2082    }
2083  3 ContactListI cl = cmholder.getContactListFor(_aa, column);
2084  3 if (cl == null && _aa.groupRef != null)
2085    {
2086  0 cl = _aa.groupRef.getContactListFor(_aa, column);
2087    }
2088  3 if (cl == null && _aa.sequenceRef != null)
2089    {
2090  3 if (_aa.annotations[column] != null)
2091    {
2092    // sequence associated
2093  3 cl = _aa.sequenceRef.getContactListFor(_aa, column);
2094  3 if (cl == null && _aa.sequenceRef.getDatasetSequence() != null)
2095    {
2096  0 int spos = _aa.sequenceRef.findPosition(column);
2097  0 if (spos >= _aa.sequenceRef.getStart()
2098    && spos <= 1 + _aa.sequenceRef.getEnd())
2099    {
2100  0 cl = _aa.sequenceRef.getDatasetSequence().getContactListFor(_aa,
2101    spos - _aa.sequenceRef.getStart());
2102    }
2103    }
2104    }
2105    }
2106  3 return cl;
2107    }
2108   
 
2109  0 toggle @Override
2110    public AlignmentAnnotation addContactList(ContactMatrixI cm)
2111    {
2112  0 AlignmentAnnotation aa = cmholder.addContactList(cm);
2113   
2114  0 Annotation _aa[] = new Annotation[getWidth()];
2115  0 for (int i = 0; i < _aa.length; _aa[i++] = new Annotation(0.0f))
2116    {
2117  0 ;
2118    }
2119  0 aa.annotations = _aa;
2120  0 addAnnotation(aa);
2121  0 return aa;
2122    }
2123   
 
2124  0 toggle @Override
2125    public void addContactListFor(AlignmentAnnotation annotation,
2126    ContactMatrixI cm)
2127    {
2128  0 cmholder.addContactListFor(annotation, cm);
2129   
2130    }
2131   
2132    }