patch for bug #36198 - more to follow on Monday.
[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 (row.annotations[j].secondaryStructure != ' ') // could be also hasGlyphs || ... \r
214             {\r
215 \r
216               text.append(comma + row.annotations[j].secondaryStructure);\r
217               comma = ",";\r
218             }\r
219             if (hasValues)\r
220             {\r
221               if (row.annotations[j].value != Float.NaN)\r
222               {\r
223                 text.append(comma + row.annotations[j].value);\r
224               }\r
225               else\r
226               {\r
227                 System.err.println("Skipping NaN - not valid value.");\r
228                 text.append(comma + 0f);// row.annotations[j].value);\r
229               }\r
230               comma = ",";\r
231             }\r
232             if (hasLabels)\r
233             {\r
234               // TODO: labels are emitted after values for bar graphs.\r
235               if // empty labels are allowed, so\r
236               (row.annotations[j].displayCharacter != null\r
237                       && row.annotations[j].displayCharacter.length() > 0\r
238                       && !row.annotations[j].displayCharacter.equals(" "))\r
239               {\r
240                 text.append(comma + row.annotations[j].displayCharacter);\r
241                 comma = ",";\r
242               }\r
243             }\r
244             if (hasText)\r
245             {\r
246               if (row.annotations[j].description != null\r
247                       && row.annotations[j].description.length() > 0\r
248                       && !row.annotations[j].description\r
249                               .equals(row.annotations[j].displayCharacter))\r
250               {\r
251                 text.append(comma + row.annotations[j].description);\r
252                 comma = ",";\r
253               }\r
254             }\r
255             if (color != null && !color.equals(row.annotations[j].colour))\r
256             {\r
257               oneColour = false;\r
258             }\r
259 \r
260             color = row.annotations[j].colour;\r
261 \r
262             if (row.annotations[j].colour != null\r
263                     && row.annotations[j].colour != java.awt.Color.black)\r
264             {\r
265               text.append(comma\r
266                       + "["\r
267                       + jalview.util.Format\r
268                               .getHexString(row.annotations[j].colour)\r
269                       + "]");\r
270               comma = ",";\r
271             }\r
272           }\r
273           text.append("|");\r
274         }\r
275 \r
276         if (row.hasScore())\r
277           text.append("\t" + row.score);\r
278 \r
279         text.append("\n");\r
280 \r
281         if (color != null && color != java.awt.Color.black && oneColour)\r
282         {\r
283           colours.append("COLOUR\t" + row.label + "\t"\r
284                   + jalview.util.Format.getHexString(color) + "\n");\r
285         }\r
286         if (row.scaleColLabel || row.showAllColLabels || row.centreColLabels)\r
287         {\r
288           rowprops.append("ROWPROPERTIES\t"+row.label);\r
289           rowprops.append("\tscaletofit="+row.scaleColLabel);\r
290           rowprops.append("\tshowalllabs="+row.showAllColLabels);\r
291           rowprops.append("\tcentrelabs="+row.centreColLabels);\r
292           rowprops.append("\n");\r
293         }\r
294       }\r
295 \r
296       text.append("\n");\r
297 \r
298       text.append(colours.toString());\r
299       text.append(graphLine.toString());\r
300       if (graphGroup.size() > 0)\r
301       {\r
302         text.append("COMBINE\t");\r
303         Enumeration en = graphGroup.elements();\r
304         while (en.hasMoreElements())\r
305         {\r
306           text.append(en.nextElement() + "\n");\r
307         }\r
308       }\r
309       text.append(rowprops.toString());\r
310     }\r
311 \r
312     if (groups != null)\r
313     {\r
314       printGroups(groups);\r
315     }\r
316 \r
317     if (properties != null)\r
318     {\r
319       text.append("\n\nALIGNMENT");\r
320       Enumeration en = properties.keys();\r
321       while (en.hasMoreElements())\r
322       {\r
323         String key = en.nextElement().toString();\r
324         text.append("\t" + key + "=" + properties.get(key));\r
325       }\r
326       // TODO: output alignment visualization settings here if required\r
327 \r
328     }\r
329 \r
330     return text.toString();\r
331   }\r
332 \r
333   public void printGroups(Vector sequenceGroups)\r
334   {\r
335     SequenceGroup sg;\r
336     SequenceI seqrep = null;\r
337     for (int i = 0; i < sequenceGroups.size(); i++)\r
338     {\r
339       sg = (SequenceGroup) sequenceGroups.elementAt(i);\r
340       if (!sg.hasSeqrep())\r
341       {\r
342         text.append("SEQUENCE_GROUP\t" + sg.getName() + "\t"\r
343                 + (sg.getStartRes() + 1) + "\t" + (sg.getEndRes() + 1)\r
344                 + "\t" + "-1\t");\r
345         seqrep = null;\r
346       }\r
347       else\r
348       {\r
349         seqrep = sg.getSeqrep();\r
350         text.append("SEQUENCE_REF\t" + seqrep.getName() + "\n");\r
351         text.append("SEQUENCE_GROUP\t" + sg.getName() + "\t"\r
352                 + (seqrep.findPosition(sg.getStartRes())) + "\t"\r
353                 + (seqrep.findPosition(sg.getEndRes())) + "\t" + "-1\t");\r
354       }\r
355       for (int s = 0; s < sg.getSize(); s++)\r
356       {\r
357         text.append(sg.getSequenceAt(s).getName() + "\t");\r
358       }\r
359 \r
360       text.append("\nPROPERTIES\t" + sg.getName() + "\t");\r
361 \r
362       if (sg.getDescription() != null)\r
363       {\r
364         text.append("description=" + sg.getDescription() + "\t");\r
365       }\r
366       if (sg.cs != null)\r
367       {\r
368         text.append("colour=" + ColourSchemeProperty.getColourName(sg.cs)\r
369                 + "\t");\r
370         if (sg.cs.getThreshold() != 0)\r
371         {\r
372           text.append("pidThreshold=" + sg.cs.getThreshold());\r
373         }\r
374         if (sg.cs.conservationApplied())\r
375         {\r
376           text.append("consThreshold=" + sg.cs.getConservationInc() + "\t");\r
377         }\r
378       }\r
379       text.append("outlineColour="\r
380               + jalview.util.Format.getHexString(sg.getOutlineColour())\r
381               + "\t");\r
382 \r
383       text.append("displayBoxes=" + sg.getDisplayBoxes() + "\t");\r
384       text.append("displayText=" + sg.getDisplayText() + "\t");\r
385       text.append("colourText=" + sg.getColourText() + "\t");\r
386       text.append("showUnconserved=" + sg.getShowunconserved() + "\t");\r
387       if (sg.textColour != java.awt.Color.black)\r
388       {\r
389         text.append("textCol1="\r
390                 + jalview.util.Format.getHexString(sg.textColour) + "\t");\r
391       }\r
392       if (sg.textColour2 != java.awt.Color.white)\r
393       {\r
394         text.append("textCol2="\r
395                 + jalview.util.Format.getHexString(sg.textColour2) + "\t");\r
396       }\r
397       if (sg.thresholdTextColour != 0)\r
398       {\r
399         text.append("textColThreshold=" + sg.thresholdTextColour + "\t");\r
400       }\r
401       if (sg.idColour != null)\r
402       {\r
403         text.append("idColour="\r
404                 + jalview.util.Format.getHexString(sg.idColour) + "\t");\r
405       }\r
406       if (sg.isHidereps())\r
407       {\r
408         text.append("hide=true\t");\r
409       }\r
410       if (sg.isHideCols())\r
411       {\r
412         text.append("hidecols=true\t");\r
413       }\r
414       if (seqrep != null)\r
415       {\r
416         // terminate the last line and clear the sequence ref for the group\r
417         text.append("\nSEQUENCE_REF");\r
418       }\r
419       text.append("\n\n");\r
420 \r
421     }\r
422   }\r
423 \r
424   SequenceI refSeq = null;\r
425 \r
426   String refSeqId = null;\r
427 \r
428   public boolean readAnnotationFile(AlignmentI al, String file,\r
429           String protocol)\r
430   {\r
431     try\r
432     {\r
433       BufferedReader in = null;\r
434       if (protocol.equals(AppletFormatAdapter.FILE))\r
435       {\r
436         in = new BufferedReader(new FileReader(file));\r
437       }\r
438       else if (protocol.equals(AppletFormatAdapter.URL))\r
439       {\r
440         URL url = new URL(file);\r
441         in = new BufferedReader(new InputStreamReader(url.openStream()));\r
442       }\r
443       else if (protocol.equals(AppletFormatAdapter.PASTE))\r
444       {\r
445         in = new BufferedReader(new StringReader(file));\r
446       }\r
447       else if (protocol.equals(AppletFormatAdapter.CLASSLOADER))\r
448       {\r
449         java.io.InputStream is = getClass().getResourceAsStream("/" + file);\r
450         if (is != null)\r
451         {\r
452           in = new BufferedReader(new java.io.InputStreamReader(is));\r
453         }\r
454       }\r
455 \r
456       String line, label, description, token;\r
457       int graphStyle, index;\r
458       int refSeqIndex = 1;\r
459       int existingAnnotations = 0;\r
460       if (al.getAlignmentAnnotation() != null)\r
461       {\r
462         existingAnnotations = al.getAlignmentAnnotation().length;\r
463       }\r
464 \r
465       int alWidth = al.getWidth();\r
466 \r
467       StringTokenizer st;\r
468       Annotation[] annotations;\r
469       AlignmentAnnotation annotation = null;\r
470 \r
471       // First confirm this is an Annotation file\r
472       boolean jvAnnotationFile = false;\r
473       while ((line = in.readLine()) != null)\r
474       {\r
475         if (line.indexOf("#") == 0)\r
476         {\r
477           continue;\r
478         }\r
479 \r
480         if (line.indexOf("JALVIEW_ANNOTATION") > -1)\r
481         {\r
482           jvAnnotationFile = true;\r
483           break;\r
484         }\r
485       }\r
486 \r
487       if (!jvAnnotationFile)\r
488       {\r
489         in.close();\r
490         return false;\r
491       }\r
492 \r
493       while ((line = in.readLine()) != null)\r
494       {\r
495         if (line.indexOf("#") == 0\r
496                 || line.indexOf("JALVIEW_ANNOTATION") > -1\r
497                 || line.length() == 0)\r
498         {\r
499           continue;\r
500         }\r
501 \r
502         st = new StringTokenizer(line, "\t");\r
503         token = st.nextToken();\r
504         if (token.equalsIgnoreCase("COLOUR"))\r
505         {\r
506           // TODO: use graduated colour def'n here too\r
507           colourAnnotations(al, st.nextToken(), st.nextToken());\r
508           continue;\r
509         }\r
510 \r
511         else if (token.equalsIgnoreCase("COMBINE"))\r
512         {\r
513           combineAnnotations(al, st);\r
514           continue;\r
515         }\r
516         else if (token.equalsIgnoreCase("ROWPROPERTIES"))\r
517         {\r
518           addRowProperties(al, st);\r
519           continue;\r
520         }\r
521         else if (token.equalsIgnoreCase("GRAPHLINE"))\r
522         {\r
523           addLine(al, st);\r
524           continue;\r
525         }\r
526 \r
527         else if (token.equalsIgnoreCase("SEQUENCE_REF"))\r
528         {\r
529           if (st.hasMoreTokens())\r
530           {\r
531             refSeq = al.findName(refSeqId = st.nextToken());\r
532             if (refSeq == null)\r
533             {\r
534               refSeqId = null;\r
535             }\r
536             try\r
537             {\r
538               refSeqIndex = Integer.parseInt(st.nextToken());\r
539               if (refSeqIndex < 1)\r
540               {\r
541                 refSeqIndex = 1;\r
542                 System.out\r
543                         .println("WARNING: SEQUENCE_REF index must be > 0 in AnnotationFile");\r
544               }\r
545             } catch (Exception ex)\r
546             {\r
547               refSeqIndex = 1;\r
548             }\r
549           }\r
550           else\r
551           {\r
552             refSeq = null;\r
553             refSeqId = null;\r
554           }\r
555           continue;\r
556         }\r
557 \r
558         else if (token.equalsIgnoreCase("SEQUENCE_GROUP"))\r
559         {\r
560           addGroup(al, st);\r
561           continue;\r
562         }\r
563 \r
564         else if (token.equalsIgnoreCase("PROPERTIES"))\r
565         {\r
566           addProperties(al, st);\r
567           continue;\r
568         }\r
569 \r
570         else if (token.equalsIgnoreCase("BELOW_ALIGNMENT"))\r
571         {\r
572           setBelowAlignment(al, st);\r
573           continue;\r
574         }\r
575         else if (token.equalsIgnoreCase("ALIGNMENT"))\r
576         {\r
577           addAlignmentDetails(al, st);\r
578           continue;\r
579         }\r
580 \r
581         graphStyle = AlignmentAnnotation.getGraphValueFromString(token);\r
582         label = st.nextToken();\r
583 \r
584         index = 0;\r
585         annotations = new Annotation[alWidth];\r
586         description = null;\r
587         float score = Float.NaN;\r
588 \r
589         if (st.hasMoreTokens())\r
590         {\r
591           line = st.nextToken();\r
592 \r
593           if (line.indexOf("|") == -1)\r
594           {\r
595             description = line;\r
596             if (st.hasMoreTokens())\r
597               line = st.nextToken();\r
598           }\r
599 \r
600           if (st.hasMoreTokens())\r
601           {\r
602             // This must be the score\r
603             score = Float.valueOf(st.nextToken()).floatValue();\r
604           }\r
605 \r
606           st = new StringTokenizer(line, "|", true);\r
607 \r
608           boolean emptyColumn = true;\r
609           boolean onlyOneElement = (st.countTokens() == 1);\r
610 \r
611           while (st.hasMoreElements() && index < alWidth)\r
612           {\r
613             token = st.nextToken().trim();\r
614 \r
615             if (onlyOneElement)\r
616             {\r
617               try\r
618               {\r
619                 score = Float.valueOf(token).floatValue();\r
620                 break;\r
621               } catch (NumberFormatException ex)\r
622               {\r
623               }\r
624             }\r
625 \r
626             if (token.equals("|"))\r
627             {\r
628               if (emptyColumn)\r
629               {\r
630                 index++;\r
631               }\r
632 \r
633               emptyColumn = true;\r
634             }\r
635             else\r
636             {\r
637               annotations[index++] = parseAnnotation(token, graphStyle);\r
638               emptyColumn = false;\r
639             }\r
640           }\r
641 \r
642         }\r
643 \r
644         annotation = new AlignmentAnnotation(label, description,\r
645                 (index == 0) ? null : annotations, 0, 0, graphStyle);\r
646 \r
647         annotation.score = score;\r
648 \r
649         if (refSeq != null)\r
650         {\r
651           annotation.belowAlignment = false;\r
652           // make a copy of refSeq so we can find other matches in the alignment\r
653           SequenceI referedSeq = refSeq;\r
654           do\r
655           {\r
656             // copy before we do any mapping business.\r
657             // TODO: verify that undo/redo with 1:many sequence associated\r
658             // annotations can be undone correctly\r
659             AlignmentAnnotation ann = new AlignmentAnnotation(annotation);\r
660             annotation\r
661                     .createSequenceMapping(referedSeq, refSeqIndex, false);\r
662             annotation.adjustForAlignment();\r
663             referedSeq.addAlignmentAnnotation(annotation);\r
664             al.addAnnotation(annotation);\r
665             al.setAnnotationIndex(annotation,\r
666                     al.getAlignmentAnnotation().length\r
667                             - existingAnnotations - 1);\r
668             // and recover our virgin copy to use again if necessary.\r
669             annotation = ann;\r
670 \r
671           } while (refSeqId != null\r
672                   && (referedSeq = al.findName(referedSeq, refSeqId, true)) != null);\r
673         }\r
674         else\r
675         {\r
676           al.addAnnotation(annotation);\r
677           al.setAnnotationIndex(annotation,\r
678                   al.getAlignmentAnnotation().length - existingAnnotations\r
679                           - 1);\r
680         }\r
681       }\r
682 \r
683     } catch (Exception ex)\r
684     {\r
685       ex.printStackTrace();\r
686       System.out.println("Problem reading annotation file: " + ex);\r
687       return false;\r
688     }\r
689     return true;\r
690   }\r
691 \r
692   Annotation parseAnnotation(String string, int graphStyle)\r
693   {\r
694     boolean hasSymbols = (graphStyle == AlignmentAnnotation.NO_GRAPH); // don't do the glyph test if we don't want secondary structure\r
695     String desc = null, displayChar = null;\r
696     char ss = ' '; // secondaryStructure\r
697     float value = 0;\r
698     boolean parsedValue = false, dcset = false;\r
699 \r
700     // find colour here\r
701     java.awt.Color colour = null;\r
702     int i = string.indexOf("[");\r
703     int j = string.indexOf("]");\r
704     if (i > -1 && j > -1)\r
705     {\r
706       UserColourScheme ucs = new UserColourScheme();\r
707 \r
708       colour = ucs.getColourFromString(string.substring(i + 1, j));\r
709       if ( i>0 && string.charAt(i-1)==',')\r
710       {\r
711         // clip the preceding comma as well\r
712         i--;\r
713       }\r
714       string = string.substring(0, i) + string.substring(j + 1);\r
715     }\r
716 \r
717     StringTokenizer st = new StringTokenizer(string, ",", true);\r
718     String token;\r
719     boolean seenContent=false;\r
720     while (st.hasMoreTokens())\r
721     {\r
722       token = st.nextToken().trim();\r
723       if (token.equals(","))\r
724       {\r
725         if (!seenContent && parsedValue && !dcset)\r
726         {\r
727           // allow the value below the bar/line to be empty\r
728           dcset = true;\r
729           displayChar = " ";\r
730         }\r
731         seenContent = false;\r
732         continue;\r
733       } else {\r
734         seenContent = true;\r
735       }\r
736 \r
737       if (!parsedValue)\r
738       {\r
739         try\r
740         {\r
741           displayChar = token; \r
742           // foo\r
743           value = new Float(token).floatValue();\r
744           parsedValue = true;\r
745           continue;\r
746         } catch (NumberFormatException ex)\r
747         {\r
748         }\r
749       }\r
750 \r
751       if (hasSymbols && (token.equals("H") || token.equals("E") || token.equals(" ")))\r
752       {\r
753         // Either this character represents a helix or sheet\r
754         // or an integer which can be displayed\r
755         ss = token.charAt(0);\r
756         if (displayChar.equals(token.substring(0, 1)))\r
757         {\r
758           displayChar = "";\r
759         }\r
760       }\r
761       else if (desc == null)\r
762       {\r
763         desc = token;\r
764       }\r
765 \r
766     }\r
767 //    if (!dcset && string.charAt(string.length() - 1) == ',')\r
768 //    {\r
769 //      displayChar = " "; // empty display char symbol.\r
770 //    }\r
771     if (displayChar != null && displayChar.length() > 1 && desc != null\r
772             && desc.length() == 1)\r
773     {\r
774       String tmp = displayChar;\r
775       displayChar = desc;\r
776       desc = tmp;\r
777     }\r
778     /*\r
779      * In principle, this code will ensure that the Annotation element generated\r
780      * is renderable by any of the applet or application rendering code but\r
781      * instead we check for null strings when the display character is rendered.\r
782      * if (displayChar==null) { displayChar=""; }\r
783      */\r
784     Annotation anot = new Annotation(displayChar, desc, ss, value);\r
785 \r
786     anot.colour = colour;\r
787 \r
788     return anot;\r
789   }\r
790 \r
791   void colourAnnotations(AlignmentI al, String label, String colour)\r
792   {\r
793     UserColourScheme ucs = new UserColourScheme(colour);\r
794     Annotation[] annotations;\r
795     for (int i = 0; i < al.getAlignmentAnnotation().length; i++)\r
796     {\r
797       if (al.getAlignmentAnnotation()[i].label.equalsIgnoreCase(label))\r
798       {\r
799         annotations = al.getAlignmentAnnotation()[i].annotations;\r
800         for (int j = 0; j < annotations.length; j++)\r
801         {\r
802           if (annotations[j] != null)\r
803           {\r
804             annotations[j].colour = ucs.findColour('A');\r
805           }\r
806         }\r
807       }\r
808     }\r
809   }\r
810 \r
811   void combineAnnotations(AlignmentI al, StringTokenizer st)\r
812   {\r
813     int graphGroup = -1;\r
814     String group = st.nextToken();\r
815     // First make sure we are not overwriting the graphIndex\r
816     for (int i = 0; i < al.getAlignmentAnnotation().length; i++)\r
817     {\r
818       if (al.getAlignmentAnnotation()[i].label.equalsIgnoreCase(group))\r
819       {\r
820         graphGroup = al.getAlignmentAnnotation()[i].graphGroup + 1;\r
821         al.getAlignmentAnnotation()[i].graphGroup = graphGroup;\r
822         break;\r
823       }\r
824     }\r
825 \r
826     // Now update groups\r
827     while (st.hasMoreTokens())\r
828     {\r
829       group = st.nextToken();\r
830       for (int i = 0; i < al.getAlignmentAnnotation().length; i++)\r
831       {\r
832         if (al.getAlignmentAnnotation()[i].label.equalsIgnoreCase(group))\r
833         {\r
834           al.getAlignmentAnnotation()[i].graphGroup = graphGroup;\r
835           break;\r
836         }\r
837       }\r
838     }\r
839   }\r
840 \r
841   void addLine(AlignmentI al, StringTokenizer st)\r
842   {\r
843     String group = st.nextToken();\r
844     AlignmentAnnotation annotation = null;\r
845 \r
846     for (int i = 0; i < al.getAlignmentAnnotation().length; i++)\r
847     {\r
848       if (al.getAlignmentAnnotation()[i].label.equalsIgnoreCase(group))\r
849       {\r
850         annotation = al.getAlignmentAnnotation()[i];\r
851         break;\r
852       }\r
853     }\r
854 \r
855     if (annotation == null)\r
856     {\r
857       return;\r
858     }\r
859     float value = new Float(st.nextToken()).floatValue();\r
860     String label = st.hasMoreTokens() ? st.nextToken() : null;\r
861     java.awt.Color colour = null;\r
862     if (st.hasMoreTokens())\r
863     {\r
864       UserColourScheme ucs = new UserColourScheme(st.nextToken());\r
865       colour = ucs.findColour('A');\r
866     }\r
867 \r
868     annotation.setThreshold(new GraphLine(value, label, colour));\r
869   }\r
870 \r
871   void addGroup(AlignmentI al, StringTokenizer st)\r
872   {\r
873     SequenceGroup sg = new SequenceGroup();\r
874     sg.setName(st.nextToken());\r
875     String rng = "";\r
876     try\r
877     {\r
878       rng = st.nextToken();\r
879       if (rng.length() > 0 && !rng.startsWith("*"))\r
880       {\r
881         sg.setStartRes(Integer.parseInt(rng) - 1);\r
882       }\r
883       else\r
884       {\r
885         sg.setStartRes(0);\r
886       }\r
887       rng = st.nextToken();\r
888       if (rng.length() > 0 && !rng.startsWith("*"))\r
889       {\r
890         sg.setEndRes(Integer.parseInt(rng) - 1);\r
891       }\r
892       else\r
893       {\r
894         sg.setEndRes(al.getWidth() - 1);\r
895       }\r
896     } catch (Exception e)\r
897     {\r
898       System.err\r
899               .println("Couldn't parse Group Start or End Field as '*' or a valid column or sequence index: '"\r
900                       + rng + "' - assuming alignment width for group.");\r
901       // assume group is full width\r
902       sg.setStartRes(0);\r
903       sg.setEndRes(al.getWidth() - 1);\r
904     }\r
905 \r
906     String index = st.nextToken();\r
907     if (index.equals("-1"))\r
908     {\r
909       while (st.hasMoreElements())\r
910       {\r
911         sg.addSequence(al.findName(st.nextToken()), false);\r
912       }\r
913     }\r
914     else\r
915     {\r
916       StringTokenizer st2 = new StringTokenizer(index, ",");\r
917 \r
918       while (st2.hasMoreTokens())\r
919       {\r
920         String tmp = st2.nextToken();\r
921         if (tmp.equals("*"))\r
922         {\r
923           for (int i = 0; i < al.getHeight(); i++)\r
924           {\r
925             sg.addSequence(al.getSequenceAt(i), false);\r
926           }\r
927         }\r
928         else if (tmp.indexOf("-") >= 0)\r
929         {\r
930           StringTokenizer st3 = new StringTokenizer(tmp, "-");\r
931 \r
932           int start = (Integer.parseInt(st3.nextToken()));\r
933           int end = (Integer.parseInt(st3.nextToken()));\r
934 \r
935           if (end > start)\r
936           {\r
937             for (int i = start; i <= end; i++)\r
938             {\r
939               sg.addSequence(al.getSequenceAt(i - 1), false);\r
940             }\r
941           }\r
942         }\r
943         else\r
944         {\r
945           sg\r
946                   .addSequence(al.getSequenceAt(Integer.parseInt(tmp) - 1),\r
947                           false);\r
948         }\r
949       }\r
950     }\r
951 \r
952     if (refSeq != null)\r
953     {\r
954       sg.setStartRes(refSeq.findIndex(sg.getStartRes() + 1) - 1);\r
955       sg.setEndRes(refSeq.findIndex(sg.getEndRes() + 1) - 1);\r
956       sg.setSeqrep(refSeq);\r
957     }\r
958 \r
959     if (sg.getSize() > 0)\r
960     {\r
961       al.addGroup(sg);\r
962     }\r
963   }\r
964 \r
965   void addRowProperties(AlignmentI al, StringTokenizer st)\r
966   {\r
967     String label = st.nextToken(),keyValue,key,value;\r
968     boolean scaletofit=false,centerlab=false,showalllabs=false;\r
969     while (st.hasMoreTokens()) {\r
970       keyValue=st.nextToken();\r
971       key = keyValue.substring(0, keyValue.indexOf("="));\r
972       value = keyValue.substring(keyValue.indexOf("=") + 1);\r
973       if (key.equalsIgnoreCase("scaletofit")) {\r
974         scaletofit = Boolean.valueOf(value).booleanValue();\r
975       }\r
976         if (key.equalsIgnoreCase("showalllabs")) {\r
977           showalllabs = Boolean.valueOf(value).booleanValue();\r
978         }\r
979         if (key.equalsIgnoreCase("centrelabs")) {\r
980           centerlab = Boolean.valueOf(value).booleanValue();\r
981     }\r
982         AlignmentAnnotation[] alr = al.getAlignmentAnnotation(); \r
983         for (int i = 0; i < alr.length; i++)\r
984         {\r
985           if (alr[i].label.equalsIgnoreCase(label))\r
986           {\r
987             alr[i].centreColLabels = centerlab;\r
988             alr[i].scaleColLabel = scaletofit;\r
989             alr[i].showAllColLabels = showalllabs;\r
990           }\r
991         }\r
992     }\r
993   }\r
994   void addProperties(AlignmentI al, StringTokenizer st)\r
995   {\r
996 \r
997     // So far we have only added groups to the annotationHash,\r
998     // the idea is in the future properties can be added to\r
999     // alignments, other annotations etc\r
1000     if (al.getGroups() == null)\r
1001     {\r
1002       return;\r
1003     }\r
1004     SequenceGroup sg = null;\r
1005 \r
1006     String name = st.nextToken();\r
1007 \r
1008     Vector groups = al.getGroups();\r
1009     for (int i = 0; i < groups.size(); i++)\r
1010     {\r
1011       sg = (SequenceGroup) groups.elementAt(i);\r
1012       if (sg.getName().equals(name))\r
1013       {\r
1014         break;\r
1015       }\r
1016       else\r
1017       {\r
1018         sg = null;\r
1019       }\r
1020     }\r
1021 \r
1022     if (sg != null)\r
1023     {\r
1024       String keyValue, key, value;\r
1025       ColourSchemeI def = sg.cs;\r
1026       sg.cs = null;\r
1027       while (st.hasMoreTokens())\r
1028       {\r
1029         keyValue = st.nextToken();\r
1030         key = keyValue.substring(0, keyValue.indexOf("="));\r
1031         value = keyValue.substring(keyValue.indexOf("=") + 1);\r
1032 \r
1033         if (key.equalsIgnoreCase("description"))\r
1034         {\r
1035           sg.setDescription(value);\r
1036         }\r
1037         else if (key.equalsIgnoreCase("colour"))\r
1038         {\r
1039           sg.cs = ColourSchemeProperty.getColour(al, value);\r
1040         }\r
1041         else if (key.equalsIgnoreCase("pidThreshold"))\r
1042         {\r
1043           sg.cs.setThreshold(Integer.parseInt(value), true);\r
1044 \r
1045         }\r
1046         else if (key.equalsIgnoreCase("consThreshold"))\r
1047         {\r
1048           sg.cs.setConservationInc(Integer.parseInt(value));\r
1049           Conservation c = new Conservation("Group",\r
1050                   ResidueProperties.propHash, 3, sg.getSequences(null), sg\r
1051                           .getStartRes(), sg.getEndRes() + 1);\r
1052 \r
1053           c.calculate();\r
1054           c.verdict(false, 25);\r
1055 \r
1056           sg.cs.setConservation(c);\r
1057 \r
1058         }\r
1059         else if (key.equalsIgnoreCase("outlineColour"))\r
1060         {\r
1061           sg.setOutlineColour(new UserColourScheme(value).findColour('A'));\r
1062         }\r
1063         else if (key.equalsIgnoreCase("displayBoxes"))\r
1064         {\r
1065           sg.setDisplayBoxes(Boolean.valueOf(value).booleanValue());\r
1066         }\r
1067         else if (key.equalsIgnoreCase("showUnconserved"))\r
1068         {\r
1069           sg.setShowunconserved(Boolean.valueOf(value).booleanValue());\r
1070         }\r
1071         else if (key.equalsIgnoreCase("displayText"))\r
1072         {\r
1073           sg.setDisplayText(Boolean.valueOf(value).booleanValue());\r
1074         }\r
1075         else if (key.equalsIgnoreCase("colourText"))\r
1076         {\r
1077           sg.setColourText(Boolean.valueOf(value).booleanValue());\r
1078         }\r
1079         else if (key.equalsIgnoreCase("textCol1"))\r
1080         {\r
1081           sg.textColour = new UserColourScheme(value).findColour('A');\r
1082         }\r
1083         else if (key.equalsIgnoreCase("textCol2"))\r
1084         {\r
1085           sg.textColour2 = new UserColourScheme(value).findColour('A');\r
1086         }\r
1087         else if (key.equalsIgnoreCase("textColThreshold"))\r
1088         {\r
1089           sg.thresholdTextColour = Integer.parseInt(value);\r
1090         }\r
1091         else if (key.equalsIgnoreCase("idColour"))\r
1092         {\r
1093           // consider warning if colour doesn't resolve to a real colour\r
1094           sg.setIdColour((def = new UserColourScheme(value))\r
1095                   .findColour('A'));\r
1096         }\r
1097         else if (key.equalsIgnoreCase("hide"))\r
1098         {\r
1099           // see bug https://mantis.lifesci.dundee.ac.uk/view.php?id=25847\r
1100           sg.setHidereps(true);\r
1101         }\r
1102         else if (key.equalsIgnoreCase("hidecols"))\r
1103         {\r
1104           // see bug https://mantis.lifesci.dundee.ac.uk/view.php?id=25847\r
1105           sg.setHideCols(true);\r
1106         }\r
1107         sg.recalcConservation();\r
1108       }\r
1109       if (sg.cs == null)\r
1110       {\r
1111         sg.cs = def;\r
1112       }\r
1113     }\r
1114   }\r
1115 \r
1116   void setBelowAlignment(AlignmentI al, StringTokenizer st)\r
1117   {\r
1118     String token;\r
1119     AlignmentAnnotation aa;\r
1120     while (st.hasMoreTokens())\r
1121     {\r
1122       token = st.nextToken();\r
1123       for (int i = 0; i < al.getAlignmentAnnotation().length; i++)\r
1124       {\r
1125         aa = al.getAlignmentAnnotation()[i];\r
1126         if (aa.sequenceRef == refSeq && aa.label.equals(token))\r
1127         {\r
1128           aa.belowAlignment = true;\r
1129         }\r
1130       }\r
1131     }\r
1132   }\r
1133 \r
1134   void addAlignmentDetails(AlignmentI al, StringTokenizer st)\r
1135   {\r
1136     String keyValue, key, value;\r
1137     while (st.hasMoreTokens())\r
1138     {\r
1139       keyValue = st.nextToken();\r
1140       key = keyValue.substring(0, keyValue.indexOf("="));\r
1141       value = keyValue.substring(keyValue.indexOf("=") + 1);\r
1142       al.setProperty(key, value);\r
1143     }\r
1144   }\r
1145 \r
1146   /**\r
1147    * Write annotations as a CSV file of the form 'label, value, value, ...' for\r
1148    * each row.\r
1149    * \r
1150    * @param annotations\r
1151    * @return CSV file as a string.\r
1152    */\r
1153   public String printCSVAnnotations(AlignmentAnnotation[] annotations)\r
1154   {\r
1155     StringBuffer sp = new StringBuffer();\r
1156     for (int i = 0; i < annotations.length; i++)\r
1157     {\r
1158       String atos = annotations[i].toString();\r
1159       int p = 0;\r
1160       do\r
1161       {\r
1162         int cp = atos.indexOf("\n", p);\r
1163         sp.append(annotations[i].label);\r
1164         sp.append(",");\r
1165         if (cp > p)\r
1166         {\r
1167           sp.append(atos.substring(p, cp + 1));\r
1168         }\r
1169         else\r
1170         {\r
1171           sp.append(atos.substring(p));\r
1172           sp.append("\n");\r
1173         }\r
1174         p = cp + 1;\r
1175       } while (p > 0);\r
1176     }\r
1177     return sp.toString();\r
1178   }\r
1179 }\r