Clover icon

jalviewX

  1. Project Clover database Wed Oct 31 2018 15:13:58 GMT
  2. Package jalview.datamodel

File AlignmentAnnotation.java

 

Coverage histogram

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

Code metrics

278
441
53
2
1,651
1,116
302
0.68
8.32
26.5
5.7

Classes

Class Line # Actions
AlignmentAnnotation 42 430 285 224
0.6993288469.9%
AlignmentAnnotation.AnnotCharSequence 436 11 17 2
0.925925992.6%
 

Contributing tests

This file is covered by 163 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.Rna;
24    import jalview.analysis.SecStrConsensus.SimpleBP;
25    import jalview.analysis.WUSSParseException;
26   
27    import java.util.ArrayList;
28    import java.util.Collection;
29    import java.util.Collections;
30    import java.util.HashMap;
31    import java.util.Iterator;
32    import java.util.List;
33    import java.util.Map;
34    import java.util.Map.Entry;
35   
36    /**
37    * DOCUMENT ME!
38    *
39    * @author $author$
40    * @version $Revision$
41    */
 
42    public class AlignmentAnnotation
43    {
44    private static final String ANNOTATION_ID_PREFIX = "ann";
45   
46    /*
47    * Identifers for different types of profile data
48    */
49    public static final int SEQUENCE_PROFILE = 0;
50   
51    public static final int STRUCTURE_PROFILE = 1;
52   
53    public static final int CDNA_PROFILE = 2;
54   
55    private static long counter = 0;
56   
57    /**
58    * If true, this annotations is calculated every edit, eg consensus, quality
59    * or conservation graphs
60    */
61    public boolean autoCalculated = false;
62   
63    /**
64    * unique ID for this annotation, used to match up the same annotation row
65    * shown in multiple views and alignments
66    */
67    public String annotationId;
68   
69    /**
70    * the sequence this annotation is associated with (or null)
71    */
72    public SequenceI sequenceRef;
73   
74    /** label shown in dropdown menus and in the annotation label area */
75    public String label;
76   
77    /** longer description text shown as a tooltip */
78    public String description;
79   
80    /** Array of annotations placed in the current coordinate system */
81    public Annotation[] annotations;
82   
83    public List<SimpleBP> bps = null;
84   
85    /**
86    * RNA secondary structure contact positions
87    */
88    public SequenceFeature[] _rnasecstr = null;
89   
90    /**
91    * position of annotation resulting in invalid WUSS parsing or -1. -2 means
92    * there was no RNA structure in this annotation
93    */
94    private long invalidrnastruc = -2;
95   
96    /**
97    * Updates the _rnasecstr field Determines the positions that base pair and
98    * the positions of helices based on secondary structure from a Stockholm file
99    *
100    * @param rnaAnnotation
101    */
 
102  1279 toggle private void _updateRnaSecStr(CharSequence rnaAnnotation)
103    {
104  1279 try
105    {
106  1279 _rnasecstr = Rna.getHelixMap(rnaAnnotation);
107  960 invalidrnastruc = -1;
108    } catch (WUSSParseException px)
109    {
110    // DEBUG System.out.println(px);
111  319 invalidrnastruc = px.getProblemPos();
112    }
113  1279 if (invalidrnastruc > -1)
114    {
115  319 return;
116    }
117   
118  960 if (_rnasecstr != null && _rnasecstr.length > 0)
119    {
120    // show all the RNA secondary structure annotation symbols.
121  960 isrna = true;
122  960 showAllColLabels = true;
123  960 scaleColLabel = true;
124  960 _markRnaHelices();
125    }
126    // System.out.println("featuregroup " + _rnasecstr[0].getFeatureGroup());
127   
128    }
129   
 
130  960 toggle private void _markRnaHelices()
131    {
132  960 int mxval = 0;
133    // Figure out number of helices
134    // Length of rnasecstr is the number of pairs of positions that base pair
135    // with each other in the secondary structure
136  19689 for (int x = 0; x < _rnasecstr.length; x++)
137    {
138   
139    /*
140    * System.out.println(this.annotation._rnasecstr[x] + " Begin" +
141    * this.annotation._rnasecstr[x].getBegin());
142    */
143    // System.out.println(this.annotation._rnasecstr[x].getFeatureGroup());
144  18729 int val = 0;
145  18729 try
146    {
147  18729 val = Integer.valueOf(_rnasecstr[x].getFeatureGroup());
148  18729 if (mxval < val)
149    {
150  284 mxval = val;
151    }
152    } catch (NumberFormatException q)
153    {
154    }
155  18729 ;
156   
157  18729 annotations[_rnasecstr[x].getBegin()].value = val;
158  18729 annotations[_rnasecstr[x].getEnd()].value = val;
159   
160    // annotations[_rnasecstr[x].getBegin()].displayCharacter = "" + val;
161    // annotations[_rnasecstr[x].getEnd()].displayCharacter = "" + val;
162    }
163  960 setScore(mxval);
164    }
165   
166    /**
167    * map of positions in the associated annotation
168    */
169    private Map<Integer, Annotation> sequenceMapping;
170   
171    /**
172    * lower range for quantitative data
173    */
174    public float graphMin;
175   
176    /**
177    * Upper range for quantitative data
178    */
179    public float graphMax;
180   
181    /**
182    * Score associated with label and description.
183    */
184    public double score = Double.NaN;
185   
186    /**
187    * flag indicating if annotation has a score.
188    */
189    public boolean hasScore = false;
190   
191    public GraphLine threshold;
192   
193    // Graphical hints and tips
194   
195    /** Can this row be edited by the user ? */
196    public boolean editable = false;
197   
198    /** Indicates if annotation has a graphical symbol track */
199    public boolean hasIcons; //
200   
201    /** Indicates if annotation has a text character label */
202    public boolean hasText;
203   
204    /** is the row visible */
205    public boolean visible = true;
206   
207    public int graphGroup = -1;
208   
209    /** Displayed height of row in pixels */
210    public int height = 0;
211   
212    public int graph = 0;
213   
214    public int graphHeight = 40;
215   
216    public boolean padGaps = false;
217   
218    public static final int NO_GRAPH = 0;
219   
220    public static final int BAR_GRAPH = 1;
221   
222    public static final int LINE_GRAPH = 2;
223   
224    public boolean belowAlignment = true;
225   
226    public SequenceGroup groupRef = null;
227   
228    /**
229    * display every column label, even if there is a row of identical labels
230    */
231    public boolean showAllColLabels = false;
232   
233    /**
234    * scale the column label to fit within the alignment column.
235    */
236    public boolean scaleColLabel = false;
237   
238    /**
239    * centre the column labels relative to the alignment column
240    */
241    public boolean centreColLabels = false;
242   
243    private boolean isrna;
244   
 
245  196 toggle public static int getGraphValueFromString(String string)
246    {
247  196 if (string.equalsIgnoreCase("BAR_GRAPH"))
248    {
249  23 return BAR_GRAPH;
250    }
251  173 else if (string.equalsIgnoreCase("LINE_GRAPH"))
252    {
253  162 return LINE_GRAPH;
254    }
255    else
256    {
257  11 return NO_GRAPH;
258    }
259    }
260   
261    /**
262    * Creates a new AlignmentAnnotation object.
263    *
264    * @param label
265    * short label shown under sequence labels
266    * @param description
267    * text displayed on mouseover
268    * @param annotations
269    * set of positional annotation elements
270    */
 
271  34301 toggle public AlignmentAnnotation(String label, String description,
272    Annotation[] annotations)
273    {
274  34301 setAnnotationId();
275    // always editable?
276  34301 editable = true;
277  34301 this.label = label;
278  34301 this.description = description;
279  34301 this.annotations = annotations;
280   
281  34301 validateRangeAndDisplay();
282    }
283   
284    /**
285    * Checks if annotation labels represent secondary structures
286    *
287    */
 
288  3509 toggle void areLabelsSecondaryStructure()
289    {
290  3509 boolean nonSSLabel = false;
291  3509 isrna = false;
292  3509 StringBuffer rnastring = new StringBuffer();
293   
294  3509 char firstChar = 0;
295  311033 for (int i = 0; i < annotations.length; i++)
296    {
297  307524 if (annotations[i] == null)
298    {
299  83664 continue;
300    }
301  223860 if (annotations[i].secondaryStructure == 'H'
302    || annotations[i].secondaryStructure == 'E')
303    {
304  27898 hasIcons |= true;
305    }
306    else
307    // Check for RNA secondary structure
308    {
309    // System.out.println(annotations[i].secondaryStructure);
310    // TODO: 2.8.2 should this ss symbol validation check be a function in
311    // RNA/ResidueProperties ?
312  195962 if (annotations[i].secondaryStructure == '('
313    || annotations[i].secondaryStructure == '['
314    || annotations[i].secondaryStructure == '<'
315    || annotations[i].secondaryStructure == '{'
316    || annotations[i].secondaryStructure == 'A'
317    || annotations[i].secondaryStructure == 'B'
318    || annotations[i].secondaryStructure == 'C'
319    || annotations[i].secondaryStructure == 'D'
320    || annotations[i].secondaryStructure == 'E'
321    || annotations[i].secondaryStructure == 'F'
322    || annotations[i].secondaryStructure == 'G'
323    || annotations[i].secondaryStructure == 'H'
324    || annotations[i].secondaryStructure == 'I'
325    || annotations[i].secondaryStructure == 'J'
326    || annotations[i].secondaryStructure == 'K'
327    || annotations[i].secondaryStructure == 'L'
328    || annotations[i].secondaryStructure == 'M'
329    || annotations[i].secondaryStructure == 'N'
330    || annotations[i].secondaryStructure == 'O'
331    || annotations[i].secondaryStructure == 'P'
332    || annotations[i].secondaryStructure == 'Q'
333    || annotations[i].secondaryStructure == 'R'
334    || annotations[i].secondaryStructure == 'S'
335    || annotations[i].secondaryStructure == 'T'
336    || annotations[i].secondaryStructure == 'U'
337    || annotations[i].secondaryStructure == 'V'
338    || annotations[i].secondaryStructure == 'W'
339    || annotations[i].secondaryStructure == 'X'
340    || annotations[i].secondaryStructure == 'Y'
341    || annotations[i].secondaryStructure == 'Z')
342    {
343  14102 hasIcons |= true;
344  14102 isrna |= true;
345    }
346    }
347   
348    // System.out.println("displaychar " + annotations[i].displayCharacter);
349   
350  223860 if (annotations[i].displayCharacter == null
351    || annotations[i].displayCharacter.length() == 0)
352    {
353  92481 rnastring.append('.');
354  92481 continue;
355    }
356  131379 if (annotations[i].displayCharacter.length() == 1)
357    {
358  83663 firstChar = annotations[i].displayCharacter.charAt(0);
359    // check to see if it looks like a sequence or is secondary structure
360    // labelling.
361  83663 if (annotations[i].secondaryStructure != ' ' && !hasIcons &&
362    // Uncomment to only catch case where
363    // displayCharacter==secondary
364    // Structure
365    // to correctly redisplay SS annotation imported from Stockholm,
366    // exported to JalviewXML and read back in again.
367    // &&
368    // annotations[i].displayCharacter.charAt(0)==annotations[i].secondaryStructure
369    firstChar != ' ' && firstChar != '$' && firstChar != 0xCE
370    && firstChar != '(' && firstChar != '[' && firstChar != '>'
371    && firstChar != '{' && firstChar != 'A' && firstChar != 'B'
372    && firstChar != 'C' && firstChar != 'D' && firstChar != 'E'
373    && firstChar != 'F' && firstChar != 'G' && firstChar != 'H'
374    && firstChar != 'I' && firstChar != 'J' && firstChar != 'K'
375    && firstChar != 'L' && firstChar != 'M' && firstChar != 'N'
376    && firstChar != 'O' && firstChar != 'P' && firstChar != 'Q'
377    && firstChar != 'R' && firstChar != 'S' && firstChar != 'T'
378    && firstChar != 'U' && firstChar != 'V' && firstChar != 'W'
379    && firstChar != 'X' && firstChar != 'Y' && firstChar != 'Z'
380    && firstChar != '-'
381    && firstChar < jalview.schemes.ResidueProperties.aaIndex.length)
382    {
383  174 if (jalview.schemes.ResidueProperties.aaIndex[firstChar] < 23) // TODO:
384    // parameterise
385    // to
386    // gap
387    // symbol
388    // number
389    {
390  11 nonSSLabel = true;
391    }
392    }
393    }
394    else
395    {
396  47716 rnastring.append(annotations[i].displayCharacter.charAt(1));
397    }
398   
399  131379 if (annotations[i].displayCharacter.length() > 0)
400    {
401  131379 hasText = true;
402    }
403    }
404   
405  3509 if (nonSSLabel)
406    {
407  1 hasIcons = false;
408  12 for (int j = 0; j < annotations.length; j++)
409    {
410  11 if (annotations[j] != null
411    && annotations[j].secondaryStructure != ' ')
412    {
413  11 annotations[j].displayCharacter = String
414    .valueOf(annotations[j].secondaryStructure);
415  11 annotations[j].secondaryStructure = ' ';
416    }
417   
418    }
419    }
420    else
421    {
422  3508 if (isrna)
423    {
424  881 _updateRnaSecStr(new AnnotCharSequence());
425    }
426    }
427    }
428   
429    /**
430    * flyweight access to positions in the alignment annotation row for RNA
431    * processing
432    *
433    * @author jimp
434    *
435    */
 
436    private class AnnotCharSequence implements CharSequence
437    {
438    int offset = 0;
439   
440    int max = 0;
441   
 
442  1287 toggle public AnnotCharSequence()
443    {
444  1287 this(0, annotations.length);
445    }
446   
 
447  1287 toggle AnnotCharSequence(int start, int end)
448    {
449  1287 offset = start;
450  1287 max = end;
451    }
452   
 
453  0 toggle @Override
454    public CharSequence subSequence(int start, int end)
455    {
456  0 return new AnnotCharSequence(offset + start, offset + end);
457    }
458   
 
459  64749 toggle @Override
460    public int length()
461    {
462  64749 return max - offset;
463    }
464   
 
465  63868 toggle @Override
466    public char charAt(int index)
467    {
468  63868 return ((index + offset < 0) || (index + offset) >= max
469    || annotations[index + offset] == null
470    || (annotations[index + offset].secondaryStructure <= ' ')
471    ? ' '
472  26634 : annotations[index + offset].displayCharacter == null
473    || annotations[index
474    + offset].displayCharacter
475    .length() == 0
476    ? annotations[index
477    + offset].secondaryStructure
478    : annotations[index
479    + offset].displayCharacter
480    .charAt(0));
481    }
482   
 
483  406 toggle @Override
484    public String toString()
485    {
486  406 char[] string = new char[max - offset];
487  406 int mx = annotations.length;
488   
489  31374 for (int i = offset; i < mx; i++)
490    {
491  30968 string[i] = (annotations[i] == null
492    || (annotations[i].secondaryStructure <= 32))
493    ? ' '
494  13168 : (annotations[i].displayCharacter == null
495    || annotations[i].displayCharacter
496    .length() == 0
497    ? annotations[i].secondaryStructure
498    : annotations[i].displayCharacter
499    .charAt(0));
500    }
501  406 return new String(string);
502    }
503    };
504   
505    private long _lastrnaannot = -1;
506   
 
507  2202 toggle public String getRNAStruc()
508    {
509  2203 if (isrna)
510    {
511  406 String rnastruc = new AnnotCharSequence().toString();
512  406 if (_lastrnaannot != rnastruc.hashCode())
513    {
514    // ensure rna structure contacts are up to date
515  398 _lastrnaannot = rnastruc.hashCode();
516  398 _updateRnaSecStr(rnastruc);
517    }
518  406 return rnastruc;
519    }
520  1797 return null;
521    }
522   
523    /**
524    * Creates a new AlignmentAnnotation object.
525    *
526    * @param label
527    * DOCUMENT ME!
528    * @param description
529    * DOCUMENT ME!
530    * @param annotations
531    * DOCUMENT ME!
532    * @param min
533    * DOCUMENT ME!
534    * @param max
535    * DOCUMENT ME!
536    * @param winLength
537    * DOCUMENT ME!
538    */
 
539  1597 toggle public AlignmentAnnotation(String label, String description,
540    Annotation[] annotations, float min, float max, int graphType)
541    {
542  1597 setAnnotationId();
543    // graphs are not editable
544  1597 editable = graphType == 0;
545   
546  1597 this.label = label;
547  1597 this.description = description;
548  1597 this.annotations = annotations;
549  1597 graph = graphType;
550  1597 graphMin = min;
551  1597 graphMax = max;
552  1597 validateRangeAndDisplay();
553    }
554   
555    /**
556    * checks graphMin and graphMax, secondary structure symbols, sets graphType
557    * appropriately, sets null labels to the empty string if appropriate.
558    */
 
559  36968 toggle public void validateRangeAndDisplay()
560    {
561   
562  36968 if (annotations == null)
563    {
564  33459 visible = false; // try to prevent renderer from displaying.
565  33459 invalidrnastruc = -1;
566  33459 return; // this is a non-annotation row annotation - ie a sequence score.
567    }
568   
569  3509 int graphType = graph;
570  3509 float min = graphMin;
571  3509 float max = graphMax;
572  3509 boolean drawValues = true;
573  3509 _linecolour = null;
574  3509 if (min == max)
575    {
576  2201 min = 999999999;
577  243677 for (int i = 0; i < annotations.length; i++)
578    {
579  241476 if (annotations[i] == null)
580    {
581  76062 continue;
582    }
583   
584  165414 if (drawValues && annotations[i].displayCharacter != null
585    && annotations[i].displayCharacter.length() > 1)
586    {
587  1046 drawValues = false;
588    }
589   
590  165414 if (annotations[i].value > max)
591    {
592  2390 max = annotations[i].value;
593    }
594   
595  165414 if (annotations[i].value < min)
596    {
597  4975 min = annotations[i].value;
598    }
599  165414 if (_linecolour == null && annotations[i].colour != null)
600    {
601  740 _linecolour = annotations[i].colour;
602    }
603    }
604    // ensure zero is origin for min/max ranges on only one side of zero
605  2200 if (min > 0)
606    {
607  878 min = 0;
608    }
609    else
610    {
611  1323 if (max < 0)
612    {
613  0 max = 0;
614    }
615    }
616    }
617   
618  3509 graphMin = min;
619  3509 graphMax = max;
620   
621  3509 areLabelsSecondaryStructure();
622   
623  3509 if (!drawValues && graphType != NO_GRAPH)
624    {
625  27176 for (int i = 0; i < annotations.length; i++)
626    {
627  27004 if (annotations[i] != null)
628    {
629  21912 annotations[i].displayCharacter = "";
630    }
631    }
632    }
633    }
634   
635    /**
636    * Copy constructor creates a new independent annotation row with the same
637    * associated sequenceRef
638    *
639    * @param annotation
640    */
 
641  520 toggle public AlignmentAnnotation(AlignmentAnnotation annotation)
642    {
643  520 setAnnotationId();
644  520 this.label = new String(annotation.label);
645  520 if (annotation.description != null)
646    {
647  496 this.description = new String(annotation.description);
648    }
649  520 this.graphMin = annotation.graphMin;
650  520 this.graphMax = annotation.graphMax;
651  520 this.graph = annotation.graph;
652  520 this.graphHeight = annotation.graphHeight;
653  520 this.graphGroup = annotation.graphGroup;
654  520 this.groupRef = annotation.groupRef;
655  520 this.editable = annotation.editable;
656  520 this.autoCalculated = annotation.autoCalculated;
657  520 this.hasIcons = annotation.hasIcons;
658  520 this.hasText = annotation.hasText;
659  520 this.height = annotation.height;
660  520 this.label = annotation.label;
661  520 this.padGaps = annotation.padGaps;
662  520 this.visible = annotation.visible;
663  520 this.centreColLabels = annotation.centreColLabels;
664  520 this.scaleColLabel = annotation.scaleColLabel;
665  520 this.showAllColLabels = annotation.showAllColLabels;
666  520 this.calcId = annotation.calcId;
667  520 if (annotation.properties != null)
668    {
669  520 properties = new HashMap<>();
670  520 for (Map.Entry<String, String> val : annotation.properties.entrySet())
671    {
672  3 properties.put(val.getKey(), val.getValue());
673    }
674    }
675  ? if (this.hasScore = annotation.hasScore)
676    {
677  129 this.score = annotation.score;
678    }
679  520 if (annotation.threshold != null)
680    {
681  0 threshold = new GraphLine(annotation.threshold);
682    }
683  520 Annotation[] ann = annotation.annotations;
684  520 if (annotation.annotations != null)
685    {
686  518 this.annotations = new Annotation[ann.length];
687  63821 for (int i = 0; i < ann.length; i++)
688    {
689  63303 if (ann[i] != null)
690    {
691  45136 annotations[i] = new Annotation(ann[i]);
692  45136 if (_linecolour != null)
693    {
694  0 _linecolour = annotations[i].colour;
695    }
696    }
697    }
698    }
699  520 if (annotation.sequenceRef != null)
700    {
701  335 this.sequenceRef = annotation.sequenceRef;
702  335 if (annotation.sequenceMapping != null)
703    {
704  333 Integer p = null;
705  333 sequenceMapping = new HashMap<>();
706  333 Iterator<Integer> pos = annotation.sequenceMapping.keySet()
707    .iterator();
708  24305 while (pos.hasNext())
709    {
710    // could optimise this!
711  23972 p = pos.next();
712  23972 Annotation a = annotation.sequenceMapping.get(p);
713  23972 if (a == null)
714    {
715  0 continue;
716    }
717  23972 if (ann != null)
718    {
719  4221563 for (int i = 0; i < ann.length; i++)
720    {
721  4197591 if (ann[i] == a)
722    {
723  23970 sequenceMapping.put(p, annotations[i]);
724    }
725    }
726    }
727    }
728    }
729    else
730    {
731  2 this.sequenceMapping = null;
732    }
733    }
734    // TODO: check if we need to do this: JAL-952
735    // if (this.isrna=annotation.isrna)
736    {
737    // _rnasecstr=new SequenceFeature[annotation._rnasecstr];
738    }
739  520 validateRangeAndDisplay(); // construct hashcodes, etc.
740    }
741   
742    /**
743    * clip the annotation to the columns given by startRes and endRes (inclusive)
744    * and prune any existing sequenceMapping to just those columns.
745    *
746    * @param startRes
747    * @param endRes
748    */
 
749  6 toggle public void restrict(int startRes, int endRes)
750    {
751  6 if (annotations == null)
752    {
753    // non-positional
754  0 return;
755    }
756  6 if (startRes < 0)
757    {
758  0 startRes = 0;
759    }
760  6 if (startRes >= annotations.length)
761    {
762  0 startRes = annotations.length - 1;
763    }
764  6 if (endRes >= annotations.length)
765    {
766  1 endRes = annotations.length - 1;
767    }
768  6 if (annotations == null)
769    {
770  0 return;
771    }
772  6 Annotation[] temp = new Annotation[endRes - startRes + 1];
773  6 if (startRes < annotations.length)
774    {
775  6 System.arraycopy(annotations, startRes, temp, 0,
776    endRes - startRes + 1);
777    }
778  6 if (sequenceRef != null)
779    {
780    // Clip the mapping, if it exists.
781  0 int spos = sequenceRef.findPosition(startRes);
782  0 int epos = sequenceRef.findPosition(endRes);
783  0 if (sequenceMapping != null)
784    {
785  0 Map<Integer, Annotation> newmapping = new HashMap<>();
786  0 Iterator<Integer> e = sequenceMapping.keySet().iterator();
787  0 while (e.hasNext())
788    {
789  0 Integer pos = e.next();
790  0 if (pos.intValue() >= spos && pos.intValue() <= epos)
791    {
792  0 newmapping.put(pos, sequenceMapping.get(pos));
793    }
794    }
795  0 sequenceMapping.clear();
796  0 sequenceMapping = newmapping;
797    }
798    }
799  6 annotations = temp;
800    }
801   
802    /**
803    * set the annotation row to be at least length Annotations
804    *
805    * @param length
806    * minimum number of columns required in the annotation row
807    * @return false if the annotation row is greater than length
808    */
 
809  0 toggle public boolean padAnnotation(int length)
810    {
811  0 if (annotations == null)
812    {
813  0 return true; // annotation row is correct - null == not visible and
814    // undefined length
815    }
816  0 if (annotations.length < length)
817    {
818  0 Annotation[] na = new Annotation[length];
819  0 System.arraycopy(annotations, 0, na, 0, annotations.length);
820  0 annotations = na;
821  0 return true;
822    }
823  0 return annotations.length > length;
824   
825    }
826   
827    /**
828    * DOCUMENT ME!
829    *
830    * @return DOCUMENT ME!
831    */
 
832  0 toggle @Override
833    public String toString()
834    {
835  0 if (annotations == null)
836    {
837  0 return "";
838    }
839  0 StringBuilder buffer = new StringBuilder(256);
840   
841  0 for (int i = 0; i < annotations.length; i++)
842    {
843  0 if (annotations[i] != null)
844    {
845  0 if (graph != 0)
846    {
847  0 buffer.append(annotations[i].value);
848    }
849  0 else if (hasIcons)
850    {
851  0 buffer.append(annotations[i].secondaryStructure);
852    }
853    else
854    {
855  0 buffer.append(annotations[i].displayCharacter);
856    }
857    }
858   
859  0 buffer.append(", ");
860    }
861    // TODO: remove disgusting hack for 'special' treatment of consensus line.
862  0 if (label.indexOf("Consensus") == 0)
863    {
864  0 buffer.append("\n");
865   
866  0 for (int i = 0; i < annotations.length; i++)
867    {
868  0 if (annotations[i] != null)
869    {
870  0 buffer.append(annotations[i].description);
871    }
872   
873  0 buffer.append(", ");
874    }
875    }
876   
877  0 return buffer.toString();
878    }
879   
 
880  185 toggle public void setThreshold(GraphLine line)
881    {
882  185 threshold = line;
883    }
884   
 
885  526 toggle public GraphLine getThreshold()
886    {
887  526 return threshold;
888    }
889   
890    /**
891    * Attach the annotation to seqRef, starting from startRes position. If
892    * alreadyMapped is true then the indices of the annotation[] array are
893    * sequence positions rather than alignment column positions.
894    *
895    * @param seqRef
896    * @param startRes
897    * @param alreadyMapped
898    */
 
899  1139 toggle public void createSequenceMapping(SequenceI seqRef, int startRes,
900    boolean alreadyMapped)
901    {
902   
903  1139 if (seqRef == null)
904    {
905  0 return;
906    }
907  1139 sequenceRef = seqRef;
908  1139 if (annotations == null)
909    {
910  88 return;
911    }
912  1051 sequenceMapping = new HashMap<>();
913   
914  1051 int seqPos;
915   
916  120674 for (int i = 0; i < annotations.length; i++)
917    {
918  119623 if (annotations[i] != null)
919    {
920  91372 if (alreadyMapped)
921    {
922  70796 seqPos = seqRef.findPosition(i);
923    }
924    else
925    {
926  20576 seqPos = i + startRes;
927    }
928   
929  91372 sequenceMapping.put(new Integer(seqPos), annotations[i]);
930    }
931    }
932   
933    }
934   
935    /**
936    * When positional annotation and a sequence reference is present, clears and
937    * resizes the annotations array to the current alignment width, and adds
938    * annotation according to aligned positions of the sequenceRef given by
939    * sequenceMapping.
940    */
 
941  1390 toggle public void adjustForAlignment()
942    {
943  1390 if (sequenceRef == null)
944    {
945  119 return;
946    }
947   
948  1271 if (annotations == null)
949    {
950  88 return;
951    }
952   
953  1183 int a = 0, aSize = sequenceRef.getLength();
954   
955  1183 if (aSize == 0)
956    {
957    // Its been deleted
958  1 return;
959    }
960   
961  1182 int position;
962  1182 Annotation[] temp = new Annotation[aSize];
963  1182 Integer index;
964  1182 if (sequenceMapping != null)
965    {
966  116956 for (a = sequenceRef.getStart(); a <= sequenceRef.getEnd(); a++)
967    {
968  115774 index = new Integer(a);
969  115774 Annotation annot = sequenceMapping.get(index);
970  115774 if (annot != null)
971    {
972  89924 position = sequenceRef.findIndex(a) - 1;
973   
974  89924 temp[position] = annot;
975    }
976    }
977    }
978  1182 annotations = temp;
979    }
980   
981    /**
982    * remove any null entries in annotation row and return the number of non-null
983    * annotation elements.
984    *
985    * @return
986    */
 
987  0 toggle public int compactAnnotationArray()
988    {
989  0 int i = 0, iSize = annotations.length;
990  0 while (i < iSize)
991    {
992  0 if (annotations[i] == null)
993    {
994  0 if (i + 1 < iSize)
995    {
996  0 System.arraycopy(annotations, i + 1, annotations, i,
997    iSize - i - 1);
998    }
999  0 iSize--;
1000    }
1001    else
1002    {
1003  0 i++;
1004    }
1005    }
1006  0 Annotation[] ann = annotations;
1007  0 annotations = new Annotation[i];
1008  0 System.arraycopy(ann, 0, annotations, 0, i);
1009  0 ann = null;
1010  0 return iSize;
1011    }
1012   
1013    /**
1014    * Associate this annotation with the aligned residues of a particular
1015    * sequence. sequenceMapping will be updated in the following way: null
1016    * sequenceI - existing mapping will be discarded but annotations left in
1017    * mapped positions. valid sequenceI not equal to current sequenceRef: mapping
1018    * is discarded and rebuilt assuming 1:1 correspondence TODO: overload with
1019    * parameter to specify correspondence between current and new sequenceRef
1020    *
1021    * @param sequenceI
1022    */
 
1023  1669 toggle public void setSequenceRef(SequenceI sequenceI)
1024    {
1025  1669 if (sequenceI != null)
1026    {
1027  1669 if (sequenceRef != null)
1028    {
1029  932 boolean rIsDs = sequenceRef.getDatasetSequence() == null,
1030    tIsDs = sequenceI.getDatasetSequence() == null;
1031  932 if (sequenceRef != sequenceI
1032    && (rIsDs && !tIsDs
1033    && sequenceRef != sequenceI.getDatasetSequence())
1034    && (!rIsDs && tIsDs
1035    && sequenceRef.getDatasetSequence() != sequenceI)
1036    && (!rIsDs && !tIsDs
1037    && sequenceRef.getDatasetSequence() != sequenceI
1038    .getDatasetSequence())
1039    && !sequenceRef.equals(sequenceI))
1040    {
1041    // if sequenceRef isn't intersecting with sequenceI
1042    // throw away old mapping and reconstruct.
1043  0 sequenceRef = null;
1044  0 if (sequenceMapping != null)
1045    {
1046  0 sequenceMapping = null;
1047    // compactAnnotationArray();
1048    }
1049  0 createSequenceMapping(sequenceI, 1, true);
1050  0 adjustForAlignment();
1051    }
1052    else
1053    {
1054    // Mapping carried over
1055  932 sequenceRef = sequenceI;
1056    }
1057    }
1058    else
1059    {
1060    // No mapping exists
1061  737 createSequenceMapping(sequenceI, 1, true);
1062  737 adjustForAlignment();
1063    }
1064    }
1065    else
1066    {
1067    // throw away the mapping without compacting.
1068  0 sequenceMapping = null;
1069  0 sequenceRef = null;
1070    }
1071    }
1072   
1073    /**
1074    * @return the score
1075    */
 
1076  156 toggle public double getScore()
1077    {
1078  156 return score;
1079    }
1080   
1081    /**
1082    * @param score
1083    * the score to set
1084    */
 
1085  34560 toggle public void setScore(double score)
1086    {
1087  34560 hasScore = true;
1088  34560 this.score = score;
1089    }
1090   
1091    /**
1092    *
1093    * @return true if annotation has an associated score
1094    */
 
1095  689 toggle public boolean hasScore()
1096    {
1097  689 return hasScore || !Double.isNaN(score);
1098    }
1099   
1100    /**
1101    * Score only annotation
1102    *
1103    * @param label
1104    * @param description
1105    * @param score
1106    */
 
1107  33428 toggle public AlignmentAnnotation(String label, String description, double score)
1108    {
1109  33428 this(label, description, null);
1110  33428 setScore(score);
1111    }
1112   
1113    /**
1114    * copy constructor with edit based on the hidden columns marked in colSel
1115    *
1116    * @param alignmentAnnotation
1117    * @param colSel
1118    */
 
1119  0 toggle public AlignmentAnnotation(AlignmentAnnotation alignmentAnnotation,
1120    HiddenColumns hidden)
1121    {
1122  0 this(alignmentAnnotation);
1123  0 if (annotations == null)
1124    {
1125  0 return;
1126    }
1127  0 makeVisibleAnnotation(hidden);
1128    }
1129   
 
1130  0 toggle public void setPadGaps(boolean padgaps, char gapchar)
1131    {
1132  0 this.padGaps = padgaps;
1133  0 if (padgaps)
1134    {
1135  0 hasText = true;
1136  0 for (int i = 0; i < annotations.length; i++)
1137    {
1138  0 if (annotations[i] == null)
1139    {
1140  0 annotations[i] = new Annotation(String.valueOf(gapchar), null,
1141    ' ', 0f, null);
1142    }
1143  0 else if (annotations[i].displayCharacter == null
1144    || annotations[i].displayCharacter.equals(" "))
1145    {
1146  0 annotations[i].displayCharacter = String.valueOf(gapchar);
1147    }
1148    }
1149    }
1150    }
1151   
1152    /**
1153    * format description string for display
1154    *
1155    * @param seqname
1156    * @return Get the annotation description string optionally prefixed by
1157    * associated sequence name (if any)
1158    */
 
1159  0 toggle public String getDescription(boolean seqname)
1160    {
1161  0 if (seqname && this.sequenceRef != null)
1162    {
1163  0 int i = description.toLowerCase().indexOf("<html>");
1164  0 if (i > -1)
1165    {
1166    // move the html tag to before the sequence reference.
1167  0 return "<html>" + sequenceRef.getName() + " : "
1168    + description.substring(i + 6);
1169    }
1170  0 return sequenceRef.getName() + " : " + description;
1171    }
1172  0 return description;
1173    }
1174   
 
1175  229 toggle public boolean isValidStruc()
1176    {
1177  229 return invalidrnastruc == -1;
1178    }
1179   
 
1180  527552 toggle public long getInvalidStrucPos()
1181    {
1182  527552 return invalidrnastruc;
1183    }
1184   
1185    /**
1186    * machine readable ID string indicating what generated this annotation
1187    */
1188    protected String calcId = "";
1189   
1190    /**
1191    * properties associated with the calcId
1192    */
1193    protected Map<String, String> properties = new HashMap<>();
1194   
1195    /**
1196    * base colour for line graphs. If null, will be set automatically by
1197    * searching the alignment annotation
1198    */
1199    public java.awt.Color _linecolour;
1200   
 
1201  6952 toggle public String getCalcId()
1202    {
1203  6952 return calcId;
1204    }
1205   
 
1206  931 toggle public void setCalcId(String calcId)
1207    {
1208  931 this.calcId = calcId;
1209    }
1210   
 
1211  6643 toggle public boolean isRNA()
1212    {
1213  6643 return isrna;
1214    }
1215   
1216    /**
1217    * transfer annotation to the given sequence using the given mapping from the
1218    * current positions or an existing sequence mapping
1219    *
1220    * @param sq
1221    * @param sp2sq
1222    * map involving sq as To or From
1223    */
 
1224  57 toggle public void liftOver(SequenceI sq, Mapping sp2sq)
1225    {
1226  57 if (sp2sq.getMappedWidth() != sp2sq.getWidth())
1227    {
1228    // TODO: employ getWord/MappedWord to transfer annotation between cDNA and
1229    // Protein reference frames
1230  0 throw new Error(
1231    "liftOver currently not implemented for transfer of annotation between different types of seqeunce");
1232    }
1233  57 boolean mapIsTo = (sp2sq != null)
1234    ? (sp2sq.getTo() == sq
1235    || sp2sq.getTo() == sq.getDatasetSequence())
1236    : false;
1237   
1238    // TODO build a better annotation element map and get rid of annotations[]
1239  57 Map<Integer, Annotation> mapForsq = new HashMap<>();
1240  57 if (sequenceMapping != null)
1241    {
1242  57 if (sp2sq != null)
1243    {
1244  57 for (Entry<Integer, Annotation> ie : sequenceMapping.entrySet())
1245    {
1246  4787 Integer mpos = Integer
1247  4787 .valueOf(mapIsTo ? sp2sq.getMappedPosition(ie.getKey())
1248    : sp2sq.getPosition(ie.getKey()));
1249  4787 if (mpos >= sq.getStart() && mpos <= sq.getEnd())
1250    {
1251  4781 mapForsq.put(mpos, ie.getValue());
1252    }
1253    }
1254  57 sequenceMapping = mapForsq;
1255  57 sequenceRef = sq;
1256  57 adjustForAlignment();
1257    }
1258    else
1259    {
1260    // trim positions
1261    }
1262    }
1263    }
1264   
1265    /**
1266    * like liftOver but more general.
1267    *
1268    * Takes an array of int pairs that will be used to update the internal
1269    * sequenceMapping and so shuffle the annotated positions
1270    *
1271    * @param newref
1272    * - new sequence reference for the annotation row - if null,
1273    * sequenceRef is left unchanged
1274    * @param mapping
1275    * array of ints containing corresponding positions
1276    * @param from
1277    * - column for current coordinate system (-1 for index+1)
1278    * @param to
1279    * - column for destination coordinate system (-1 for index+1)
1280    * @param idxoffset
1281    * - offset added to index when referencing either coordinate system
1282    * @note no checks are made as to whether from and/or to are sensible
1283    * @note caller should add the remapped annotation to newref if they have not
1284    * already
1285    */
 
1286  0 toggle public void remap(SequenceI newref, HashMap<Integer, int[]> mapping,
1287    int from, int to, int idxoffset)
1288    {
1289  0 if (mapping != null)
1290    {
1291  0 Map<Integer, Annotation> old = sequenceMapping;
1292  0 Map<Integer, Annotation> remap = new HashMap<>();
1293  0 int index = -1;
1294  0 for (int mp[] : mapping.values())
1295    {
1296  0 if (index++ < 0)
1297    {
1298  0 continue;
1299    }
1300  0 Annotation ann = null;
1301  0 if (from == -1)
1302    {
1303  0 ann = sequenceMapping.get(Integer.valueOf(idxoffset + index));
1304    }
1305    else
1306    {
1307  0 if (mp != null && mp.length > from)
1308    {
1309  0 ann = sequenceMapping.get(Integer.valueOf(mp[from]));
1310    }
1311    }
1312  0 if (ann != null)
1313    {
1314  0 if (to == -1)
1315    {
1316  0 remap.put(Integer.valueOf(idxoffset + index), ann);
1317    }
1318    else
1319    {
1320  0 if (to > -1 && to < mp.length)
1321    {
1322  0 remap.put(Integer.valueOf(mp[to]), ann);
1323    }
1324    }
1325    }
1326    }
1327  0 sequenceMapping = remap;
1328  0 old.clear();
1329  0 if (newref != null)
1330    {
1331  0 sequenceRef = newref;
1332    }
1333  0 adjustForAlignment();
1334    }
1335    }
1336   
 
1337  2 toggle public String getProperty(String property)
1338    {
1339  2 if (properties == null)
1340    {
1341  0 return null;
1342    }
1343  2 return properties.get(property);
1344    }
1345   
 
1346  35 toggle public void setProperty(String property, String value)
1347    {
1348  35 if (properties == null)
1349    {
1350  0 properties = new HashMap<>();
1351    }
1352  35 properties.put(property, value);
1353    }
1354   
 
1355  331 toggle public boolean hasProperties()
1356    {
1357  331 return properties != null && properties.size() > 0;
1358    }
1359   
 
1360  1 toggle public Collection<String> getProperties()
1361    {
1362  1 if (properties == null)
1363    {
1364  0 return Collections.emptyList();
1365    }
1366  1 return properties.keySet();
1367    }
1368   
1369    /**
1370    * Returns the Annotation for the given sequence position (base 1) if any,
1371    * else null
1372    *
1373    * @param position
1374    * @return
1375    */
 
1376  25 toggle public Annotation getAnnotationForPosition(int position)
1377    {
1378  25 return sequenceMapping == null ? null : sequenceMapping.get(position);
1379   
1380    }
1381   
1382    /**
1383    * Set the id to "ann" followed by a counter that increments so as to be
1384    * unique for the lifetime of the JVM
1385    */
 
1386  36418 toggle protected final void setAnnotationId()
1387    {
1388  36418 this.annotationId = ANNOTATION_ID_PREFIX + Long.toString(nextId());
1389    }
1390   
1391    /**
1392    * Returns the match for the last unmatched opening RNA helix pair symbol
1393    * preceding the given column, or '(' if nothing found to match.
1394    *
1395    * @param column
1396    * @return
1397    */
 
1398  43 toggle public String getDefaultRnaHelixSymbol(int column)
1399    {
1400  43 String result = "(";
1401  43 if (annotations == null)
1402    {
1403  1 return result;
1404    }
1405   
1406    /*
1407    * for each preceding column, if it contains an open bracket,
1408    * count whether it is still unmatched at column, if so return its pair
1409    * (likely faster than the fancy alternative using stacks)
1410    */
1411  150 for (int col = column - 1; col >= 0; col--)
1412    {
1413  142 Annotation annotation = annotations[col];
1414  142 if (annotation == null)
1415    {
1416  66 continue;
1417    }
1418  76 String displayed = annotation.displayCharacter;
1419  76 if (displayed == null || displayed.length() != 1)
1420    {
1421  0 continue;
1422    }
1423  76 char symbol = displayed.charAt(0);
1424  76 if (!Rna.isOpeningParenthesis(symbol))
1425    {
1426  24 continue;
1427    }
1428   
1429    /*
1430    * found an opening bracket symbol
1431    * count (closing-opening) symbols of this type that follow it,
1432    * up to and excluding the target column; if the count is less
1433    * than 1, the opening bracket is unmatched, so return its match
1434    */
1435  52 String closer = String
1436    .valueOf(Rna.getMatchingClosingParenthesis(symbol));
1437  52 String opener = String.valueOf(symbol);
1438  52 int count = 0;
1439  256 for (int j = col + 1; j < column; j++)
1440    {
1441  204 if (annotations[j] != null)
1442    {
1443  90 String s = annotations[j].displayCharacter;
1444  90 if (closer.equals(s))
1445    {
1446  18 count++;
1447    }
1448  72 else if (opener.equals(s))
1449    {
1450  0 count--;
1451    }
1452    }
1453    }
1454  52 if (count < 1)
1455    {
1456  34 return closer;
1457    }
1458    }
1459  8 return result;
1460    }
1461   
 
1462  36418 toggle protected static synchronized long nextId()
1463    {
1464  36418 return counter++;
1465    }
1466   
1467    /**
1468    *
1469    * @return true for rows that have a range of values in their annotation set
1470    */
 
1471  5 toggle public boolean isQuantitative()
1472    {
1473  5 return graphMin < graphMax;
1474    }
1475   
1476    /**
1477    * delete any columns in alignmentAnnotation that are hidden (including
1478    * sequence associated annotation).
1479    *
1480    * @param hiddenColumns
1481    * the set of hidden columns
1482    */
 
1483  2 toggle public void makeVisibleAnnotation(HiddenColumns hiddenColumns)
1484    {
1485  2 if (annotations != null)
1486    {
1487  1 makeVisibleAnnotation(0, annotations.length, hiddenColumns);
1488    }
1489    }
1490   
1491    /**
1492    * delete any columns in alignmentAnnotation that are hidden (including
1493    * sequence associated annotation).
1494    *
1495    * @param start
1496    * remove any annotation to the right of this column
1497    * @param end
1498    * remove any annotation to the left of this column
1499    * @param hiddenColumns
1500    * the set of hidden columns
1501    */
 
1502  10 toggle public void makeVisibleAnnotation(int start, int end,
1503    HiddenColumns hiddenColumns)
1504    {
1505  10 if (annotations != null)
1506    {
1507  9 if (hiddenColumns.hasHiddenColumns())
1508    {
1509  3 removeHiddenAnnotation(start, end, hiddenColumns);
1510    }
1511    else
1512    {
1513  6 restrict(start, end);
1514    }
1515    }
1516    }
1517   
1518    /**
1519    * The actual implementation of deleting hidden annotation columns
1520    *
1521    * @param start
1522    * remove any annotation to the right of this column
1523    * @param end
1524    * remove any annotation to the left of this column
1525    * @param hiddenColumns
1526    * the set of hidden columns
1527    */
 
1528  3 toggle private void removeHiddenAnnotation(int start, int end,
1529    HiddenColumns hiddenColumns)
1530    {
1531    // mangle the alignmentAnnotation annotation array
1532  3 ArrayList<Annotation[]> annels = new ArrayList<>();
1533  3 Annotation[] els = null;
1534   
1535  3 int w = 0;
1536   
1537  3 Iterator<int[]> blocks = hiddenColumns.getVisContigsIterator(start,
1538    end + 1, false);
1539   
1540  3 int copylength;
1541  3 int annotationLength;
1542  9 while (blocks.hasNext())
1543    {
1544  6 int[] block = blocks.next();
1545  6 annotationLength = block[1] - block[0] + 1;
1546   
1547  6 if (blocks.hasNext())
1548    {
1549    // copy just the visible segment of the annotation row
1550  3 copylength = annotationLength;
1551    }
1552    else
1553    {
1554  3 if (annotationLength + block[0] <= annotations.length)
1555    {
1556    // copy just the visible segment of the annotation row
1557  2 copylength = annotationLength;
1558    }
1559    else
1560    {
1561    // copy to the end of the annotation row
1562  1 copylength = annotations.length - block[0];
1563    }
1564    }
1565   
1566  6 els = new Annotation[annotationLength];
1567  6 annels.add(els);
1568  6 System.arraycopy(annotations, block[0], els, 0, copylength);
1569  6 w += annotationLength;
1570    }
1571   
1572  3 if (w != 0)
1573    {
1574  3 annotations = new Annotation[w];
1575   
1576  3 w = 0;
1577  3 for (Annotation[] chnk : annels)
1578    {
1579  6 System.arraycopy(chnk, 0, annotations, w, chnk.length);
1580  6 w += chnk.length;
1581    }
1582    }
1583    }
1584   
 
1585  17 toggle public static Iterable<AlignmentAnnotation> findAnnotations(
1586    Iterable<AlignmentAnnotation> list, SequenceI seq, String calcId,
1587    String label)
1588    {
1589   
1590  17 ArrayList<AlignmentAnnotation> aa = new ArrayList<>();
1591  17 for (AlignmentAnnotation ann : list)
1592    {
1593  70 if ((calcId == null || (ann.getCalcId() != null
1594    && ann.getCalcId().equals(calcId)))
1595    && (seq == null || (ann.sequenceRef != null
1596    && ann.sequenceRef == seq))
1597    && (label == null
1598    || (ann.label != null && ann.label.equals(label))))
1599    {
1600  13 aa.add(ann);
1601    }
1602    }
1603  17 return aa;
1604    }
1605   
1606    /**
1607    * Answer true if any annotation matches the calcId passed in (if not null).
1608    *
1609    * @param list
1610    * annotation to search
1611    * @param calcId
1612    * @return
1613    */
 
1614  0 toggle public static boolean hasAnnotation(List<AlignmentAnnotation> list,
1615    String calcId)
1616    {
1617   
1618  0 if (calcId != null && !"".equals(calcId))
1619    {
1620  0 for (AlignmentAnnotation a : list)
1621    {
1622  0 if (a.getCalcId() == calcId)
1623    {
1624  0 return true;
1625    }
1626    }
1627    }
1628  0 return false;
1629    }
1630   
 
1631  223 toggle public static Iterable<AlignmentAnnotation> findAnnotation(
1632    List<AlignmentAnnotation> list, String calcId)
1633    {
1634   
1635  223 List<AlignmentAnnotation> aa = new ArrayList<>();
1636  223 if (calcId == null)
1637    {
1638  1 return aa;
1639    }
1640  222 for (AlignmentAnnotation a : list)
1641    {
1642   
1643  1647 if (a.getCalcId() == calcId || (a.getCalcId() != null
1644    && calcId != null && a.getCalcId().equals(calcId)))
1645    {
1646  489 aa.add(a);
1647    }
1648    }
1649  222 return aa;
1650    }
1651    }