Clover icon

Coverage Report

  1. Project Clover database Mon Sep 2 2024 17:57:51 BST
  2. Package jalview.datamodel

File AlignmentAnnotation.java

 

Coverage histogram

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

Code metrics

296
468
60
2
1,782
1,185
313
0.67
7.8
30
5.22

Classes

Class Line # Actions
AlignmentAnnotation 45 457 296
0.777917277.8%
AlignmentAnnotation.AnnotCharSequence 540 11 17
0.925925992.6%
 

Contributing tests

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