Clover icon

Coverage Report

  1. Project Clover database Fri Dec 6 2024 13:47:14 GMT
  2. Package jalview.io

File AnnotationFile.java

 

Coverage histogram

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

Code metrics

426
759
27
2
1,787
1,509
299
0.39
28.11
13.5
11.07

Classes

Class Line # Actions
AnnotationFile 50 755 298
0.771333977.1%
AnnotationFile.ViewDef 105 4 1
1.0100%
 

Contributing tests

This file is covered by 12 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.io;
22   
23    import java.awt.Color;
24    import java.io.BufferedReader;
25    import java.util.ArrayList;
26    import java.util.BitSet;
27    import java.util.Enumeration;
28    import java.util.HashMap;
29    import java.util.Hashtable;
30    import java.util.List;
31    import java.util.Map;
32    import java.util.StringTokenizer;
33    import java.util.Vector;
34   
35    import jalview.analysis.Conservation;
36    import jalview.api.AlignViewportI;
37    import jalview.datamodel.AlignmentAnnotation;
38    import jalview.datamodel.AlignmentI;
39    import jalview.datamodel.Annotation;
40    import jalview.datamodel.ColumnSelection;
41    import jalview.datamodel.GraphLine;
42    import jalview.datamodel.HiddenColumns;
43    import jalview.datamodel.HiddenSequences;
44    import jalview.datamodel.SequenceGroup;
45    import jalview.datamodel.SequenceI;
46    import jalview.schemes.ColourSchemeI;
47    import jalview.schemes.ColourSchemeProperty;
48    import jalview.util.ColorUtils;
49   
 
