Clover icon

Coverage Report

  1. Project Clover database Tue Mar 10 2026 14:58:44 GMT
  2. Package jalview.datamodel

File AlignmentAnnotation.java

 

Coverage histogram

../../img/srcFileCovDistChart0.png
0% of files have more coverage

Code metrics

302
499
79
2
1,950
1,295
341
0.68
6.32
39.5
4.32

Classes

Class Line # Actions
AlignmentAnnotation 46 488 324
0.00%
AlignmentAnnotation.AnnotCharSequence 580 11 17
0.00%
 

Contributing tests

No tests hitting this source file were found.

Source view

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