Clover icon

Coverage Report

  1. Project Clover database Fri Nov 15 2024 13:56:46 GMT
  2. Package jalview.datamodel

File AlignmentAnnotation.java

 

Coverage histogram

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

Code metrics

296
470
61
2
1,799
1,191
314
0.67
7.7
30.5
5.15

Classes

Class Line # Actions
AlignmentAnnotation 45 459 297
0.7812578.1%
AlignmentAnnotation.AnnotCharSequence 548 11 17
0.925925992.6%
 

Contributing tests

This file is covered by 315 tests. .

Source view

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