50    public class AnnotationFile
51    {
52    private static final String GRAPHLINE = "GRAPHLINE";
53   
54    private static final String COMBINE = "COMBINE";
55   
56    protected String newline = System.getProperty("line.separator");
57   
58    private StringBuffer text;
59   
60    private SequenceI refSeq = null;
61   
62    private String refSeqId = null;
63   
64    private long nlinesread = 0;
65   
66    private String lastread = "";
67   
68    /**
69    * Constructor
70    */
 
71  38 toggle public AnnotationFile()
72    {
73  38 init();
74    }
75   
 
76  38 toggle private void init()
77    {
78  38 text = new StringBuffer("JALVIEW_ANNOTATION" + newline + "# Created: "
79    + new java.util.Date() + newline + newline);
80  38 refSeq = null;
81  38 refSeqId = null;
82    }
83   
84    /**
85    * convenience method for pre-2.9 annotation files which have no view, hidden
86    * columns or hidden row keywords.
87    *
88    * @param annotations
89    * @param list
90    * @param properties
91    * @return annotation file as a string.
92    */
 
93  0 toggle public String printAnnotations(AlignmentAnnotation[] annotations,
94    List<SequenceGroup> list, Hashtable properties)
95    {
96  0 return printAnnotations(annotations, list, properties, null, null,
97    null);
98   
99    }
100   
101    /**
102    * hold all the information about a particular view definition read from or
103    * written out in an annotations file.
104    */
 
105    public class ViewDef
106    {
107    // TODO this class is not used - remove?
108    public final String viewname;
109   
110    public final HiddenSequences hidseqs;
111   
112    public final HiddenColumns hiddencols;
113   
114    public final Hashtable hiddenRepSeqs;
115   
 
116  5 toggle public ViewDef(String vname, HiddenSequences hseqs, HiddenColumns hcols,
117    Hashtable hRepSeqs)
118    {
119  5 this.viewname = vname;
120  5 this.hidseqs = hseqs;
121  5 this.hiddencols = hcols;
122  5 this.hiddenRepSeqs = hRepSeqs;
123    }
124    }
125   
126    /**
127    * Prepare an annotation file given a set of annotations, groups, alignment
128    * properties and views.
129    *
130    * @param annotations
131    * @param list
132    * @param properties
133    * @param views
134    * @return annotation file
135    */
 
136  5 toggle public String printAnnotations(AlignmentAnnotation[] annotations,
137    List<SequenceGroup> list, Hashtable properties, HiddenColumns cs,
138    AlignmentI al, ViewDef view)
139    {
140  5 if (view != null)
141    {
142  5 if (view.viewname != null)
143    {
144  0 text.append("VIEW_DEF\t" + view.viewname + "\n");
145    }
146  5 if (list == null)
147    {
148    // list = view.visibleGroups;
149    }
150  5 if (cs == null)
151    {
152  5 cs = view.hiddencols;
153    }
154  5 if (al == null)
155    {
156    // add hidden rep sequences.
157    }
158    }
159    // first target - store and restore all settings for a view.
160  5 if (al != null && al.hasSeqrep())
161    {
162  1 text.append("VIEW_SETREF\t" + al.getSeqrep().getName() + "\n");
163    }
164  5 if (cs != null && cs.hasHiddenColumns())
165    {
166  1 text.append("VIEW_HIDECOLS\t");
167   
168  1 String regions = cs.regionsToString(",", "-");
169  1 text.append(regions);
170  1 text.append("\n");
171    }
172    // TODO: allow efficient recovery of annotation data shown in several
173    // different views
174  5 if (annotations != null)
175    {
176  4 boolean oneColour = true;
177  4 AlignmentAnnotation row;
178  4 String comma;
179  4 SequenceI refSeq = null;
180  4 SequenceGroup refGroup = null;
181   
182  4 StringBuffer colours = new StringBuffer();
183  4 StringBuffer graphLine = new StringBuffer();
184  4 StringBuffer rowprops = new StringBuffer();
185  4 Hashtable<Integer, String> graphGroup = new Hashtable<>();
186  4 Hashtable<Integer, Object[]> graphGroup_refs = new Hashtable<>();
187  4 BitSet graphGroupSeen = new BitSet();
188   
189  4 java.awt.Color color;
190   
191  84 for (int i = 0; i < annotations.length; i++)
192    {
193  80 row = annotations[i];
194   
195  80 if (!row.visible && !row.hasScore() && !(row.graphGroup > -1
196    && graphGroupSeen.get(row.graphGroup)))
197    {
198  0 continue;
199    }
200   
201  80 color = null;
202  80 oneColour = true;
203   
204    // mark any sequence references for the row
205  80 writeSequence_Ref(refSeq, row.sequenceRef);
206  80 refSeq = row.sequenceRef;
207    // mark any group references for the row
208  80 writeGroup_Ref(refGroup, row.groupRef);
209  80 refGroup = row.groupRef;
210   
211  80 boolean hasGlyphs = row.hasIcons, hasLabels = row.hasText,
212    hasValues = row.hasScore, hasText = false;
213    // lookahead to check what the annotation row object actually contains.
214  12348 for (int j = 0; row.annotations != null
215    && j < row.annotations.length
216    && (!hasGlyphs || !hasLabels || !hasValues); j++)
217    {
218  12268 if (row.annotations[j] != null)
219    {
220  9156 hasLabels |= (row.annotations[j].displayCharacter != null
221    && row.annotations[j].displayCharacter.length() > 0
222    && !row.annotations[j].displayCharacter.equals(" "));
223  9156 hasGlyphs |= (row.annotations[j].secondaryStructure != 0
224    && row.annotations[j].secondaryStructure != ' ');
225  9156 hasValues |= (!Float.isNaN(row.annotations[j].value)); // NaNs can't
226    // be
227    // rendered..
228  9156 hasText |= (row.annotations[j].description != null
229    && row.annotations[j].description.length() > 0);
230    }
231    }
232   
233  80 if (row.graph == AlignmentAnnotation.NO_GRAPH)
234    {
235  4 text.append("NO_GRAPH\t");
236  4 hasValues = false; // only secondary structure
237    // hasLabels = false; // and annotation description string.
238    }
239    else
240    {
241  76 if (row.graph == AlignmentAnnotation.BAR_GRAPH)
242    {
243  10 text.append("BAR_GRAPH\t");
244  10 hasGlyphs = false; // no secondary structure
245   
246    }
247  66 else if (row.graph == AlignmentAnnotation.LINE_GRAPH)
248    {
249  66 hasGlyphs = false; // no secondary structure
250  66 text.append("LINE_GRAPH\t");
251    }
252   
253  76 if (row.getThreshold() != null)
254    {
255  62 graphLine.append("GRAPHLINE\t");
256  62 graphLine.append(row.label);
257  62 graphLine.append("\t");
258  62 graphLine.append(row.getThreshold().value);
259  62 graphLine.append("\t");
260  62 graphLine.append(row.getThreshold().label);
261  62 graphLine.append("\t");
262  62 graphLine.append(jalview.util.Format
263    .getHexString(row.getThreshold().colour));
264  62 graphLine.append(newline);
265    }
266   
267  76 if (row.graphGroup > -1)
268    {
269  66 graphGroupSeen.set(row.graphGroup);
270  66 Integer key = Integer.valueOf(row.graphGroup);
271  66 if (graphGroup.containsKey(key))
272    {
273  33 graphGroup.put(key, graphGroup.get(key) + "\t" + row.label);
274   
275    }
276    else
277    {
278  33 graphGroup_refs.put(key, new Object[] { refSeq, refGroup });
279  33 graphGroup.put(key, row.label);
280    }
281    }
282    }
283   
284  80 text.append(row.label + "\t");
285  80 if (row.description != null)
286    {
287  68 text.append(row.description + "\t");
288    }
289  12640 for (int j = 0; row.annotations != null
290    && j < row.annotations.length; j++)
291    {
292  12560 if (refSeq != null
293    && jalview.util.Comparison.isGap(refSeq.getCharAt(j)))
294    {
295  1462 continue;
296    }
297   
298  11098 if (row.annotations[j] != null)
299    {
300  9168 comma = "";
301  9168 if (hasGlyphs) // could be also hasGlyphs || ...
302    {
303   
304  32 text.append(comma);
305  32 if (row.annotations[j].secondaryStructure != ' ')
306    {
307    // only write out the field if its not whitespace.
308  32 text.append(row.annotations[j].secondaryStructure);
309    }
310  32 comma = ",";
311    }
312  9168 if (hasValues)
313    {
314  9136 if (!Float.isNaN(row.annotations[j].value))
315    {
316  9136 text.append(comma + row.annotations[j].value);
317    }
318    else
319    {
320    // jalview.bin.Console.errPrintln("Skipping NaN - not valid
321    // value.");
322  0 text.append(comma + 0f);// row.annotations[j].value);
323    }
324  9136 comma = ",";
325    }
326  9168 if (hasLabels)
327    {
328    // TODO: labels are emitted after values for bar graphs.
329  9150 if // empty labels are allowed, so
330  9150 (row.annotations[j].displayCharacter != null
331    && row.annotations[j].displayCharacter.length() > 0
332    && !row.annotations[j].displayCharacter.equals(" "))
333    {
334  362 text.append(comma + row.annotations[j].displayCharacter);
335  362 comma = ",";
336    }
337    }
338  9168 if (hasText)
339    {
340  9070 if (row.annotations[j].description != null
341    && row.annotations[j].description.length() > 0
342    && !row.annotations[j].description
343    .equals(row.annotations[j].displayCharacter))
344    {
345  9028 text.append(comma + row.annotations[j].description);
346  9028 comma = ",";
347    }
348    }
349  9168 if (color != null && !color.equals(row.annotations[j].colour))
350    {
351  450 oneColour = false;
352    }
353   
354  9168 color = row.annotations[j].colour;
355   
356  9168 if (row.annotations[j].colour != null
357    && row.annotations[j].colour != java.awt.Color.black)
358    {
359  8796 text.append(comma + "[" + jalview.util.Format
360    .getHexString(row.annotations[j].colour) + "]");
361  8796 comma = ",";
362    }
363    }
364  11098 text.append("|");
365    }
366   
367  80 if (row.hasScore())
368    {
369  0 text.append("\t" + row.score);
370    }
371   
372  80 text.append(newline);
373   
374  80 if (color != null && color != java.awt.Color.black && oneColour)
375    {
376  68 colours.append("COLOUR\t");
377  68 colours.append(row.label);
378  68 colours.append("\t");
379  68 colours.append(jalview.util.Format.getHexString(color));
380  68 colours.append(newline);
381    }
382  80 if (row.scaleColLabel || row.showAllColLabels
383    || row.centreColLabels)
384    {
385  0 rowprops.append("ROWPROPERTIES\t");
386  0 rowprops.append(row.label);
387  0 rowprops.append("\tscaletofit=");
388  0 rowprops.append(row.scaleColLabel);
389  0 rowprops.append("\tshowalllabs=");
390  0 rowprops.append(row.showAllColLabels);
391  0 rowprops.append("\tcentrelabs=");
392  0 rowprops.append(row.centreColLabels);
393  0 rowprops.append(newline);
394    }
395  80 if (graphLine.length() > 0)
396    {
397  62 text.append(graphLine.toString());
398  62 graphLine.setLength(0);
399    }
400    }
401   
402  4 text.append(newline);
403   
404  4 text.append(colours.toString());
405  4 if (graphGroup.size() > 0)
406    {
407  4 SequenceI oldRefSeq = refSeq;
408  4 SequenceGroup oldRefGroup = refGroup;
409  4 for (Map.Entry<Integer, String> combine_statement : graphGroup
410    .entrySet())
411    {
412  33 Object[] seqRefAndGroup = graphGroup_refs
413    .get(combine_statement.getKey());
414   
415  33 writeSequence_Ref(refSeq, (SequenceI) seqRefAndGroup[0]);
416  33 refSeq = (SequenceI) seqRefAndGroup[0];
417   
418  33 writeGroup_Ref(refGroup, (SequenceGroup) seqRefAndGroup[1]);
419  33 refGroup = (SequenceGroup) seqRefAndGroup[1];
420  33 text.append("COMBINE\t");
421  33 text.append(combine_statement.getValue());
422  33 text.append(newline);
423    }
424  4 writeSequence_Ref(refSeq, oldRefSeq);
425  4 refSeq = oldRefSeq;
426   
427  4 writeGroup_Ref(refGroup, oldRefGroup);
428  4 refGroup = oldRefGroup;
429    }
430  4 text.append(rowprops.toString());
431    }
432   
433  5 if (list != null)
434    {
435  5 printGroups(list);
436    }
437   
438  5 if (properties != null)
439    {
440  0 text.append(newline);
441  0 text.append(newline);
442  0 text.append("ALIGNMENT");
443  0 Enumeration en = properties.keys();
444  0 while (en.hasMoreElements())
445    {
446  0 String key = en.nextElement().toString();
447  0 text.append("\t");
448  0 text.append(key);
449  0 text.append("=");
450  0 text.append(properties.get(key));
451    }
452    // TODO: output alignment visualization settings here if required
453    // iterate through one or more views, defining, marking columns and rows
454    // as visible/hidden, and emmitting view properties.
455    // View specific annotation is
456    }
457   
458  5 return text.toString();
459    }
460   
 
461  117 toggle private Object writeGroup_Ref(SequenceGroup refGroup,
462    SequenceGroup next_refGroup)
463    {
464  117 if (next_refGroup == null)
465    {
466   
467  117 if (refGroup != null)
468    {
469  0 text.append(newline);
470  0 text.append("GROUP_REF\t");
471  0 text.append("ALIGNMENT");
472  0 text.append(newline);
473    }
474  117 return true;
475    }
476    else
477    {
478  0 if (refGroup == null || refGroup != next_refGroup)
479    {
480  0 text.append(newline);
481  0 text.append("GROUP_REF\t");
482  0 text.append(next_refGroup.getName());
483  0 text.append(newline);
484  0 return true;
485    }
486    }
487  0 return false;
488    }
489   
 
490  117 toggle private boolean writeSequence_Ref(SequenceI refSeq, SequenceI next_refSeq)
491    {
492   
493  117 if (next_refSeq == null)
494    {
495  6 if (refSeq != null)
496    {
497  0 text.append(newline);
498  0 text.append("SEQUENCE_REF\t");
499  0 text.append("ALIGNMENT");
500  0 text.append(newline);
501  0 return true;
502    }
503    }
504    else
505    {
506  111 if (refSeq == null || refSeq != next_refSeq)
507    {
508  62 text.append(newline);
509  62 text.append("SEQUENCE_REF\t");
510  62 text.append(next_refSeq.getName());
511  62 text.append(newline);
512  62 return true;
513    }
514    }
515  55 return false;
516    }
517   
 
518  5 toggle protected void printGroups(List<SequenceGroup> list)
519    {
520  5 SequenceI seqrep = null;
521  5 for (SequenceGroup sg : list)
522    {
523  6 if (!sg.hasSeqrep())
524    {
525  2 text.append("SEQUENCE_GROUP\t" + sg.getName() + "\t"
526    + (sg.getStartRes() + 1) + "\t" + (sg.getEndRes() + 1)
527    + "\t" + "-1\t");
528  2 seqrep = null;
529    }
530    else
531    {
532  4 seqrep = sg.getSeqrep();
533  4 text.append("SEQUENCE_REF\t");
534  4 text.append(seqrep.getName());
535  4 text.append(newline);
536  4 text.append("SEQUENCE_GROUP\t");
537  4 text.append(sg.getName());
538  4 text.append("\t");
539  4 text.append((seqrep.findPosition(sg.getStartRes())));
540  4 text.append("\t");
541  4 text.append((seqrep.findPosition(sg.getEndRes())));
542  4 text.append("\t");
543  4 text.append("-1\t");
544    }
545  47 for (int s = 0; s < sg.getSize(); s++)
546    {
547  41 text.append(sg.getSequenceAt(s).getName());
548  41 text.append("\t");
549    }
550  6 text.append(newline);
551  6 text.append("PROPERTIES\t");
552  6 text.append(sg.getName());
553  6 text.append("\t");
554   
555  6 if (sg.getDescription() != null)
556    {
557  2 text.append("description=");
558  2 text.append(sg.getDescription());
559  2 text.append("\t");
560    }
561  6 if (sg.cs != null)
562    {
563  6 text.append("colour=");
564  6 text.append(ColourSchemeProperty
565    .getColourName(sg.cs.getColourScheme()));
566  6 text.append("\t");
567  6 if (sg.cs.getThreshold() != 0)
568    {
569  0 text.append("pidThreshold=");
570  0 text.append(sg.cs.getThreshold());
571    }
572  6 if (sg.cs.isConsensusSecondaryStructureColouring())
573    {
574  0 text.append("secondaryStructureConservationThreshold=");
575  0 text.append(sg.cs.getConsensusSecondaryStructureThreshold());
576  0 text.append("\t");
577    }
578  6 if (sg.cs.conservationApplied())
579    {
580  0 text.append("consThreshold=");
581  0 text.append(sg.cs.getConservationInc());
582  0 text.append("\t");
583    }
584    }
585  6 text.append("outlineColour=");
586  6 text.append(jalview.util.Format.getHexString(sg.getOutlineColour()));
587  6 text.append("\t");
588   
589  6 text.append("displayBoxes=");
590  6 text.append(sg.getDisplayBoxes());
591  6 text.append("\t");
592  6 text.append("displayText=");
593  6 text.append(sg.getDisplayText());
594  6 text.append("\t");
595  6 text.append("colourText=");
596  6 text.append(sg.getColourText());
597  6 text.append("\t");
598  6 text.append("showUnconserved=");
599  6 text.append(sg.getShowNonconserved());
600  6 text.append("\t");
601  6 if (sg.textColour != java.awt.Color.black)
602    {
603  0 text.append("textCol1=");
604  0 text.append(jalview.util.Format.getHexString(sg.textColour));
605  0 text.append("\t");
606    }
607  6 if (sg.textColour2 != java.awt.Color.white)
608    {
609  2 text.append("textCol2=");
610  2 text.append(jalview.util.Format.getHexString(sg.textColour2));
611  2 text.append("\t");
612    }
613  6 if (sg.thresholdTextColour != 0)
614    {
615  0 text.append("textColThreshold=");
616  0 text.append(sg.thresholdTextColour);
617  0 text.append("\t");
618    }
619  6 if (sg.idColour != null)
620    {
621  0 text.append("idColour=");
622  0 text.append(jalview.util.Format.getHexString(sg.idColour));
623  0 text.append("\t");
624    }
625  6 if (sg.isHidereps())
626    {
627  0 text.append("hide=true\t");
628    }
629  6 if (sg.isHideCols())
630    {
631  0 text.append("hidecols=true\t");
632    }
633  6 if (seqrep != null)
634    {
635    // terminate the last line and clear the sequence ref for the group
636  4 text.append(newline);
637  4 text.append("SEQUENCE_REF");
638    }
639  6 text.append(newline);
640  6 text.append(newline);
641   
642    }
643    }
644   
 
645  8 toggle public boolean annotateAlignmentView(AlignViewportI viewport, Object file,
646    DataSourceType protocol)
647    {
648  8 ColumnSelection colSel = viewport.getColumnSelection();
649  8 HiddenColumns hidden = viewport.getAlignment().getHiddenColumns();
650  8 if (colSel == null)
651    {
652  0 colSel = new ColumnSelection();
653    }
654  8 if (hidden == null)
655    {
656  0 hidden = new HiddenColumns();
657    }
658  8 boolean rslt = readAnnotationFile(viewport.getAlignment(), hidden, file,
659    protocol);
660  8 if (rslt && (colSel.hasSelectedColumns() || hidden.hasHiddenColumns()))
661    {
662  0 viewport.setColumnSelection(colSel);
663  0 viewport.getAlignment().setHiddenColumns(hidden);
664    }
665   
666  8 return rslt;
667    }
668   
 
669  13 toggle public boolean readAnnotationFile(AlignmentI al, String file,
670    DataSourceType sourceType)
671    {
672  13 return readAnnotationFile(al, null, file, sourceType);
673    }
674   
 
675  26 toggle public boolean readAnnotationFile(AlignmentI al, HiddenColumns hidden,
676    Object file, DataSourceType sourceType)
677    {
678  26 BufferedReader in = null;
679  26 try
680    {
681  26 in = new FileParse().getBufferedReader(file, sourceType);
682  26 if (in != null)
683    {
684  26 return parseAnnotationFrom(al, hidden, in);
685    }
686    } catch (Exception ex)
687    {
688  0 ex.printStackTrace();
689  0 jalview.bin.Console
690    .outPrintln("Problem reading annotation file: " + ex);
691  0 if (nlinesread > 0)
692    {
693  0 jalview.bin.Console.outPrintln("Last read line " + nlinesread
694    + ": '" + lastread + "' (first 80 chars) ...");
695    }
696  0 return false;
697    }
698  0 return false;
699    }
700   
 
701  26 toggle public boolean parseAnnotationFrom(AlignmentI al, HiddenColumns hidden,
702    BufferedReader in) throws Exception
703    {
704  26 nlinesread = 0;
705  26 ArrayList<Object[]> combineAnnotation_calls = new ArrayList<>();
706  26 ArrayList<Object[]> deferredAnnotation_calls = new ArrayList<>();
707  26 boolean modified = false;
708  26 String groupRef = null;
709  26 Hashtable groupRefRows = new Hashtable();
710   
711  26 Hashtable autoAnnots = new Hashtable();
712    {
713  26 String line, label, description, token;
714  26 int graphStyle, index;
715  26 int refSeqIndex = 1;
716  26 int existingAnnotations = 0;
717    // when true - will add new rows regardless of whether they are duplicate
718    // auto-annotation like consensus or conservation graphs
719  26 boolean overrideAutoAnnot = false;
720  26 if (al.getAlignmentAnnotation() != null)
721    {
722  8 existingAnnotations = al.getAlignmentAnnotation().length;
723  8 if (existingAnnotations > 0)
724    {
725  8 AlignmentAnnotation[] aa = al.getAlignmentAnnotation();
726  40 for (int aai = 0; aai < aa.length; aai++)
727    {
728  32 if (aa[aai].autoCalculated)
729    {
730    // make a note of the name and description
731  32 autoAnnots.put(
732    autoAnnotsKey(aa[aai], aa[aai].sequenceRef,
733  32 (aa[aai].groupRef == null ? null
734    : aa[aai].groupRef.getName())),
735    Integer.valueOf(1));
736    }
737    }
738    }
739    }
740   
741  26 int alWidth = al.getWidth();
742   
743  26 StringTokenizer st;
744  26 Annotation[] annotations;
745  26 AlignmentAnnotation annotation = null;
746   
747    // First confirm this is an Annotation file
748  26 boolean jvAnnotationFile = false;
749  ? while ((line = in.readLine()) != null)
750    {
751  146 nlinesread++;
752  146 lastread = new String(line);
753  146 if (line.indexOf("#") == 0)
754    {
755  18 continue;
756    }
757   
758  128 if (line.indexOf("JALVIEW_ANNOTATION") > -1)
759    {
760  23 jvAnnotationFile = true;
761  23 break;
762    }
763    }
764   
765  26 if (!jvAnnotationFile)
766    {
767  3 in.close();
768  3 return false;
769    }
770   
771  ? while ((line = in.readLine()) != null)
772    {
773  1076 nlinesread++;
774  1076 lastread = new String(line);
775  1076 if (line.indexOf("#") == 0
776    || line.indexOf("JALVIEW_ANNOTATION") > -1
777    || line.length() == 0)
778    {
779  234 continue;
780    }
781   
782  842 st = new StringTokenizer(line, "\t");
783  842 token = st.nextToken();
784  842 if (token.equalsIgnoreCase("COLOUR"))
785    {
786    // TODO: use graduated colour def'n here too
787  172 colourAnnotations(al, st.nextToken(), st.nextToken());
788  172 modified = true;
789  172 continue;
790    }
791   
792  670 else if (token.equalsIgnoreCase(COMBINE))
793    {
794    // keep a record of current state and resolve groupRef at end
795  81 combineAnnotation_calls
796    .add(new Object[]
797    { st, refSeq, groupRef });
798  81 modified = true;
799  81 continue;
800    }
801  589 else if (token.equalsIgnoreCase("ROWPROPERTIES"))
802    {
803  8 addRowProperties(al, st);
804  8 modified = true;
805  8 continue;
806    }
807  581 else if (token.equalsIgnoreCase(GRAPHLINE))
808    {
809    // resolve at end
810  154 deferredAnnotation_calls
811    .add(new Object[]
812    { GRAPHLINE, st, refSeq, groupRef });
813  154 modified = true;
814  154 continue;
815    }
816   
817  427 else if (token.equalsIgnoreCase("SEQUENCE_REF"))
818    {
819  165 if (st.hasMoreTokens())
820    {
821  161 refSeq = al.findName(refSeqId = st.nextToken());
822  161 if (refSeq == null)
823    {
824  0 refSeqId = null;
825    }
826  161 try
827    {
828  161 refSeqIndex = Integer.parseInt(st.nextToken());
829  2 if (refSeqIndex < 1)
830    {
831  0 refSeqIndex = 1;
832  0 jalview.bin.Console.outPrintln(
833    "WARNING: SEQUENCE_REF index must be > 0 in AnnotationFile");
834    }
835    } catch (Exception ex)
836    {
837  159 refSeqIndex = 1;
838    }
839    }
840    else
841    {
842  4 refSeq = null;
843  4 refSeqId = null;
844    }
845  165 continue;
846    }
847  262 else if (token.equalsIgnoreCase("GROUP_REF"))
848    {
849    // Group references could be forward or backwards, so they are
850    // resolved after the whole file is read in
851  0 groupRef = null;
852  0 if (st.hasMoreTokens())
853    {
854  0 groupRef = st.nextToken();
855  0 if (groupRef.length() < 1)
856    {
857  0 groupRef = null; // empty string
858    }
859    else
860    {
861  0 if (groupRefRows.get(groupRef) == null)
862    {
863  0 groupRefRows.put(groupRef, new Vector());
864    }
865    }
866    }
867  0 continue;
868    }
869  262 else if (token.equalsIgnoreCase("SEQUENCE_GROUP"))
870    {
871  22 addGroup(al, st);
872  22 modified = true;
873  22 continue;
874    }
875   
876  240 else if (token.equalsIgnoreCase("PROPERTIES"))
877    {
878  18 addProperties(al, st);
879  18 modified = true;
880  18 continue;
881    }
882   
883  222 else if (token.equalsIgnoreCase("BELOW_ALIGNMENT"))
884    {
885  0 setBelowAlignment(al, st);
886  0 modified = true;
887  0 continue;
888    }
889  222 else if (token.equalsIgnoreCase("ALIGNMENT"))
890    {
891  5 addAlignmentDetails(al, st);
892  5 modified = true;
893  5 continue;
894    }
895    // else if (token.equalsIgnoreCase("VIEW_DEF"))
896    // {
897    // addOrSetView(al,st);
898    // modified = true;
899    // continue;
900    // }
901  217 else if (token.equalsIgnoreCase("VIEW_SETREF"))
902    {
903  2 if (refSeq != null)
904    {
905  1 al.setSeqrep(refSeq);
906    }
907  2 modified = true;
908  2 continue;
909    }
910  215 else if (token.equalsIgnoreCase("VIEW_HIDECOLS"))
911    {
912  1 if (st.hasMoreTokens())
913    {
914  1 if (hidden == null)
915    {
916  1 hidden = new HiddenColumns();
917    }
918  1 parseHideCols(hidden, st.nextToken());
919    }
920  1 modified = true;
921  1 continue;
922    }
923  214 else if (token.equalsIgnoreCase("HIDE_INSERTIONS"))
924    {
925  1 SequenceI sr = refSeq == null ? al.getSeqrep() : refSeq;
926  1 if (sr == null)
927    {
928  0 sr = al.getSequenceAt(0);
929    }
930  1 if (sr != null)
931    {
932  1 if (hidden == null)
933    {
934  0 jalview.bin.Console.errPrintln(
935    "Cannot process HIDE_INSERTIONS without an alignment view: Ignoring line: "
936    + line);
937    }
938    else
939    {
940    // consider deferring this till after the file has been parsed ?
941  1 hidden.hideList(sr.getInsertions());
942    }
943    }
944  1 modified = true;
945  1 continue;
946    }
947   
948    // Parse out the annotation row
949  213 graphStyle = AlignmentAnnotation.getGraphValueFromString(token);
950  213 label = st.nextToken();
951   
952  213 index = 0;
953  213 annotations = new Annotation[alWidth];
954  213 description = null;
955  213 float score = Float.NaN;
956   
957  213 if (st.hasMoreTokens())
958    {
959  213 line = st.nextToken();
960   
961  213 if (line.indexOf("|") == -1)
962    {
963  187 description = line;
964  187 if (st.hasMoreTokens())
965    {
966  187 line = st.nextToken();
967    }
968    }
969   
970  213 if (st.hasMoreTokens())
971    {
972    // This must be the score
973  12 score = Float.valueOf(st.nextToken()).floatValue();
974    }
975   
976  213 st = new StringTokenizer(line, "|", true);
977   
978  213 boolean emptyColumn = true;
979  213 boolean onlyOneElement = (st.countTokens() == 1);
980   
981  50121 while (st.hasMoreElements() && index < alWidth)
982    {
983  49908 token = st.nextToken().trim();
984   
985  49908 if (onlyOneElement)
986    {
987  0 try
988    {
989  0 score = Float.valueOf(token).floatValue();
990  0 break;
991    } catch (NumberFormatException ex)
992    {
993    }
994    }
995   
996  49908 if (token.equals("|"))
997    {
998  26769 if (emptyColumn)
999    {
1000  3662 index++;
1001    }
1002   
1003  26769 emptyColumn = true;
1004    }
1005    else
1006    {
1007  23139 annotations[index++] = parseAnnotation(token, graphStyle);
1008  23139 emptyColumn = false;
1009    }
1010    }
1011   
1012    }
1013   
1014  213 annotation = new AlignmentAnnotation(label, description,
1015  213 (index == 0) ? null : annotations, 0, 0, graphStyle);
1016   
1017  213 annotation.score = score;
1018  213 if (!overrideAutoAnnot && autoAnnots
1019    .containsKey(autoAnnotsKey(annotation, refSeq, groupRef)))
1020    {
1021    // skip - we've already got an automatic annotation of this type.
1022  3 continue;
1023    }
1024    // otherwise add it!
1025  210 if (refSeq != null)
1026    {
1027   
1028  181 annotation.belowAlignment = false;
1029    // make a copy of refSeq so we can find other matches in the alignment
1030  181 SequenceI referedSeq = refSeq;
1031  181 do
1032    {
1033    // copy before we do any mapping business.
1034    // TODO: verify that undo/redo with 1:many sequence associated
1035    // annotations can be undone correctly
1036  181 AlignmentAnnotation ann = new AlignmentAnnotation(annotation);
1037  181 annotation.createSequenceMapping(referedSeq, refSeqIndex,
1038    false);
1039  181 annotation.adjustForAlignment();
1040  181 referedSeq.addAlignmentAnnotation(annotation);
1041  181 al.addAnnotation(annotation);
1042  181 al.setAnnotationIndex(annotation,
1043    al.getAlignmentAnnotation().length - existingAnnotations
1044    - 1);
1045  181 if (groupRef != null)
1046    {
1047  0 ((Vector) groupRefRows.get(groupRef)).addElement(annotation);
1048    }
1049    // and recover our virgin copy to use again if necessary.
1050  181 annotation = ann;
1051   
1052  ? } while (refSeqId != null && (referedSeq = al.findName(referedSeq,
1053    refSeqId, true)) != null);
1054    }
1055    else
1056    {
1057  29 al.addAnnotation(annotation);
1058  29 al.setAnnotationIndex(annotation,
1059    al.getAlignmentAnnotation().length - existingAnnotations
1060    - 1);
1061  29 if (groupRef != null)
1062    {
1063  0 ((Vector) groupRefRows.get(groupRef)).addElement(annotation);
1064    }
1065    }
1066    // and set modification flag
1067  210 modified = true;
1068    }
1069    // Resolve the groupRefs
1070  23 Hashtable<String, SequenceGroup> groupRefLookup = new Hashtable<>();
1071  23 Enumeration en = groupRefRows.keys();
1072   
1073  23 while (en.hasMoreElements())
1074    {
1075  0 groupRef = (String) en.nextElement();
1076  0 boolean matched = false;
1077    // Resolve group: TODO: add a getGroupByName method to alignments
1078  0 for (SequenceGroup theGroup : al.getGroups())
1079    {
1080  0 if (theGroup.getName().equals(groupRef))
1081    {
1082  0 if (matched)
1083    {
1084    // TODO: specify and implement duplication of alignment annotation
1085    // for multiple group references.
1086  0 jalview.bin.Console.errPrintln(
1087    "Ignoring 1:many group reference mappings for group name '"
1088    + groupRef + "'");
1089    }
1090    else
1091    {
1092  0 matched = true;
1093  0 Vector rowset = (Vector) groupRefRows.get(groupRef);
1094  0 groupRefLookup.put(groupRef, theGroup);
1095  0 if (rowset != null && rowset.size() > 0)
1096    {
1097  0 AlignmentAnnotation alan = null;
1098  0 for (int elm = 0, elmSize = rowset
1099  0 .size(); elm < elmSize; elm++)
1100    {
1101  0 alan = (AlignmentAnnotation) rowset.elementAt(elm);
1102  0 alan.groupRef = theGroup;
1103    }
1104    }
1105    }
1106    }
1107    }
1108  0 ((Vector) groupRefRows.get(groupRef)).removeAllElements();
1109    }
1110    // process any deferred attribute settings for each context
1111  23 for (Object[] _deferred_args : deferredAnnotation_calls)
1112    {
1113  154 if (_deferred_args[0] == GRAPHLINE)
1114    {
1115  154 addLine(al, (StringTokenizer) _deferred_args[1], // st
1116    (SequenceI) _deferred_args[2], // refSeq
1117  154 (_deferred_args[3] == null) ? null
1118    : groupRefLookup.get(_deferred_args[3]) // the
1119    // reference
1120    // group, or
1121    // null
1122    );
1123    }
1124    }
1125   
1126    // finally, combine all the annotation rows within each context.
1127    /**
1128    * number of combine statements in this annotation file. Used to create
1129    * new groups for combined annotation graphs without disturbing existing
1130    * ones
1131    */
1132  23 int combinecount = 0;
1133  23 for (Object[] _combine_args : combineAnnotation_calls)
1134    {
1135  81 combineAnnotations(al, ++combinecount,
1136    (StringTokenizer) _combine_args[0], // st
1137    (SequenceI) _combine_args[1], // refSeq
1138  81 (_combine_args[2] == null) ? null
1139    : groupRefLookup.get(_combine_args[2]) // the reference
1140    // group,
1141    // or null
1142    );
1143    }
1144    }
1145  23 return modified;
1146    }
1147   
 
1148  1 toggle private void parseHideCols(HiddenColumns hidden, String nextToken)
1149    {
1150  1 StringTokenizer inval = new StringTokenizer(nextToken, ",");
1151  7 while (inval.hasMoreTokens())
1152    {
1153  6 String range = inval.nextToken().trim();
1154  6 int from, to = range.indexOf("-");
1155  6 if (to == -1)
1156    {
1157  0 from = to = Integer.parseInt(range);
1158  0 if (from >= 0)
1159    {
1160  0 hidden.hideColumns(from, to);
1161    }
1162    }
1163    else
1164    {
1165  6 from = Integer.parseInt(range.substring(0, to));
1166  6 if (to < range.length() - 1)
1167    {
1168  6 to = Integer.parseInt(range.substring(to + 1));
1169    }
1170    else
1171    {
1172  0 to = from;
1173    }
1174  6 if (from > 0 && to >= from)
1175    {
1176  6 hidden.hideColumns(from, to);
1177    }
1178    }
1179    }
1180    }
1181   
 
1182  245 toggle private Object autoAnnotsKey(AlignmentAnnotation annotation,
1183    SequenceI refSeq, String groupRef)
1184    {
1185  245 return annotation.graph + "\t" + annotation.label + "\t"
1186    + annotation.description + "\t"
1187  245 + (refSeq != null ? refSeq.getDisplayId(true) : "");
1188    }
1189   
 
1190  23139 toggle Annotation parseAnnotation(String string, int graphStyle)
1191    {
1192    // don't do the glyph test if we don't want secondary structure
1193  23139 boolean hasSymbols = (graphStyle == AlignmentAnnotation.NO_GRAPH);
1194  23139 String desc = null, displayChar = null;
1195  23139 char ss = ' '; // secondaryStructure
1196  23139 float value = 0;
1197  23139 boolean parsedValue = false, dcset = false;
1198   
1199    // find colour here
1200  23139 Color colour = null;
1201  23139 int i = string.indexOf("[");
1202  23139 int j = string.indexOf("]");
1203  23139 if (i > -1 && j > -1)
1204    {
1205  21882 colour = ColorUtils.parseColourString(string.substring(i + 1, j));
1206  21882 if (i > 0 && string.charAt(i - 1) == ',')
1207    {
1208    // clip the preceding comma as well
1209  21882 i--;
1210    }
1211  21882 string = string.substring(0, i) + string.substring(j + 1);
1212    }
1213   
1214  23139 StringTokenizer st = new StringTokenizer(string, ",", true);
1215  23139 String token;
1216  23139 boolean seenContent = false;
1217  23139 int pass = 0;
1218  94252 while (st.hasMoreTokens())
1219    {
1220  71113 pass++;
1221  71113 token = st.nextToken().trim();
1222  71113 if (token.equals(","))
1223    {
1224  23991 if (!seenContent && parsedValue && !dcset)
1225    {
1226    // allow the value below the bar/line to be empty
1227  0 dcset = true;
1228  0 displayChar = " ";
1229    }
1230  23991 seenContent = false;
1231  23991 continue;
1232    }
1233    else
1234    {
1235  47122 seenContent = true;
1236    }
1237   
1238  47122 if (!parsedValue)
1239    {
1240  23215 try
1241    {
1242  23215 displayChar = token;
1243    // foo
1244  23215 value = Float.valueOf(token).floatValue();
1245  22815 parsedValue = true;
1246  22815 continue;
1247    } catch (NumberFormatException ex)
1248    {
1249    }
1250    }
1251    else
1252    {
1253  23907 if (token.length() == 1)
1254    {
1255  1340 displayChar = token;
1256    }
1257    }
1258  24307 if (hasSymbols && (token.length() == 1
1259    && "()<>[]{}AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"
1260    .contains(token)))
1261    {
1262    // Either this character represents a helix or sheet
1263    // or an integer which can be displayed
1264  352 ss = token.charAt(0);
1265  352 if (displayChar.equals(token.substring(0, 1)))
1266    {
1267  352 displayChar = "";
1268    }
1269    }
1270  23955 else if (desc == null || (parsedValue && pass > 2))
1271    {
1272  23945 desc = token;
1273    }
1274   
1275    }
1276    // if (!dcset && string.charAt(string.length() - 1) == ',')
1277    // {
1278    // displayChar = " "; // empty display char symbol.
1279    // }
1280  23139 if (displayChar != null && desc != null && desc.length() == 1)
1281    {
1282  80 if (displayChar.length() > 1)
1283    {
1284    // switch desc and displayChar - legacy support
1285  0 String tmp = displayChar;
1286  0 displayChar = desc;
1287  0 desc = tmp;
1288    }
1289    else
1290    {
1291  80 if (displayChar.equals(desc))
1292    {
1293    // duplicate label - hangover from the 'robust parser' above
1294  80 desc = null;
1295    }
1296    }
1297    }
1298  23139 Annotation anot = new Annotation(displayChar, desc, ss, value);
1299   
1300  23139 anot.colour = colour;
1301   
1302  23139 return anot;
1303    }
1304   
 
1305  172 toggle void colourAnnotations(AlignmentI al, String label, String colour)
1306    {
1307  172 Color awtColour = ColorUtils.parseColourString(colour);
1308  172 Annotation[] annotations;
1309  5322 for (int i = 0; i < al.getAlignmentAnnotation().length; i++)
1310    {
1311  5150 if (al.getAlignmentAnnotation()[i].label.equalsIgnoreCase(label))
1312    {
1313  2270 annotations = al.getAlignmentAnnotation()[i].annotations;
1314  358660 for (int j = 0; j < annotations.length; j++)
1315    {
1316  356390 if (annotations[j] != null)
1317    {
1318  303946 annotations[j].colour = awtColour;
1319    }
1320    }
1321    }
1322    }
1323    }
1324   
 
1325  81 toggle void combineAnnotations(AlignmentI al, int combineCount,
1326    StringTokenizer st, SequenceI seqRef, SequenceGroup groupRef)
1327    {
1328  81 String group = st.nextToken();
1329    // First make sure we are not overwriting the graphIndex
1330  81 int graphGroup = 0;
1331  81 if (al.getAlignmentAnnotation() != null)
1332    {
1333  1321 for (int i = 0; i < al.getAlignmentAnnotation().length; i++)
1334    {
1335  1321 AlignmentAnnotation aa = al.getAlignmentAnnotation()[i];
1336   
1337  1321 if (aa.graphGroup > graphGroup)
1338    {
1339    // try to number graphGroups in order of occurence.
1340  113 graphGroup = aa.graphGroup + 1;
1341    }
1342  1321 if (aa.sequenceRef == seqRef && aa.groupRef == groupRef
1343    && aa.label.equalsIgnoreCase(group))
1344    {
1345  81 if (aa.graphGroup > -1)
1346    {
1347  0 graphGroup = aa.graphGroup;
1348    }
1349    else
1350    {
1351  81 if (graphGroup <= combineCount)
1352    {
1353  66 graphGroup = combineCount + 1;
1354    }
1355  81 aa.graphGroup = graphGroup;
1356    }
1357  81 break;
1358    }
1359    }
1360   
1361    // Now update groups
1362  162 while (st.hasMoreTokens())
1363    {
1364  81 group = st.nextToken();
1365  1402 for (int i = 0; i < al.getAlignmentAnnotation().length; i++)
1366    {
1367  1402 AlignmentAnnotation aa = al.getAlignmentAnnotation()[i];
1368  1402 if (aa.sequenceRef == seqRef && aa.groupRef == groupRef
1369    && aa.label.equalsIgnoreCase(group))
1370    {
1371  81 aa.graphGroup = graphGroup;
1372  81 break;
1373    }
1374    }
1375    }
1376    }
1377    else
1378    {
1379  0 jalview.bin.Console.errPrintln(
1380    "Couldn't combine annotations. None are added to alignment yet!");
1381    }
1382    }
1383   
 
1384  154 toggle void addLine(AlignmentI al, StringTokenizer st, SequenceI seqRef,
1385    SequenceGroup groupRef)
1386    {
1387  154 String group = st.nextToken();
1388  154 AlignmentAnnotation[] alannot = al.getAlignmentAnnotation();
1389  154 String nextToken = st.nextToken();
1390  154 float value = 0f;
1391  154 try
1392    {
1393  154 value = Float.valueOf(nextToken);
1394    } catch (NumberFormatException e)
1395    {
1396  0 jalview.bin.Console.errPrintln("line " + nlinesread + ": Threshold '"
1397    + nextToken + "' invalid, setting to zero");
1398    }
1399  154 String label = st.hasMoreTokens() ? st.nextToken() : null;
1400  154 Color colour = null;
1401  154 if (st.hasMoreTokens())
1402    {
1403  154 colour = ColorUtils.parseColourString(st.nextToken());
1404    }
1405  154 if (alannot != null)
1406    {
1407  5162 for (int i = 0; i < alannot.length; i++)
1408    {
1409  5008 if (alannot[i].label.equalsIgnoreCase(group)
1410    && (seqRef == null || alannot[i].sequenceRef == seqRef)
1411    && (groupRef == null || alannot[i].groupRef == groupRef))
1412    {
1413  154 alannot[i].setThreshold(new GraphLine(value, label, colour));
1414    }
1415    }
1416    }
1417    }
1418   
 
1419  22 toggle void addGroup(AlignmentI al, StringTokenizer st)
1420    {
1421  22 SequenceGroup sg = new SequenceGroup();
1422  22 sg.setName(st.nextToken());
1423  22 String rng = "";
1424  22 try
1425    {
1426  22 rng = st.nextToken();
1427  22 if (rng.length() > 0 && !rng.startsWith("*"))
1428    {
1429  19 sg.setStartRes(Integer.parseInt(rng) - 1);
1430    }
1431    else
1432    {
1433  3 sg.setStartRes(0);
1434    }
1435  22 rng = st.nextToken();
1436  22 if (rng.length() > 0 && !rng.startsWith("*"))
1437    {
1438  19 sg.setEndRes(Integer.parseInt(rng) - 1);
1439    }
1440    else
1441    {
1442  3 sg.setEndRes(al.getWidth() - 1);
1443    }
1444    } catch (Exception e)
1445    {
1446  0 jalview.bin.Console.errPrintln(
1447    "Couldn't parse Group Start or End Field as '*' or a valid column or sequence index: '"
1448    + rng + "' - assuming alignment width for group.");
1449    // assume group is full width
1450  0 sg.setStartRes(0);
1451  0 sg.setEndRes(al.getWidth() - 1);
1452    }
1453   
1454  22 String index = st.nextToken();
1455  22 if (index.equals("-1"))
1456    {
1457  99 while (st.hasMoreElements())
1458    {
1459  84 sg.addSequence(al.findName(st.nextToken()), false);
1460    }
1461    }
1462    else
1463    {
1464  7 StringTokenizer st2 = new StringTokenizer(index, ",");
1465   
1466  14 while (st2.hasMoreTokens())
1467    {
1468  7 String tmp = st2.nextToken();
1469  7 if (tmp.equals("*"))
1470    {
1471  32 for (int i = 0; i < al.getHeight(); i++)
1472    {
1473  30 sg.addSequence(al.getSequenceAt(i), false);
1474    }
1475    }
1476  5 else if (tmp.indexOf("-") >= 0)
1477    {
1478  2 StringTokenizer st3 = new StringTokenizer(tmp, "-");
1479   
1480  2 int start = (Integer.parseInt(st3.nextToken()));
1481  2 int end = (Integer.parseInt(st3.nextToken()));
1482   
1483  2 if (end > start)
1484    {
1485  10 for (int i = start; i <= end; i++)
1486    {
1487  8 sg.addSequence(al.getSequenceAt(i - 1), false);
1488    }
1489    }
1490    }
1491    else
1492    {
1493  3 sg.addSequence(al.getSequenceAt(Integer.parseInt(tmp) - 1),
1494    false);
1495    }
1496    }
1497    }
1498   
1499  22 if (refSeq != null)
1500    {
1501  10 sg.setStartRes(refSeq.findIndex(sg.getStartRes() + 1) - 1);
1502  10 sg.setEndRes(refSeq.findIndex(sg.getEndRes() + 1) - 1);
1503  10 sg.setSeqrep(refSeq);
1504    }
1505   
1506  22 if (sg.getSize() > 0)
1507    {
1508  20 al.addGroup(sg);
1509    }
1510    }
1511   
 
1512  8 toggle void addRowProperties(AlignmentI al, StringTokenizer st)
1513    {
1514  8 String label = st.nextToken(), keyValue, key, value;
1515  8 boolean scaletofit = false, centerlab = false, showalllabs = false;
1516  32 while (st.hasMoreTokens())
1517    {
1518  24 keyValue = st.nextToken();
1519  24 key = keyValue.substring(0, keyValue.indexOf("="));
1520  24 value = keyValue.substring(keyValue.indexOf("=") + 1);
1521  24 if (key.equalsIgnoreCase("scaletofit"))
1522    {
1523  8 scaletofit = Boolean.valueOf(value).booleanValue();
1524    }
1525  24 if (key.equalsIgnoreCase("showalllabs"))
1526    {
1527  8 showalllabs = Boolean.valueOf(value).booleanValue();
1528    }
1529  24 if (key.equalsIgnoreCase("centrelabs"))
1530    {
1531  8 centerlab = Boolean.valueOf(value).booleanValue();
1532    }
1533  24 AlignmentAnnotation[] alr = al.getAlignmentAnnotation();
1534  24 if (alr != null)
1535    {
1536  48 for (int i = 0; i < alr.length; i++)
1537    {
1538  24 if (alr[i].label.equalsIgnoreCase(label))
1539    {
1540  24 alr[i].centreColLabels = centerlab;
1541  24 alr[i].scaleColLabel = scaletofit;
1542  24 alr[i].showAllColLabels = showalllabs;
1543    }
1544    }
1545    }
1546    }
1547    }
1548   
 
1549  18 toggle void addProperties(AlignmentI al, StringTokenizer st)
1550    {
1551   
1552    // So far we have only added groups to the annotationHash,
1553    // the idea is in the future properties can be added to
1554    // alignments, other annotations etc
1555  18 if (al.getGroups() == null)
1556    {
1557  0 return;
1558    }
1559   
1560  18 String name = st.nextToken();
1561   
1562  18 Map<String, String> properties = new HashMap<>();
1563  120 while (st.hasMoreTokens())
1564    {
1565  102 String keyValue = st.nextToken();
1566  102 String key = keyValue.substring(0, keyValue.indexOf("="));
1567  102 String value = keyValue.substring(keyValue.indexOf("=") + 1);
1568  102 properties.put(key, value);
1569    }
1570   
1571  18 for (SequenceGroup sg : al.getGroups())
1572    {
1573  39 if (sg.getName().equals(name))
1574    {
1575  17 addProperties(sg, properties, al);
1576    }
1577    }
1578    }
1579   
1580    /**
1581    * Helper method that applies any specified properties to a SequenceGroup
1582    *
1583    * @param sg
1584    * @param properties
1585    * @param al
1586    */
 
1587  17 toggle private void addProperties(SequenceGroup sg,
1588    Map<String, String> properties, AlignmentI al)
1589    {
1590  17 ColourSchemeI def = sg.getColourScheme();
1591  17 for (String key : properties.keySet())
1592    {
1593  102 String value = properties.get(key);
1594  102 if (key.equalsIgnoreCase("description"))
1595    {
1596  4 sg.setDescription(value);
1597    }
1598  98 else if (key.equalsIgnoreCase("colour"))
1599    {
1600    // TODO need to notify colourscheme of view reference once it is
1601    // available
1602  9 sg.cs.setColourScheme(
1603    ColourSchemeProperty.getColourScheme(null, al, value));
1604    }
1605  89 else if (key.equalsIgnoreCase("pidThreshold"))
1606    {
1607  2 sg.cs.setThreshold(Integer.parseInt(value), true);
1608   
1609    }
1610  87 else if (key
1611    .equalsIgnoreCase("secondaryStructureConservationThreshold"))
1612    {
1613  0 sg.cs.setConsensusSecondaryStructureThreshold(
1614    Integer.parseInt(value));
1615  0 sg.cs.setConsensusSecondaryStructureColouring(true);
1616   
1617    }
1618  87 else if (key.equalsIgnoreCase("consThreshold"))
1619    {
1620  0 sg.cs.setConservationInc(Integer.parseInt(value));
1621  0 Conservation c = new Conservation("Group", sg.getSequences(null),
1622    sg.getStartRes(), sg.getEndRes() + 1);
1623   
1624  0 c.calculate();
1625  0 c.verdict(false, 25); // TODO: refer to conservation percent threshold
1626   
1627  0 sg.cs.setConservation(c);
1628   
1629    }
1630  87 else if (key.equalsIgnoreCase("outlineColour"))
1631    {
1632  17 sg.setOutlineColour(ColorUtils.parseColourString(value));
1633    }
1634  70 else if (key.equalsIgnoreCase("displayBoxes"))
1635    {
1636  13 sg.setDisplayBoxes(Boolean.valueOf(value).booleanValue());
1637    }
1638  57 else if (key.equalsIgnoreCase("showUnconserved"))
1639    {
1640  11 sg.setShowNonconserved(Boolean.valueOf(value).booleanValue());
1641    }
1642  46 else if (key.equalsIgnoreCase("displayText"))
1643    {
1644  13 sg.setDisplayText(Boolean.valueOf(value).booleanValue());
1645    }
1646  33 else if (key.equalsIgnoreCase("colourText"))
1647    {
1648  13 sg.setColourText(Boolean.valueOf(value).booleanValue());
1649    }
1650  20 else if (key.equalsIgnoreCase("textCol1"))
1651    {
1652  7 sg.textColour = ColorUtils.parseColourString(value);
1653    }
1654  13 else if (key.equalsIgnoreCase("textCol2"))
1655    {
1656  9 sg.textColour2 = ColorUtils.parseColourString(value);
1657    }
1658  4 else if (key.equalsIgnoreCase("textColThreshold"))
1659    {
1660  2 sg.thresholdTextColour = Integer.parseInt(value);
1661    }
1662  2 else if (key.equalsIgnoreCase("idColour"))
1663    {
1664  2 Color idColour = ColorUtils.parseColourString(value);
1665  2 sg.setIdColour(idColour == null ? Color.black : idColour);
1666    }
1667  0 else if (key.equalsIgnoreCase("hide"))
1668    {
1669    // see bug https://mantis.lifesci.dundee.ac.uk/view.php?id=25847
1670  0 sg.setHidereps(true);
1671    }
1672  0 else if (key.equalsIgnoreCase("hidecols"))
1673    {
1674    // see bug https://mantis.lifesci.dundee.ac.uk/view.php?id=25847
1675  0 sg.setHideCols(true);
1676    }
1677  102 sg.recalcConservation();
1678    }
1679   
1680  17 if (sg.getColourScheme() == null)
1681    {
1682  13 sg.setColourScheme(def);
1683    }
1684    }
1685   
 
1686  0 toggle void setBelowAlignment(AlignmentI al, StringTokenizer st)
1687    {
1688  0 String token;
1689  0 AlignmentAnnotation aa, ala[] = al.getAlignmentAnnotation();
1690  0 if (ala == null)
1691    {
1692  0 System.err.print(
1693    "Warning - no annotation to set below for sequence associated annotation:");
1694    }
1695  0 while (st.hasMoreTokens())
1696    {
1697  0 token = st.nextToken();
1698  0 if (ala == null)
1699    {
1700  0 System.err.print(" " + token);
1701    }
1702    else
1703    {
1704  0 for (int i = 0; i < al.getAlignmentAnnotation().length; i++)
1705    {
1706  0 aa = al.getAlignmentAnnotation()[i];
1707  0 if (aa.sequenceRef == refSeq && aa.label.equals(token))
1708    {
1709  0 aa.belowAlignment = true;
1710    }
1711    }
1712    }
1713    }
1714  0 if (ala == null)
1715    {
1716  0 System.err.print("\n");
1717    }
1718    }
1719   
 
1720  5 toggle void addAlignmentDetails(AlignmentI al, StringTokenizer st)
1721    {
1722  5 String keyValue, key, value;
1723  15 while (st.hasMoreTokens())
1724    {
1725  10 keyValue = st.nextToken();
1726  10 key = keyValue.substring(0, keyValue.indexOf("="));
1727  10 value = keyValue.substring(keyValue.indexOf("=") + 1);
1728  10 al.setProperty(key, value);
1729    }
1730    }
1731   
1732    /**
1733    * Write annotations as a CSV file of the form 'label, value, value, ...' for
1734    * each row.
1735    *
1736    * @param annotations
1737    * @return CSV file as a string.
1738    */
 
1739  2 toggle public String printCSVAnnotations(AlignmentAnnotation[] annotations)
1740    {
1741  2 if (annotations == null)
1742    {
1743  0 return "";
1744    }
1745  2 StringBuffer sp = new StringBuffer();
1746  7 for (int i = 0; i < annotations.length; i++)
1747    {
1748  5 String atos = annotations[i].toString();
1749  5 int p = 0;
1750  5 do
1751    {
1752  6 int cp = atos.indexOf("\n", p);
1753  6 sp.append(annotations[i].label);
1754  6 sp.append(",");
1755  6 if (cp > p)
1756    {
1757  1 sp.append(atos.substring(p, cp + 1));
1758    }
1759    else
1760    {
1761  5 sp.append(atos.substring(p));
1762  5 sp.append(newline);
1763    }
1764  6 p = cp + 1;
1765  6 } while (p > 0);
1766    }
1767  2 return sp.toString();
1768    }
1769   
 
1770  0 toggle public String printAnnotationsForView(AlignViewportI viewport)
1771    {
1772  0 return printAnnotations(
1773  0 viewport.isShowAnnotation()
1774    ? viewport.getAlignment().getAlignmentAnnotation()
1775    : null,
1776    viewport.getAlignment().getGroups(),
1777    viewport.getAlignment().getProperties(),
1778    viewport.getAlignment().getHiddenColumns(),
1779    viewport.getAlignment(), null);
1780    }
1781   
 
1782  0 toggle public String printAnnotationsForAlignment(AlignmentI al)
1783    {
1784  0 return printAnnotations(al.getAlignmentAnnotation(), al.getGroups(),
1785    al.getProperties(), null, al, null);
1786    }
1787    }