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