Clover icon

Coverage Report

  1. Project Clover database Wed Dec 3 2025 13:28:34 GMT
  2. Package jalview.datamodel

File AlignmentAnnotation.java

 

Coverage histogram

../../img/srcFileCovDistChart8.png
21% of files have more coverage

Code metrics

300
484
71
2
1,882
1,245
327
0.68
6.82
35.5
4.61

Classes

Class Line # Actions
AlignmentAnnotation 46 473 310
0.7886473578.9%
AlignmentAnnotation.AnnotCharSequence 576 11 17
0.925925992.6%
 

Contributing tests

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