next attempt for Bug #36198 - appears to work reproducibly
[jalview.git] / src / jalview / io / AnnotationFile.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)\r
3  * Copyright (C) 2009 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
4  * \r
5  * This program is free software; you can redistribute it and/or\r
6  * modify it under the terms of the GNU General Public License\r
7  * as published by the Free Software Foundation; either version 2\r
8  * of the License, or (at your option) any later version.\r
9  * \r
10  * This program is distributed in the hope that it will be useful,\r
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13  * GNU General Public License for more details.\r
14  * \r
15  * You should have received a copy of the GNU General Public License\r
16  * along with this program; if not, write to the Free Software\r
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
18  */\r
19 package jalview.io;\r
20 \r
21 import java.io.*;\r
22 import java.net.*;\r
23 import java.util.*;\r
24 \r
25 import jalview.analysis.*;\r
26 import jalview.datamodel.*;\r
27 import jalview.schemes.*;\r
28 \r
29 public class AnnotationFile\r
30 {\r
31   StringBuffer text = new StringBuffer("JALVIEW_ANNOTATION\n"\r
32           + "# Created: " + new java.util.Date() + "\n\n");\r
33 \r
34   /**\r
35    * convenience method for pre-2.4 feature files which have no view, hidden\r
36    * columns or hidden row keywords.\r
37    * \r
38    * @param annotations\r
39    * @param groups\r
40    * @param properties\r
41    * @return feature file as a string.\r
42    */\r
43   public String printAnnotations(AlignmentAnnotation[] annotations,\r
44           Vector groups, Hashtable properties)\r
45   {\r
46     return printAnnotations(annotations, groups, properties, null);\r
47 \r
48   }\r
49 \r
50   /**\r
51    * hold all the information about a particular view definition read from or\r
52    * written out in an annotations file.\r
53    */\r
54   public class ViewDef\r
55   {\r
56     public String viewname;\r
57 \r
58     public HiddenSequences hidseqs;\r
59 \r
60     public ColumnSelection hiddencols;\r
61 \r
62     public Vector visibleGroups;\r
63 \r
64     public Hashtable hiddenRepSeqs;\r
65 \r
66     public ViewDef(String viewname, HiddenSequences hidseqs,\r
67             ColumnSelection hiddencols, Hashtable hiddenRepSeqs)\r
68     {\r
69       this.viewname = viewname;\r
70       this.hidseqs = hidseqs;\r
71       this.hiddencols = hiddencols;\r
72       this.hiddenRepSeqs = hiddenRepSeqs;\r
73     }\r
74   }\r
75   /**\r
76    * Prepare an annotation file given a set of annotations, groups, alignment properties and views. \r
77    * @param annotations\r
78    * @param groups\r
79    * @param properties\r
80    * @param views\r
81    * @return annotation file\r
82    */\r
83   public String printAnnotations(AlignmentAnnotation[] annotations,\r
84           Vector groups, Hashtable properties, ViewDef[] views)\r
85   {\r
86     // TODO: resolve views issue : annotationFile could contain visible region, or full data + hidden region specifications for a view.\r
87     if (annotations != null)\r
88     {\r
89       boolean oneColour = true;\r
90       AlignmentAnnotation row;\r
91       String comma;\r
92       SequenceI refSeq = null;\r
93 \r
94       StringBuffer colours = new StringBuffer();\r
95       StringBuffer graphLine = new StringBuffer();\r
96       StringBuffer rowprops = new StringBuffer();\r
97       Hashtable graphGroup = new Hashtable();\r
98 \r
99       java.awt.Color color;\r
100 \r
101       for (int i = 0; i < annotations.length; i++)\r
102       {\r
103         row = annotations[i];\r
104 \r
105         if (!row.visible && !row.hasScore())\r
106         {\r
107           continue;\r
108         }\r
109 \r
110         color = null;\r
111         oneColour = true;\r
112 \r
113         if (row.sequenceRef == null)\r
114         {\r
115           if (refSeq != null)\r
116           {\r
117             text.append("\nSEQUENCE_REF\tALIGNMENT\n");\r
118           }\r
119 \r
120           refSeq = null;\r
121         }\r
122 \r
123         else if (refSeq == null || refSeq != row.sequenceRef)\r
124         {\r
125           refSeq = row.sequenceRef;\r
126           text.append("\nSEQUENCE_REF\t" + refSeq.getName() + "\n");\r
127         }\r
128         boolean hasGlyphs = row.hasIcons, hasLabels = row.hasText, hasValues = row.hasScore, hasText = false;\r
129         // lookahead to check what the annotation row object actually contains.\r
130         for (int j = 0; row.annotations != null\r
131                 && j < row.annotations.length\r
132                 && (!hasGlyphs || !hasLabels || !hasValues); j++)\r
133         {\r
134           if (row.annotations[j] != null)\r
135           {\r
136             hasLabels |= (row.annotations[j].displayCharacter != null\r
137                     && row.annotations[j].displayCharacter.length() > 0 && !row.annotations[j].displayCharacter\r
138                     .equals(" "));\r
139             hasGlyphs |= (row.annotations[j].secondaryStructure != 0 && row.annotations[j].secondaryStructure != ' ');\r
140             hasValues |= (row.annotations[j].value != Float.NaN); // NaNs can't\r
141                                                                   // be\r
142                                                                   // rendered..\r
143             hasText |= (row.annotations[j].description != null && row.annotations[j].description\r
144                     .length() > 0);\r
145           }\r
146         }\r
147 \r
148         if (row.graph == AlignmentAnnotation.NO_GRAPH)\r
149         {\r
150           text.append("NO_GRAPH\t");\r
151           hasValues = false; // only secondary structure\r
152           // hasLabels = false; // and annotation description string.\r
153         }\r
154         else\r
155         {\r
156           if (row.graph == AlignmentAnnotation.BAR_GRAPH)\r
157           {\r
158             text.append("BAR_GRAPH\t");\r
159             hasGlyphs = false; // no secondary structure\r
160 \r
161           }\r
162           else if (row.graph == AlignmentAnnotation.LINE_GRAPH)\r
163           {\r
164             hasGlyphs = false; // no secondary structure\r
165             text.append("LINE_GRAPH\t");\r
166           }\r
167 \r
168           if (row.getThreshold() != null)\r
169           {\r
170             graphLine\r
171                     .append("GRAPHLINE\t"\r
172                             + row.label\r
173                             + "\t"\r
174                             + row.getThreshold().value\r
175                             + "\t"\r
176                             + row.getThreshold().label\r
177                             + "\t"\r
178                             + jalview.util.Format.getHexString(row\r
179                                     .getThreshold().colour) + "\n");\r
180           }\r
181 \r
182           if (row.graphGroup > -1)\r
183           {\r
184             String key = String.valueOf(row.graphGroup);\r
185             if (graphGroup.containsKey(key))\r
186             {\r
187               graphGroup.put(key, graphGroup.get(key) + "\t" + row.label);\r
188             }\r
189             else\r
190             {\r
191               graphGroup.put(key, row.label);\r
192             }\r
193           }\r
194         }\r
195 \r
196         text.append(row.label + "\t");\r
197         if (row.description != null)\r
198         {\r
199           text.append(row.description + "\t");\r
200         }\r
201         for (int j = 0; row.annotations != null\r
202                 && j < row.annotations.length; j++)\r
203         {\r
204           if (refSeq != null\r
205                   && jalview.util.Comparison.isGap(refSeq.getCharAt(j)))\r
206           {\r
207             continue;\r
208           }\r
209 \r
210           if (row.annotations[j] != null)\r
211           {\r
212             comma = "";\r
213             if (hasGlyphs) // could be also hasGlyphs || ... \r
214             {\r
215 \r
216               text.append(comma);\r
217               if (row.annotations[j].secondaryStructure != ' ')\r
218               {\r
219                 // only write out the field if its not whitespace.\r
220                 text.append(row.annotations[j].secondaryStructure);\r
221               }\r
222               comma = ",";\r
223             }\r
224             if (hasValues)\r
225             {\r
226               if (row.annotations[j].value != Float.NaN)\r
227               {\r
228                 text.append(comma + row.annotations[j].value);\r
229               }\r
230               else\r
231               {\r
232                 System.err.println("Skipping NaN - not valid value.");\r
233                 text.append(comma + 0f);// row.annotations[j].value);\r
234               }\r
235               comma = ",";\r
236             }\r
237             if (hasLabels)\r
238             {\r
239               // TODO: labels are emitted after values for bar graphs.\r
240               if // empty labels are allowed, so\r
241               (row.annotations[j].displayCharacter != null\r
242                       && row.annotations[j].displayCharacter.length() > 0\r
243                       && !row.annotations[j].displayCharacter.equals(" "))\r
244               {\r
245                 text.append(comma + row.annotations[j].displayCharacter);\r
246                 comma = ",";\r
247               }\r
248             }\r
249             if (hasText)\r
250             {\r
251               if (row.annotations[j].description != null\r
252                       && row.annotations[j].description.length() > 0\r
253                       && !row.annotations[j].description\r
254                               .equals(row.annotations[j].displayCharacter))\r
255               {\r
256                 text.append(comma + row.annotations[j].description);\r
257                 comma = ",";\r
258               }\r
259             }\r
260             if (color != null && !color.equals(row.annotations[j].colour))\r
261             {\r
262               oneColour = false;\r
263             }\r
264 \r
265             color = row.annotations[j].colour;\r
266 \r
267             if (row.annotations[j].colour != null\r
268                     && row.annotations[j].colour != java.awt.Color.black)\r
269             {\r
270               text.append(comma\r
271                       + "["\r
272                       + jalview.util.Format\r
273                               .getHexString(row.annotations[j].colour)\r
274                       + "]");\r
275               comma = ",";\r
276             }\r
277           }\r
278           text.append("|");\r
279         }\r
280 \r
281         if (row.hasScore())\r
282           text.append("\t" + row.score);\r
283 \r
284         text.append("\n");\r
285 \r
286         if (color != null && color != java.awt.Color.black && oneColour)\r
287         {\r
288           colours.append("COLOUR\t" + row.label + "\t"\r
289                   + jalview.util.Format.getHexString(color) + "\n");\r
290         }\r
291         if (row.scaleColLabel || row.showAllColLabels || row.centreColLabels)\r
292         {\r
293           rowprops.append("ROWPROPERTIES\t"+row.label);\r
294           rowprops.append("\tscaletofit="+row.scaleColLabel);\r
295           rowprops.append("\tshowalllabs="+row.showAllColLabels);\r
296           rowprops.append("\tcentrelabs="+row.centreColLabels);\r
297           rowprops.append("\n");\r
298         }\r
299       }\r
300 \r
301       text.append("\n");\r
302 \r
303       text.append(colours.toString());\r
304       text.append(graphLine.toString());\r
305       if (graphGroup.size() > 0)\r
306       {\r
307         text.append("COMBINE\t");\r
308         Enumeration en = graphGroup.elements();\r
309         while (en.hasMoreElements())\r
310         {\r
311           text.append(en.nextElement() + "\n");\r
312         }\r
313       }\r
314       text.append(rowprops.toString());\r
315     }\r
316 \r
317     if (groups != null)\r
318     {\r
319       printGroups(groups);\r
320     }\r
321 \r
322     if (properties != null)\r
323     {\r
324       text.append("\n\nALIGNMENT");\r
325       Enumeration en = properties.keys();\r
326       while (en.hasMoreElements())\r
327       {\r
328         String key = en.nextElement().toString();\r
329         text.append("\t" + key + "=" + properties.get(key));\r
330       }\r
331       // TODO: output alignment visualization settings here if required\r
332 \r
333     }\r
334 \r
335     return text.toString();\r
336   }\r
337 \r
338   public void printGroups(Vector sequenceGroups)\r
339   {\r
340     SequenceGroup sg;\r
341     SequenceI seqrep = null;\r
342     for (int i = 0; i < sequenceGroups.size(); i++)\r
343     {\r
344       sg = (SequenceGroup) sequenceGroups.elementAt(i);\r
345       if (!sg.hasSeqrep())\r
346       {\r
347         text.append("SEQUENCE_GROUP\t" + sg.getName() + "\t"\r
348                 + (sg.getStartRes() + 1) + "\t" + (sg.getEndRes() + 1)\r
349                 + "\t" + "-1\t");\r
350         seqrep = null;\r
351       }\r
352       else\r
353       {\r
354         seqrep = sg.getSeqrep();\r
355         text.append("SEQUENCE_REF\t" + seqrep.getName() + "\n");\r
356         text.append("SEQUENCE_GROUP\t" + sg.getName() + "\t"\r
357                 + (seqrep.findPosition(sg.getStartRes())) + "\t"\r
358                 + (seqrep.findPosition(sg.getEndRes())) + "\t" + "-1\t");\r
359       }\r
360       for (int s = 0; s < sg.getSize(); s++)\r
361       {\r
362         text.append(sg.getSequenceAt(s).getName() + "\t");\r
363       }\r
364 \r
365       text.append("\nPROPERTIES\t" + sg.getName() + "\t");\r
366 \r
367       if (sg.getDescription() != null)\r
368       {\r
369         text.append("description=" + sg.getDescription() + "\t");\r
370       }\r
371       if (sg.cs != null)\r
372       {\r
373         text.append("colour=" + ColourSchemeProperty.getColourName(sg.cs)\r
374                 + "\t");\r
375         if (sg.cs.getThreshold() != 0)\r
376         {\r
377           text.append("pidThreshold=" + sg.cs.getThreshold());\r
378         }\r
379         if (sg.cs.conservationApplied())\r
380         {\r
381           text.append("consThreshold=" + sg.cs.getConservationInc() + "\t");\r
382         }\r
383       }\r
384       text.append("outlineColour="\r
385               + jalview.util.Format.getHexString(sg.getOutlineColour())\r
386               + "\t");\r
387 \r
388       text.append("displayBoxes=" + sg.getDisplayBoxes() + "\t");\r
389       text.append("displayText=" + sg.getDisplayText() + "\t");\r
390       text.append("colourText=" + sg.getColourText() + "\t");\r
391       text.append("showUnconserved=" + sg.getShowunconserved() + "\t");\r
392       if (sg.textColour != java.awt.Color.black)\r
393       {\r
394         text.append("textCol1="\r
395                 + jalview.util.Format.getHexString(sg.textColour) + "\t");\r
396       }\r
397       if (sg.textColour2 != java.awt.Color.white)\r
398       {\r
399         text.append("textCol2="\r
400                 + jalview.util.Format.getHexString(sg.textColour2) + "\t");\r
401       }\r
402       if (sg.thresholdTextColour != 0)\r
403       {\r
404         text.append("textColThreshold=" + sg.thresholdTextColour + "\t");\r
405       }\r
406       if (sg.idColour != null)\r
407       {\r
408         text.append("idColour="\r
409                 + jalview.util.Format.getHexString(sg.idColour) + "\t");\r
410       }\r
411       if (sg.isHidereps())\r
412       {\r
413         text.append("hide=true\t");\r
414       }\r
415       if (sg.isHideCols())\r
416       {\r
417         text.append("hidecols=true\t");\r
418       }\r
419       if (seqrep != null)\r
420       {\r
421         // terminate the last line and clear the sequence ref for the group\r
422         text.append("\nSEQUENCE_REF");\r
423       }\r
424       text.append("\n\n");\r
425 \r
426     }\r
427   }\r
428 \r
429   SequenceI refSeq = null;\r
430 \r
431   String refSeqId = null;\r
432 \r
433   public boolean readAnnotationFile(AlignmentI al, String file,\r
434           String protocol)\r
435   {\r
436     try\r
437     {\r
438       BufferedReader in = null;\r
439       if (protocol.equals(AppletFormatAdapter.FILE))\r
440       {\r
441         in = new BufferedReader(new FileReader(file));\r
442       }\r
443       else if (protocol.equals(AppletFormatAdapter.URL))\r
444       {\r
445         URL url = new URL(file);\r
446         in = new BufferedReader(new InputStreamReader(url.openStream()));\r
447       }\r
448       else if (protocol.equals(AppletFormatAdapter.PASTE))\r
449       {\r
450         in = new BufferedReader(new StringReader(file));\r
451       }\r
452       else if (protocol.equals(AppletFormatAdapter.CLASSLOADER))\r
453       {\r
454         java.io.InputStream is = getClass().getResourceAsStream("/" + file);\r
455         if (is != null)\r
456         {\r
457           in = new BufferedReader(new java.io.InputStreamReader(is));\r
458         }\r
459       }\r
460 \r
461       String line, label, description, token;\r
462       int graphStyle, index;\r
463       int refSeqIndex = 1;\r
464       int existingAnnotations = 0;\r
465       if (al.getAlignmentAnnotation() != null)\r
466       {\r
467         existingAnnotations = al.getAlignmentAnnotation().length;\r
468       }\r
469 \r
470       int alWidth = al.getWidth();\r
471 \r
472       StringTokenizer st;\r
473       Annotation[] annotations;\r
474       AlignmentAnnotation annotation = null;\r
475 \r
476       // First confirm this is an Annotation file\r
477       boolean jvAnnotationFile = false;\r
478       while ((line = in.readLine()) != null)\r
479       {\r
480         if (line.indexOf("#") == 0)\r
481         {\r
482           continue;\r
483         }\r
484 \r
485         if (line.indexOf("JALVIEW_ANNOTATION") > -1)\r
486         {\r
487           jvAnnotationFile = true;\r
488           break;\r
489         }\r
490       }\r
491 \r
492       if (!jvAnnotationFile)\r
493       {\r
494         in.close();\r
495         return false;\r
496       }\r
497 \r
498       while ((line = in.readLine()) != null)\r
499       {\r
500         if (line.indexOf("#") == 0\r
501                 || line.indexOf("JALVIEW_ANNOTATION") > -1\r
502                 || line.length() == 0)\r
503         {\r
504           continue;\r
505         }\r
506 \r
507         st = new StringTokenizer(line, "\t");\r
508         token = st.nextToken();\r
509         if (token.equalsIgnoreCase("COLOUR"))\r
510         {\r
511           // TODO: use graduated colour def'n here too\r
512           colourAnnotations(al, st.nextToken(), st.nextToken());\r
513           continue;\r
514         }\r
515 \r
516         else if (token.equalsIgnoreCase("COMBINE"))\r
517         {\r
518           combineAnnotations(al, st);\r
519           continue;\r
520         }\r
521         else if (token.equalsIgnoreCase("ROWPROPERTIES"))\r
522         {\r
523           addRowProperties(al, st);\r
524           continue;\r
525         }\r
526         else if (token.equalsIgnoreCase("GRAPHLINE"))\r
527         {\r
528           addLine(al, st);\r
529           continue;\r
530         }\r
531 \r
532         else if (token.equalsIgnoreCase("SEQUENCE_REF"))\r
533         {\r
534           if (st.hasMoreTokens())\r
535           {\r
536             refSeq = al.findName(refSeqId = st.nextToken());\r
537             if (refSeq == null)\r
538             {\r
539               refSeqId = null;\r
540             }\r
541             try\r
542             {\r
543               refSeqIndex = Integer.parseInt(st.nextToken());\r
544               if (refSeqIndex < 1)\r
545               {\r
546                 refSeqIndex = 1;\r
547                 System.out\r
548                         .println("WARNING: SEQUENCE_REF index must be > 0 in AnnotationFile");\r
549               }\r
550             } catch (Exception ex)\r
551             {\r
552               refSeqIndex = 1;\r
553             }\r
554           }\r
555           else\r
556           {\r
557             refSeq = null;\r
558             refSeqId = null;\r
559           }\r
560           continue;\r
561         }\r
562 \r
563         else if (token.equalsIgnoreCase("SEQUENCE_GROUP"))\r
564         {\r
565           addGroup(al, st);\r
566           continue;\r
567         }\r
568 \r
569         else if (token.equalsIgnoreCase("PROPERTIES"))\r
570         {\r
571           addProperties(al, st);\r
572           continue;\r
573         }\r
574 \r
575         else if (token.equalsIgnoreCase("BELOW_ALIGNMENT"))\r
576         {\r
577           setBelowAlignment(al, st);\r
578           continue;\r
579         }\r
580         else if (token.equalsIgnoreCase("ALIGNMENT"))\r
581         {\r
582           addAlignmentDetails(al, st);\r
583           continue;\r
584         }\r
585 \r
586         graphStyle = AlignmentAnnotation.getGraphValueFromString(token);\r
587         label = st.nextToken();\r
588 \r
589         index = 0;\r
590         annotations = new Annotation[alWidth];\r
591         description = null;\r
592         float score = Float.NaN;\r
593 \r
594         if (st.hasMoreTokens())\r
595         {\r
596           line = st.nextToken();\r
597 \r
598           if (line.indexOf("|") == -1)\r
599           {\r
600             description = line;\r
601             if (st.hasMoreTokens())\r
602               line = st.nextToken();\r
603           }\r
604 \r
605           if (st.hasMoreTokens())\r
606           {\r
607             // This must be the score\r
608             score = Float.valueOf(st.nextToken()).floatValue();\r
609           }\r
610 \r
611           st = new StringTokenizer(line, "|", true);\r
612 \r
613           boolean emptyColumn = true;\r
614           boolean onlyOneElement = (st.countTokens() == 1);\r
615 \r
616           while (st.hasMoreElements() && index < alWidth)\r
617           {\r
618             token = st.nextToken().trim();\r
619 \r
620             if (onlyOneElement)\r
621             {\r
622               try\r
623               {\r
624                 score = Float.valueOf(token).floatValue();\r
625                 break;\r
626               } catch (NumberFormatException ex)\r
627               {\r
628               }\r
629             }\r
630 \r
631             if (token.equals("|"))\r
632             {\r
633               if (emptyColumn)\r
634               {\r
635                 index++;\r
636               }\r
637 \r
638               emptyColumn = true;\r
639             }\r
640             else\r
641             {\r
642               annotations[index++] = parseAnnotation(token, graphStyle);\r
643               emptyColumn = false;\r
644             }\r
645           }\r
646 \r
647         }\r
648 \r
649         annotation = new AlignmentAnnotation(label, description,\r
650                 (index == 0) ? null : annotations, 0, 0, graphStyle);\r
651 \r
652         annotation.score = score;\r
653 \r
654         if (refSeq != null)\r
655         {\r
656           annotation.belowAlignment = false;\r
657           // make a copy of refSeq so we can find other matches in the alignment\r
658           SequenceI referedSeq = refSeq;\r
659           do\r
660           {\r
661             // copy before we do any mapping business.\r
662             // TODO: verify that undo/redo with 1:many sequence associated\r
663             // annotations can be undone correctly\r
664             AlignmentAnnotation ann = new AlignmentAnnotation(annotation);\r
665             annotation\r
666                     .createSequenceMapping(referedSeq, refSeqIndex, false);\r
667             annotation.adjustForAlignment();\r
668             referedSeq.addAlignmentAnnotation(annotation);\r
669             al.addAnnotation(annotation);\r
670             al.setAnnotationIndex(annotation,\r
671                     al.getAlignmentAnnotation().length\r
672                             - existingAnnotations - 1);\r
673             // and recover our virgin copy to use again if necessary.\r
674             annotation = ann;\r
675 \r
676           } while (refSeqId != null\r
677                   && (referedSeq = al.findName(referedSeq, refSeqId, true)) != null);\r
678         }\r
679         else\r
680         {\r
681           al.addAnnotation(annotation);\r
682           al.setAnnotationIndex(annotation,\r
683                   al.getAlignmentAnnotation().length - existingAnnotations\r
684                           - 1);\r
685         }\r
686       }\r
687 \r
688     } catch (Exception ex)\r
689     {\r
690       ex.printStackTrace();\r
691       System.out.println("Problem reading annotation file: " + ex);\r
692       return false;\r
693     }\r
694     return true;\r
695   }\r
696 \r
697   Annotation parseAnnotation(String string, int graphStyle)\r
698   {\r
699     boolean hasSymbols = (graphStyle == AlignmentAnnotation.NO_GRAPH); // don't do the glyph test if we don't want secondary structure\r
700     String desc = null, displayChar = null;\r
701     char ss = ' '; // secondaryStructure\r
702     float value = 0;\r
703     boolean parsedValue = false, dcset = false;\r
704 \r
705     // find colour here\r
706     java.awt.Color colour = null;\r
707     int i = string.indexOf("[");\r
708     int j = string.indexOf("]");\r
709     if (i > -1 && j > -1)\r
710     {\r
711       UserColourScheme ucs = new UserColourScheme();\r
712 \r
713       colour = ucs.getColourFromString(string.substring(i + 1, j));\r
714       if ( i>0 && string.charAt(i-1)==',')\r
715       {\r
716         // clip the preceding comma as well\r
717         i--;\r
718       }\r
719       string = string.substring(0, i) + string.substring(j + 1);\r
720     }\r
721 \r
722     StringTokenizer st = new StringTokenizer(string, ",", true);\r
723     String token;\r
724     boolean seenContent=false;\r
725     int pass=0;\r
726     while (st.hasMoreTokens())\r
727     {\r
728       pass++;\r
729       token = st.nextToken().trim();\r
730       if (token.equals(","))\r
731       {\r
732         if (!seenContent && parsedValue && !dcset)\r
733         {\r
734           // allow the value below the bar/line to be empty\r
735           dcset = true;\r
736           displayChar = " ";\r
737         }\r
738         seenContent = false;\r
739         continue;\r
740       } else {\r
741         seenContent = true;\r
742       }\r
743 \r
744       if (!parsedValue)\r
745       {\r
746         try\r
747         {\r
748           displayChar = token; \r
749           // foo\r
750           value = new Float(token).floatValue();\r
751           parsedValue = true;\r
752           continue;\r
753         } catch (NumberFormatException ex)\r
754         {\r
755         }\r
756       } else {\r
757         if (token.length()==1)\r
758         {\r
759           displayChar = token;\r
760         }\r
761       }\r
762       if (hasSymbols && (token.equals("H") || token.equals("E") || token.equals(" ")))\r
763       {\r
764         // Either this character represents a helix or sheet\r
765         // or an integer which can be displayed\r
766         ss = token.charAt(0);\r
767         if (displayChar.equals(token.substring(0, 1)))\r
768         {\r
769           displayChar = "";\r
770         }\r
771       }\r
772       else if (desc == null || (parsedValue && pass>2))\r
773       {\r
774         desc = token;\r
775       }\r
776 \r
777     }\r
778 //    if (!dcset && string.charAt(string.length() - 1) == ',')\r
779 //    {\r
780 //      displayChar = " "; // empty display char symbol.\r
781 //    }\r
782     if (displayChar != null && desc!=null && desc.length()==1) \r
783     {\r
784       if (displayChar.length() > 1)\r
785       {\r
786         // switch desc and displayChar - legacy support  \r
787         String tmp = displayChar;\r
788         displayChar = desc;\r
789         desc = tmp;\r
790       } else {\r
791         if (displayChar.equals(desc)) {\r
792           // duplicate label - hangover from the 'robust parser' above\r
793           desc = null;\r
794         }\r
795       }\r
796     }\r
797     Annotation anot = new Annotation(displayChar, desc, ss, value);\r
798 \r
799     anot.colour = colour;\r
800 \r
801     return anot;\r
802   }\r
803 \r
804   void colourAnnotations(AlignmentI al, String label, String colour)\r
805   {\r
806     UserColourScheme ucs = new UserColourScheme(colour);\r
807     Annotation[] annotations;\r
808     for (int i = 0; i < al.getAlignmentAnnotation().length; i++)\r
809     {\r
810       if (al.getAlignmentAnnotation()[i].label.equalsIgnoreCase(label))\r
811       {\r
812         annotations = al.getAlignmentAnnotation()[i].annotations;\r
813         for (int j = 0; j < annotations.length; j++)\r
814         {\r
815           if (annotations[j] != null)\r
816           {\r
817             annotations[j].colour = ucs.findColour('A');\r
818           }\r
819         }\r
820       }\r
821     }\r
822   }\r
823 \r
824   void combineAnnotations(AlignmentI al, StringTokenizer st)\r
825   {\r
826     int graphGroup = -1;\r
827     String group = st.nextToken();\r
828     // First make sure we are not overwriting the graphIndex\r
829     for (int i = 0; i < al.getAlignmentAnnotation().length; i++)\r
830     {\r
831       if (al.getAlignmentAnnotation()[i].label.equalsIgnoreCase(group))\r
832       {\r
833         graphGroup = al.getAlignmentAnnotation()[i].graphGroup + 1;\r
834         al.getAlignmentAnnotation()[i].graphGroup = graphGroup;\r
835         break;\r
836       }\r
837     }\r
838 \r
839     // Now update groups\r
840     while (st.hasMoreTokens())\r
841     {\r
842       group = st.nextToken();\r
843       for (int i = 0; i < al.getAlignmentAnnotation().length; i++)\r
844       {\r
845         if (al.getAlignmentAnnotation()[i].label.equalsIgnoreCase(group))\r
846         {\r
847           al.getAlignmentAnnotation()[i].graphGroup = graphGroup;\r
848           break;\r
849         }\r
850       }\r
851     }\r
852   }\r
853 \r
854   void addLine(AlignmentI al, StringTokenizer st)\r
855   {\r
856     String group = st.nextToken();\r
857     AlignmentAnnotation annotation = null;\r
858 \r
859     for (int i = 0; i < al.getAlignmentAnnotation().length; i++)\r
860     {\r
861       if (al.getAlignmentAnnotation()[i].label.equalsIgnoreCase(group))\r
862       {\r
863         annotation = al.getAlignmentAnnotation()[i];\r
864         break;\r
865       }\r
866     }\r
867 \r
868     if (annotation == null)\r
869     {\r
870       return;\r
871     }\r
872     float value = new Float(st.nextToken()).floatValue();\r
873     String label = st.hasMoreTokens() ? st.nextToken() : null;\r
874     java.awt.Color colour = null;\r
875     if (st.hasMoreTokens())\r
876     {\r
877       UserColourScheme ucs = new UserColourScheme(st.nextToken());\r
878       colour = ucs.findColour('A');\r
879     }\r
880 \r
881     annotation.setThreshold(new GraphLine(value, label, colour));\r
882   }\r
883 \r
884   void addGroup(AlignmentI al, StringTokenizer st)\r
885   {\r
886     SequenceGroup sg = new SequenceGroup();\r
887     sg.setName(st.nextToken());\r
888     String rng = "";\r
889     try\r
890     {\r
891       rng = st.nextToken();\r
892       if (rng.length() > 0 && !rng.startsWith("*"))\r
893       {\r
894         sg.setStartRes(Integer.parseInt(rng) - 1);\r
895       }\r
896       else\r
897       {\r
898         sg.setStartRes(0);\r
899       }\r
900       rng = st.nextToken();\r
901       if (rng.length() > 0 && !rng.startsWith("*"))\r
902       {\r
903         sg.setEndRes(Integer.parseInt(rng) - 1);\r
904       }\r
905       else\r
906       {\r
907         sg.setEndRes(al.getWidth() - 1);\r
908       }\r
909     } catch (Exception e)\r
910     {\r
911       System.err\r
912               .println("Couldn't parse Group Start or End Field as '*' or a valid column or sequence index: '"\r
913                       + rng + "' - assuming alignment width for group.");\r
914       // assume group is full width\r
915       sg.setStartRes(0);\r
916       sg.setEndRes(al.getWidth() - 1);\r
917     }\r
918 \r
919     String index = st.nextToken();\r
920     if (index.equals("-1"))\r
921     {\r
922       while (st.hasMoreElements())\r
923       {\r
924         sg.addSequence(al.findName(st.nextToken()), false);\r
925       }\r
926     }\r
927     else\r
928     {\r
929       StringTokenizer st2 = new StringTokenizer(index, ",");\r
930 \r
931       while (st2.hasMoreTokens())\r
932       {\r
933         String tmp = st2.nextToken();\r
934         if (tmp.equals("*"))\r
935         {\r
936           for (int i = 0; i < al.getHeight(); i++)\r
937           {\r
938             sg.addSequence(al.getSequenceAt(i), false);\r
939           }\r
940         }\r
941         else if (tmp.indexOf("-") >= 0)\r
942         {\r
943           StringTokenizer st3 = new StringTokenizer(tmp, "-");\r
944 \r
945           int start = (Integer.parseInt(st3.nextToken()));\r
946           int end = (Integer.parseInt(st3.nextToken()));\r
947 \r
948           if (end > start)\r
949           {\r
950             for (int i = start; i <= end; i++)\r
951             {\r
952               sg.addSequence(al.getSequenceAt(i - 1), false);\r
953             }\r
954           }\r
955         }\r
956         else\r
957         {\r
958           sg\r
959                   .addSequence(al.getSequenceAt(Integer.parseInt(tmp) - 1),\r
960                           false);\r
961         }\r
962       }\r
963     }\r
964 \r
965     if (refSeq != null)\r
966     {\r
967       sg.setStartRes(refSeq.findIndex(sg.getStartRes() + 1) - 1);\r
968       sg.setEndRes(refSeq.findIndex(sg.getEndRes() + 1) - 1);\r
969       sg.setSeqrep(refSeq);\r
970     }\r
971 \r
972     if (sg.getSize() > 0)\r
973     {\r
974       al.addGroup(sg);\r
975     }\r
976   }\r
977 \r
978   void addRowProperties(AlignmentI al, StringTokenizer st)\r
979   {\r
980     String label = st.nextToken(),keyValue,key,value;\r
981     boolean scaletofit=false,centerlab=false,showalllabs=false;\r
982     while (st.hasMoreTokens()) {\r
983       keyValue=st.nextToken();\r
984       key = keyValue.substring(0, keyValue.indexOf("="));\r
985       value = keyValue.substring(keyValue.indexOf("=") + 1);\r
986       if (key.equalsIgnoreCase("scaletofit")) {\r
987         scaletofit = Boolean.valueOf(value).booleanValue();\r
988       }\r
989         if (key.equalsIgnoreCase("showalllabs")) {\r
990           showalllabs = Boolean.valueOf(value).booleanValue();\r
991         }\r
992         if (key.equalsIgnoreCase("centrelabs")) {\r
993           centerlab = Boolean.valueOf(value).booleanValue();\r
994     }\r
995         AlignmentAnnotation[] alr = al.getAlignmentAnnotation(); \r
996         for (int i = 0; i < alr.length; i++)\r
997         {\r
998           if (alr[i].label.equalsIgnoreCase(label))\r
999           {\r
1000             alr[i].centreColLabels = centerlab;\r
1001             alr[i].scaleColLabel = scaletofit;\r
1002             alr[i].showAllColLabels = showalllabs;\r
1003           }\r
1004         }\r
1005     }\r
1006   }\r
1007   void addProperties(AlignmentI al, StringTokenizer st)\r
1008   {\r
1009 \r
1010     // So far we have only added groups to the annotationHash,\r
1011     // the idea is in the future properties can be added to\r
1012     // alignments, other annotations etc\r
1013     if (al.getGroups() == null)\r
1014     {\r
1015       return;\r
1016     }\r
1017     SequenceGroup sg = null;\r
1018 \r
1019     String name = st.nextToken();\r
1020 \r
1021     Vector groups = al.getGroups();\r
1022     for (int i = 0; i < groups.size(); i++)\r
1023     {\r
1024       sg = (SequenceGroup) groups.elementAt(i);\r
1025       if (sg.getName().equals(name))\r
1026       {\r
1027         break;\r
1028       }\r
1029       else\r
1030       {\r
1031         sg = null;\r
1032       }\r
1033     }\r
1034 \r
1035     if (sg != null)\r
1036     {\r
1037       String keyValue, key, value;\r
1038       ColourSchemeI def = sg.cs;\r
1039       sg.cs = null;\r
1040       while (st.hasMoreTokens())\r
1041       {\r
1042         keyValue = st.nextToken();\r
1043         key = keyValue.substring(0, keyValue.indexOf("="));\r
1044         value = keyValue.substring(keyValue.indexOf("=") + 1);\r
1045 \r
1046         if (key.equalsIgnoreCase("description"))\r
1047         {\r
1048           sg.setDescription(value);\r
1049         }\r
1050         else if (key.equalsIgnoreCase("colour"))\r
1051         {\r
1052           sg.cs = ColourSchemeProperty.getColour(al, value);\r
1053         }\r
1054         else if (key.equalsIgnoreCase("pidThreshold"))\r
1055         {\r
1056           sg.cs.setThreshold(Integer.parseInt(value), true);\r
1057 \r
1058         }\r
1059         else if (key.equalsIgnoreCase("consThreshold"))\r
1060         {\r
1061           sg.cs.setConservationInc(Integer.parseInt(value));\r
1062           Conservation c = new Conservation("Group",\r
1063                   ResidueProperties.propHash, 3, sg.getSequences(null), sg\r
1064                           .getStartRes(), sg.getEndRes() + 1);\r
1065 \r
1066           c.calculate();\r
1067           c.verdict(false, 25);\r
1068 \r
1069           sg.cs.setConservation(c);\r
1070 \r
1071         }\r
1072         else if (key.equalsIgnoreCase("outlineColour"))\r
1073         {\r
1074           sg.setOutlineColour(new UserColourScheme(value).findColour('A'));\r
1075         }\r
1076         else if (key.equalsIgnoreCase("displayBoxes"))\r
1077         {\r
1078           sg.setDisplayBoxes(Boolean.valueOf(value).booleanValue());\r
1079         }\r
1080         else if (key.equalsIgnoreCase("showUnconserved"))\r
1081         {\r
1082           sg.setShowunconserved(Boolean.valueOf(value).booleanValue());\r
1083         }\r
1084         else if (key.equalsIgnoreCase("displayText"))\r
1085         {\r
1086           sg.setDisplayText(Boolean.valueOf(value).booleanValue());\r
1087         }\r
1088         else if (key.equalsIgnoreCase("colourText"))\r
1089         {\r
1090           sg.setColourText(Boolean.valueOf(value).booleanValue());\r
1091         }\r
1092         else if (key.equalsIgnoreCase("textCol1"))\r
1093         {\r
1094           sg.textColour = new UserColourScheme(value).findColour('A');\r
1095         }\r
1096         else if (key.equalsIgnoreCase("textCol2"))\r
1097         {\r
1098           sg.textColour2 = new UserColourScheme(value).findColour('A');\r
1099         }\r
1100         else if (key.equalsIgnoreCase("textColThreshold"))\r
1101         {\r
1102           sg.thresholdTextColour = Integer.parseInt(value);\r
1103         }\r
1104         else if (key.equalsIgnoreCase("idColour"))\r
1105         {\r
1106           // consider warning if colour doesn't resolve to a real colour\r
1107           sg.setIdColour((def = new UserColourScheme(value))\r
1108                   .findColour('A'));\r
1109         }\r
1110         else if (key.equalsIgnoreCase("hide"))\r
1111         {\r
1112           // see bug https://mantis.lifesci.dundee.ac.uk/view.php?id=25847\r
1113           sg.setHidereps(true);\r
1114         }\r
1115         else if (key.equalsIgnoreCase("hidecols"))\r
1116         {\r
1117           // see bug https://mantis.lifesci.dundee.ac.uk/view.php?id=25847\r
1118           sg.setHideCols(true);\r
1119         }\r
1120         sg.recalcConservation();\r
1121       }\r
1122       if (sg.cs == null)\r
1123       {\r
1124         sg.cs = def;\r
1125       }\r
1126     }\r
1127   }\r
1128 \r
1129   void setBelowAlignment(AlignmentI al, StringTokenizer st)\r
1130   {\r
1131     String token;\r
1132     AlignmentAnnotation aa;\r
1133     while (st.hasMoreTokens())\r
1134     {\r
1135       token = st.nextToken();\r
1136       for (int i = 0; i < al.getAlignmentAnnotation().length; i++)\r
1137       {\r
1138         aa = al.getAlignmentAnnotation()[i];\r
1139         if (aa.sequenceRef == refSeq && aa.label.equals(token))\r
1140         {\r
1141           aa.belowAlignment = true;\r
1142         }\r
1143       }\r
1144     }\r
1145   }\r
1146 \r
1147   void addAlignmentDetails(AlignmentI al, StringTokenizer st)\r
1148   {\r
1149     String keyValue, key, value;\r
1150     while (st.hasMoreTokens())\r
1151     {\r
1152       keyValue = st.nextToken();\r
1153       key = keyValue.substring(0, keyValue.indexOf("="));\r
1154       value = keyValue.substring(keyValue.indexOf("=") + 1);\r
1155       al.setProperty(key, value);\r
1156     }\r
1157   }\r
1158 \r
1159   /**\r
1160    * Write annotations as a CSV file of the form 'label, value, value, ...' for\r
1161    * each row.\r
1162    * \r
1163    * @param annotations\r
1164    * @return CSV file as a string.\r
1165    */\r
1166   public String printCSVAnnotations(AlignmentAnnotation[] annotations)\r
1167   {\r
1168     StringBuffer sp = new StringBuffer();\r
1169     for (int i = 0; i < annotations.length; i++)\r
1170     {\r
1171       String atos = annotations[i].toString();\r
1172       int p = 0;\r
1173       do\r
1174       {\r
1175         int cp = atos.indexOf("\n", p);\r
1176         sp.append(annotations[i].label);\r
1177         sp.append(",");\r
1178         if (cp > p)\r
1179         {\r
1180           sp.append(atos.substring(p, cp + 1));\r
1181         }\r
1182         else\r
1183         {\r
1184           sp.append(atos.substring(p));\r
1185           sp.append("\n");\r
1186         }\r
1187         p = cp + 1;\r
1188       } while (p > 0);\r
1189     }\r
1190     return sp.toString();\r
1191   }\r
1192 }\r