aac48eb0fe46c6366a736ecd868f8df285ce1ca4
[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     Hashtable autoAnnots=new Hashtable();\r
437     try\r
438     {\r
439       BufferedReader in = null;\r
440       if (protocol.equals(AppletFormatAdapter.FILE))\r
441       {\r
442         in = new BufferedReader(new FileReader(file));\r
443       }\r
444       else if (protocol.equals(AppletFormatAdapter.URL))\r
445       {\r
446         URL url = new URL(file);\r
447         in = new BufferedReader(new InputStreamReader(url.openStream()));\r
448       }\r
449       else if (protocol.equals(AppletFormatAdapter.PASTE))\r
450       {\r
451         in = new BufferedReader(new StringReader(file));\r
452       }\r
453       else if (protocol.equals(AppletFormatAdapter.CLASSLOADER))\r
454       {\r
455         java.io.InputStream is = getClass().getResourceAsStream("/" + file);\r
456         if (is != null)\r
457         {\r
458           in = new BufferedReader(new java.io.InputStreamReader(is));\r
459         }\r
460       }\r
461 \r
462       String line, label, description, token;\r
463       int graphStyle, index;\r
464       int refSeqIndex = 1;\r
465       int existingAnnotations = 0;\r
466       // when true - will add new rows regardless of whether they are duplicate auto-annotation like consensus or conservation graphs\r
467       boolean overrideAutoAnnot=false; \r
468       if (al.getAlignmentAnnotation() != null)\r
469       {\r
470         existingAnnotations = al.getAlignmentAnnotation().length;\r
471         if (existingAnnotations>0)\r
472         {\r
473           AlignmentAnnotation[] aa = al.getAlignmentAnnotation();\r
474           for (int aai=0;aai<aa.length;aai++)\r
475           {\r
476             if (aa[aai].autoCalculated)\r
477             {\r
478               // make a note of the name and description\r
479               autoAnnots.put(aa[aai].graph+"\t"+aa[aai].label+"\t"+aa[aai].description+"\t"+(aa[aai].sequenceRef!=null ? aa[aai].sequenceRef.getDisplayId(true) : "")\r
480                       ,new Integer(1));\r
481             }\r
482           }\r
483         }\r
484       }\r
485 \r
486       int alWidth = al.getWidth();\r
487 \r
488       StringTokenizer st;\r
489       Annotation[] annotations;\r
490       AlignmentAnnotation annotation = null;\r
491 \r
492       // First confirm this is an Annotation file\r
493       boolean jvAnnotationFile = false;\r
494       while ((line = in.readLine()) != null)\r
495       {\r
496         if (line.indexOf("#") == 0)\r
497         {\r
498           continue;\r
499         }\r
500 \r
501         if (line.indexOf("JALVIEW_ANNOTATION") > -1)\r
502         {\r
503           jvAnnotationFile = true;\r
504           break;\r
505         }\r
506       }\r
507 \r
508       if (!jvAnnotationFile)\r
509       {\r
510         in.close();\r
511         return false;\r
512       }\r
513 \r
514       while ((line = in.readLine()) != null)\r
515       {\r
516         if (line.indexOf("#") == 0\r
517                 || line.indexOf("JALVIEW_ANNOTATION") > -1\r
518                 || line.length() == 0)\r
519         {\r
520           continue;\r
521         }\r
522 \r
523         st = new StringTokenizer(line, "\t");\r
524         token = st.nextToken();\r
525         if (token.equalsIgnoreCase("COLOUR"))\r
526         {\r
527           // TODO: use graduated colour def'n here too\r
528           colourAnnotations(al, st.nextToken(), st.nextToken());\r
529           continue;\r
530         }\r
531 \r
532         else if (token.equalsIgnoreCase("COMBINE"))\r
533         {\r
534           combineAnnotations(al, st);\r
535           continue;\r
536         }\r
537         else if (token.equalsIgnoreCase("ROWPROPERTIES"))\r
538         {\r
539           addRowProperties(al, st);\r
540           continue;\r
541         }\r
542         else if (token.equalsIgnoreCase("GRAPHLINE"))\r
543         {\r
544           addLine(al, st);\r
545           continue;\r
546         }\r
547 \r
548         else if (token.equalsIgnoreCase("SEQUENCE_REF"))\r
549         {\r
550           if (st.hasMoreTokens())\r
551           {\r
552             refSeq = al.findName(refSeqId = st.nextToken());\r
553             if (refSeq == null)\r
554             {\r
555               refSeqId = null;\r
556             }\r
557             try\r
558             {\r
559               refSeqIndex = Integer.parseInt(st.nextToken());\r
560               if (refSeqIndex < 1)\r
561               {\r
562                 refSeqIndex = 1;\r
563                 System.out\r
564                         .println("WARNING: SEQUENCE_REF index must be > 0 in AnnotationFile");\r
565               }\r
566             } catch (Exception ex)\r
567             {\r
568               refSeqIndex = 1;\r
569             }\r
570           }\r
571           else\r
572           {\r
573             refSeq = null;\r
574             refSeqId = null;\r
575           }\r
576           continue;\r
577         }\r
578 \r
579         else if (token.equalsIgnoreCase("SEQUENCE_GROUP"))\r
580         {\r
581           addGroup(al, st);\r
582           continue;\r
583         }\r
584 \r
585         else if (token.equalsIgnoreCase("PROPERTIES"))\r
586         {\r
587           addProperties(al, st);\r
588           continue;\r
589         }\r
590 \r
591         else if (token.equalsIgnoreCase("BELOW_ALIGNMENT"))\r
592         {\r
593           setBelowAlignment(al, st);\r
594           continue;\r
595         }\r
596         else if (token.equalsIgnoreCase("ALIGNMENT"))\r
597         {\r
598           addAlignmentDetails(al, st);\r
599           continue;\r
600         }\r
601 \r
602         graphStyle = AlignmentAnnotation.getGraphValueFromString(token);\r
603         label = st.nextToken();\r
604 \r
605         index = 0;\r
606         annotations = new Annotation[alWidth];\r
607         description = null;\r
608         float score = Float.NaN;\r
609 \r
610         if (st.hasMoreTokens())\r
611         {\r
612           line = st.nextToken();\r
613 \r
614           if (line.indexOf("|") == -1)\r
615           {\r
616             description = line;\r
617             if (st.hasMoreTokens())\r
618               line = st.nextToken();\r
619           }\r
620 \r
621           if (st.hasMoreTokens())\r
622           {\r
623             // This must be the score\r
624             score = Float.valueOf(st.nextToken()).floatValue();\r
625           }\r
626 \r
627           st = new StringTokenizer(line, "|", true);\r
628 \r
629           boolean emptyColumn = true;\r
630           boolean onlyOneElement = (st.countTokens() == 1);\r
631 \r
632           while (st.hasMoreElements() && index < alWidth)\r
633           {\r
634             token = st.nextToken().trim();\r
635 \r
636             if (onlyOneElement)\r
637             {\r
638               try\r
639               {\r
640                 score = Float.valueOf(token).floatValue();\r
641                 break;\r
642               } catch (NumberFormatException ex)\r
643               {\r
644               }\r
645             }\r
646 \r
647             if (token.equals("|"))\r
648             {\r
649               if (emptyColumn)\r
650               {\r
651                 index++;\r
652               }\r
653 \r
654               emptyColumn = true;\r
655             }\r
656             else\r
657             {\r
658               annotations[index++] = parseAnnotation(token, graphStyle);\r
659               emptyColumn = false;\r
660             }\r
661           }\r
662 \r
663         }\r
664         \r
665         annotation = new AlignmentAnnotation(label, description,\r
666                 (index == 0) ? null : annotations, 0, 0, graphStyle);\r
667 \r
668         annotation.score = score;\r
669         if (!overrideAutoAnnot && autoAnnots.containsKey(annotation.graph+"\t"+annotation.label+"\t"+annotation.description+"\t"+(refSeq!=null ? refSeq.getDisplayId(true) : "")))\r
670         {\r
671           // skip - we've already got an automatic annotation of this type.\r
672           continue;\r
673         }\r
674         // otherwise add it!\r
675         if (refSeq != null)\r
676         {\r
677           \r
678           annotation.belowAlignment = false;\r
679           // make a copy of refSeq so we can find other matches in the alignment\r
680           SequenceI referedSeq = refSeq;\r
681           do\r
682           {\r
683             // copy before we do any mapping business.\r
684             // TODO: verify that undo/redo with 1:many sequence associated\r
685             // annotations can be undone correctly\r
686             AlignmentAnnotation ann = new AlignmentAnnotation(annotation);\r
687             annotation\r
688                     .createSequenceMapping(referedSeq, refSeqIndex, false);\r
689             annotation.adjustForAlignment();\r
690             referedSeq.addAlignmentAnnotation(annotation);\r
691             al.addAnnotation(annotation);\r
692             al.setAnnotationIndex(annotation,\r
693                     al.getAlignmentAnnotation().length\r
694                             - existingAnnotations - 1);\r
695             // and recover our virgin copy to use again if necessary.\r
696             annotation = ann;\r
697 \r
698           } while (refSeqId != null\r
699                   && (referedSeq = al.findName(referedSeq, refSeqId, true)) != null);\r
700         }\r
701         else\r
702         {\r
703           al.addAnnotation(annotation);\r
704           al.setAnnotationIndex(annotation,\r
705                   al.getAlignmentAnnotation().length - existingAnnotations\r
706                           - 1);\r
707         }\r
708       }\r
709 \r
710     } catch (Exception ex)\r
711     {\r
712       ex.printStackTrace();\r
713       System.out.println("Problem reading annotation file: " + ex);\r
714       return false;\r
715     }\r
716     return true;\r
717   }\r
718 \r
719   Annotation parseAnnotation(String string, int graphStyle)\r
720   {\r
721     boolean hasSymbols = (graphStyle == AlignmentAnnotation.NO_GRAPH); // don't do the glyph test if we don't want secondary structure\r
722     String desc = null, displayChar = null;\r
723     char ss = ' '; // secondaryStructure\r
724     float value = 0;\r
725     boolean parsedValue = false, dcset = false;\r
726 \r
727     // find colour here\r
728     java.awt.Color colour = null;\r
729     int i = string.indexOf("[");\r
730     int j = string.indexOf("]");\r
731     if (i > -1 && j > -1)\r
732     {\r
733       UserColourScheme ucs = new UserColourScheme();\r
734 \r
735       colour = ucs.getColourFromString(string.substring(i + 1, j));\r
736       if ( i>0 && string.charAt(i-1)==',')\r
737       {\r
738         // clip the preceding comma as well\r
739         i--;\r
740       }\r
741       string = string.substring(0, i) + string.substring(j + 1);\r
742     }\r
743 \r
744     StringTokenizer st = new StringTokenizer(string, ",", true);\r
745     String token;\r
746     boolean seenContent=false;\r
747     int pass=0;\r
748     while (st.hasMoreTokens())\r
749     {\r
750       pass++;\r
751       token = st.nextToken().trim();\r
752       if (token.equals(","))\r
753       {\r
754         if (!seenContent && parsedValue && !dcset)\r
755         {\r
756           // allow the value below the bar/line to be empty\r
757           dcset = true;\r
758           displayChar = " ";\r
759         }\r
760         seenContent = false;\r
761         continue;\r
762       } else {\r
763         seenContent = true;\r
764       }\r
765 \r
766       if (!parsedValue)\r
767       {\r
768         try\r
769         {\r
770           displayChar = token; \r
771           // foo\r
772           value = new Float(token).floatValue();\r
773           parsedValue = true;\r
774           continue;\r
775         } catch (NumberFormatException ex)\r
776         {\r
777         }\r
778       } else {\r
779         if (token.length()==1)\r
780         {\r
781           displayChar = token;\r
782         }\r
783       }\r
784       if (hasSymbols && (token.equals("H") || token.equals("E") || token.equals(" ")))\r
785       {\r
786         // Either this character represents a helix or sheet\r
787         // or an integer which can be displayed\r
788         ss = token.charAt(0);\r
789         if (displayChar.equals(token.substring(0, 1)))\r
790         {\r
791           displayChar = "";\r
792         }\r
793       }\r
794       else if (desc == null || (parsedValue && pass>2))\r
795       {\r
796         desc = token;\r
797       }\r
798 \r
799     }\r
800 //    if (!dcset && string.charAt(string.length() - 1) == ',')\r
801 //    {\r
802 //      displayChar = " "; // empty display char symbol.\r
803 //    }\r
804     if (displayChar != null && desc!=null && desc.length()==1) \r
805     {\r
806       if (displayChar.length() > 1)\r
807       {\r
808         // switch desc and displayChar - legacy support  \r
809         String tmp = displayChar;\r
810         displayChar = desc;\r
811         desc = tmp;\r
812       } else {\r
813         if (displayChar.equals(desc)) {\r
814           // duplicate label - hangover from the 'robust parser' above\r
815           desc = null;\r
816         }\r
817       }\r
818     }\r
819     Annotation anot = new Annotation(displayChar, desc, ss, value);\r
820 \r
821     anot.colour = colour;\r
822 \r
823     return anot;\r
824   }\r
825 \r
826   void colourAnnotations(AlignmentI al, String label, String colour)\r
827   {\r
828     UserColourScheme ucs = new UserColourScheme(colour);\r
829     Annotation[] annotations;\r
830     for (int i = 0; i < al.getAlignmentAnnotation().length; i++)\r
831     {\r
832       if (al.getAlignmentAnnotation()[i].label.equalsIgnoreCase(label))\r
833       {\r
834         annotations = al.getAlignmentAnnotation()[i].annotations;\r
835         for (int j = 0; j < annotations.length; j++)\r
836         {\r
837           if (annotations[j] != null)\r
838           {\r
839             annotations[j].colour = ucs.findColour('A');\r
840           }\r
841         }\r
842       }\r
843     }\r
844   }\r
845 \r
846   void combineAnnotations(AlignmentI al, StringTokenizer st)\r
847   {\r
848     int graphGroup = -1;\r
849     String group = st.nextToken();\r
850     // First make sure we are not overwriting the graphIndex\r
851     for (int i = 0; i < al.getAlignmentAnnotation().length; i++)\r
852     {\r
853       if (al.getAlignmentAnnotation()[i].label.equalsIgnoreCase(group))\r
854       {\r
855         graphGroup = al.getAlignmentAnnotation()[i].graphGroup + 1;\r
856         al.getAlignmentAnnotation()[i].graphGroup = graphGroup;\r
857         break;\r
858       }\r
859     }\r
860 \r
861     // Now update groups\r
862     while (st.hasMoreTokens())\r
863     {\r
864       group = st.nextToken();\r
865       for (int i = 0; i < al.getAlignmentAnnotation().length; i++)\r
866       {\r
867         if (al.getAlignmentAnnotation()[i].label.equalsIgnoreCase(group))\r
868         {\r
869           al.getAlignmentAnnotation()[i].graphGroup = graphGroup;\r
870           break;\r
871         }\r
872       }\r
873     }\r
874   }\r
875 \r
876   void addLine(AlignmentI al, StringTokenizer st)\r
877   {\r
878     String group = st.nextToken();\r
879     AlignmentAnnotation annotation = null;\r
880 \r
881     for (int i = 0; i < al.getAlignmentAnnotation().length; i++)\r
882     {\r
883       if (al.getAlignmentAnnotation()[i].label.equalsIgnoreCase(group))\r
884       {\r
885         annotation = al.getAlignmentAnnotation()[i];\r
886         break;\r
887       }\r
888     }\r
889 \r
890     if (annotation == null)\r
891     {\r
892       return;\r
893     }\r
894     float value = new Float(st.nextToken()).floatValue();\r
895     String label = st.hasMoreTokens() ? st.nextToken() : null;\r
896     java.awt.Color colour = null;\r
897     if (st.hasMoreTokens())\r
898     {\r
899       UserColourScheme ucs = new UserColourScheme(st.nextToken());\r
900       colour = ucs.findColour('A');\r
901     }\r
902 \r
903     annotation.setThreshold(new GraphLine(value, label, colour));\r
904   }\r
905 \r
906   void addGroup(AlignmentI al, StringTokenizer st)\r
907   {\r
908     SequenceGroup sg = new SequenceGroup();\r
909     sg.setName(st.nextToken());\r
910     String rng = "";\r
911     try\r
912     {\r
913       rng = st.nextToken();\r
914       if (rng.length() > 0 && !rng.startsWith("*"))\r
915       {\r
916         sg.setStartRes(Integer.parseInt(rng) - 1);\r
917       }\r
918       else\r
919       {\r
920         sg.setStartRes(0);\r
921       }\r
922       rng = st.nextToken();\r
923       if (rng.length() > 0 && !rng.startsWith("*"))\r
924       {\r
925         sg.setEndRes(Integer.parseInt(rng) - 1);\r
926       }\r
927       else\r
928       {\r
929         sg.setEndRes(al.getWidth() - 1);\r
930       }\r
931     } catch (Exception e)\r
932     {\r
933       System.err\r
934               .println("Couldn't parse Group Start or End Field as '*' or a valid column or sequence index: '"\r
935                       + rng + "' - assuming alignment width for group.");\r
936       // assume group is full width\r
937       sg.setStartRes(0);\r
938       sg.setEndRes(al.getWidth() - 1);\r
939     }\r
940 \r
941     String index = st.nextToken();\r
942     if (index.equals("-1"))\r
943     {\r
944       while (st.hasMoreElements())\r
945       {\r
946         sg.addSequence(al.findName(st.nextToken()), false);\r
947       }\r
948     }\r
949     else\r
950     {\r
951       StringTokenizer st2 = new StringTokenizer(index, ",");\r
952 \r
953       while (st2.hasMoreTokens())\r
954       {\r
955         String tmp = st2.nextToken();\r
956         if (tmp.equals("*"))\r
957         {\r
958           for (int i = 0; i < al.getHeight(); i++)\r
959           {\r
960             sg.addSequence(al.getSequenceAt(i), false);\r
961           }\r
962         }\r
963         else if (tmp.indexOf("-") >= 0)\r
964         {\r
965           StringTokenizer st3 = new StringTokenizer(tmp, "-");\r
966 \r
967           int start = (Integer.parseInt(st3.nextToken()));\r
968           int end = (Integer.parseInt(st3.nextToken()));\r
969 \r
970           if (end > start)\r
971           {\r
972             for (int i = start; i <= end; i++)\r
973             {\r
974               sg.addSequence(al.getSequenceAt(i - 1), false);\r
975             }\r
976           }\r
977         }\r
978         else\r
979         {\r
980           sg\r
981                   .addSequence(al.getSequenceAt(Integer.parseInt(tmp) - 1),\r
982                           false);\r
983         }\r
984       }\r
985     }\r
986 \r
987     if (refSeq != null)\r
988     {\r
989       sg.setStartRes(refSeq.findIndex(sg.getStartRes() + 1) - 1);\r
990       sg.setEndRes(refSeq.findIndex(sg.getEndRes() + 1) - 1);\r
991       sg.setSeqrep(refSeq);\r
992     }\r
993 \r
994     if (sg.getSize() > 0)\r
995     {\r
996       al.addGroup(sg);\r
997     }\r
998   }\r
999 \r
1000   void addRowProperties(AlignmentI al, StringTokenizer st)\r
1001   {\r
1002     String label = st.nextToken(),keyValue,key,value;\r
1003     boolean scaletofit=false,centerlab=false,showalllabs=false;\r
1004     while (st.hasMoreTokens()) {\r
1005       keyValue=st.nextToken();\r
1006       key = keyValue.substring(0, keyValue.indexOf("="));\r
1007       value = keyValue.substring(keyValue.indexOf("=") + 1);\r
1008       if (key.equalsIgnoreCase("scaletofit")) {\r
1009         scaletofit = Boolean.valueOf(value).booleanValue();\r
1010       }\r
1011         if (key.equalsIgnoreCase("showalllabs")) {\r
1012           showalllabs = Boolean.valueOf(value).booleanValue();\r
1013         }\r
1014         if (key.equalsIgnoreCase("centrelabs")) {\r
1015           centerlab = Boolean.valueOf(value).booleanValue();\r
1016     }\r
1017         AlignmentAnnotation[] alr = al.getAlignmentAnnotation(); \r
1018         for (int i = 0; i < alr.length; i++)\r
1019         {\r
1020           if (alr[i].label.equalsIgnoreCase(label))\r
1021           {\r
1022             alr[i].centreColLabels = centerlab;\r
1023             alr[i].scaleColLabel = scaletofit;\r
1024             alr[i].showAllColLabels = showalllabs;\r
1025           }\r
1026         }\r
1027     }\r
1028   }\r
1029   void addProperties(AlignmentI al, StringTokenizer st)\r
1030   {\r
1031 \r
1032     // So far we have only added groups to the annotationHash,\r
1033     // the idea is in the future properties can be added to\r
1034     // alignments, other annotations etc\r
1035     if (al.getGroups() == null)\r
1036     {\r
1037       return;\r
1038     }\r
1039     SequenceGroup sg = null;\r
1040 \r
1041     String name = st.nextToken();\r
1042 \r
1043     Vector groups = al.getGroups();\r
1044     for (int i = 0; i < groups.size(); i++)\r
1045     {\r
1046       sg = (SequenceGroup) groups.elementAt(i);\r
1047       if (sg.getName().equals(name))\r
1048       {\r
1049         break;\r
1050       }\r
1051       else\r
1052       {\r
1053         sg = null;\r
1054       }\r
1055     }\r
1056 \r
1057     if (sg != null)\r
1058     {\r
1059       String keyValue, key, value;\r
1060       ColourSchemeI def = sg.cs;\r
1061       sg.cs = null;\r
1062       while (st.hasMoreTokens())\r
1063       {\r
1064         keyValue = st.nextToken();\r
1065         key = keyValue.substring(0, keyValue.indexOf("="));\r
1066         value = keyValue.substring(keyValue.indexOf("=") + 1);\r
1067 \r
1068         if (key.equalsIgnoreCase("description"))\r
1069         {\r
1070           sg.setDescription(value);\r
1071         }\r
1072         else if (key.equalsIgnoreCase("colour"))\r
1073         {\r
1074           sg.cs = ColourSchemeProperty.getColour(al, value);\r
1075         }\r
1076         else if (key.equalsIgnoreCase("pidThreshold"))\r
1077         {\r
1078           sg.cs.setThreshold(Integer.parseInt(value), true);\r
1079 \r
1080         }\r
1081         else if (key.equalsIgnoreCase("consThreshold"))\r
1082         {\r
1083           sg.cs.setConservationInc(Integer.parseInt(value));\r
1084           Conservation c = new Conservation("Group",\r
1085                   ResidueProperties.propHash, 3, sg.getSequences(null), sg\r
1086                           .getStartRes(), sg.getEndRes() + 1);\r
1087 \r
1088           c.calculate();\r
1089           c.verdict(false, 25);\r
1090 \r
1091           sg.cs.setConservation(c);\r
1092 \r
1093         }\r
1094         else if (key.equalsIgnoreCase("outlineColour"))\r
1095         {\r
1096           sg.setOutlineColour(new UserColourScheme(value).findColour('A'));\r
1097         }\r
1098         else if (key.equalsIgnoreCase("displayBoxes"))\r
1099         {\r
1100           sg.setDisplayBoxes(Boolean.valueOf(value).booleanValue());\r
1101         }\r
1102         else if (key.equalsIgnoreCase("showUnconserved"))\r
1103         {\r
1104           sg.setShowunconserved(Boolean.valueOf(value).booleanValue());\r
1105         }\r
1106         else if (key.equalsIgnoreCase("displayText"))\r
1107         {\r
1108           sg.setDisplayText(Boolean.valueOf(value).booleanValue());\r
1109         }\r
1110         else if (key.equalsIgnoreCase("colourText"))\r
1111         {\r
1112           sg.setColourText(Boolean.valueOf(value).booleanValue());\r
1113         }\r
1114         else if (key.equalsIgnoreCase("textCol1"))\r
1115         {\r
1116           sg.textColour = new UserColourScheme(value).findColour('A');\r
1117         }\r
1118         else if (key.equalsIgnoreCase("textCol2"))\r
1119         {\r
1120           sg.textColour2 = new UserColourScheme(value).findColour('A');\r
1121         }\r
1122         else if (key.equalsIgnoreCase("textColThreshold"))\r
1123         {\r
1124           sg.thresholdTextColour = Integer.parseInt(value);\r
1125         }\r
1126         else if (key.equalsIgnoreCase("idColour"))\r
1127         {\r
1128           // consider warning if colour doesn't resolve to a real colour\r
1129           sg.setIdColour((def = new UserColourScheme(value))\r
1130                   .findColour('A'));\r
1131         }\r
1132         else if (key.equalsIgnoreCase("hide"))\r
1133         {\r
1134           // see bug https://mantis.lifesci.dundee.ac.uk/view.php?id=25847\r
1135           sg.setHidereps(true);\r
1136         }\r
1137         else if (key.equalsIgnoreCase("hidecols"))\r
1138         {\r
1139           // see bug https://mantis.lifesci.dundee.ac.uk/view.php?id=25847\r
1140           sg.setHideCols(true);\r
1141         }\r
1142         sg.recalcConservation();\r
1143       }\r
1144       if (sg.cs == null)\r
1145       {\r
1146         sg.cs = def;\r
1147       }\r
1148     }\r
1149   }\r
1150 \r
1151   void setBelowAlignment(AlignmentI al, StringTokenizer st)\r
1152   {\r
1153     String token;\r
1154     AlignmentAnnotation aa;\r
1155     while (st.hasMoreTokens())\r
1156     {\r
1157       token = st.nextToken();\r
1158       for (int i = 0; i < al.getAlignmentAnnotation().length; i++)\r
1159       {\r
1160         aa = al.getAlignmentAnnotation()[i];\r
1161         if (aa.sequenceRef == refSeq && aa.label.equals(token))\r
1162         {\r
1163           aa.belowAlignment = true;\r
1164         }\r
1165       }\r
1166     }\r
1167   }\r
1168 \r
1169   void addAlignmentDetails(AlignmentI al, StringTokenizer st)\r
1170   {\r
1171     String keyValue, key, value;\r
1172     while (st.hasMoreTokens())\r
1173     {\r
1174       keyValue = st.nextToken();\r
1175       key = keyValue.substring(0, keyValue.indexOf("="));\r
1176       value = keyValue.substring(keyValue.indexOf("=") + 1);\r
1177       al.setProperty(key, value);\r
1178     }\r
1179   }\r
1180 \r
1181   /**\r
1182    * Write annotations as a CSV file of the form 'label, value, value, ...' for\r
1183    * each row.\r
1184    * \r
1185    * @param annotations\r
1186    * @return CSV file as a string.\r
1187    */\r
1188   public String printCSVAnnotations(AlignmentAnnotation[] annotations)\r
1189   {\r
1190     StringBuffer sp = new StringBuffer();\r
1191     for (int i = 0; i < annotations.length; i++)\r
1192     {\r
1193       String atos = annotations[i].toString();\r
1194       int p = 0;\r
1195       do\r
1196       {\r
1197         int cp = atos.indexOf("\n", p);\r
1198         sp.append(annotations[i].label);\r
1199         sp.append(",");\r
1200         if (cp > p)\r
1201         {\r
1202           sp.append(atos.substring(p, cp + 1));\r
1203         }\r
1204         else\r
1205         {\r
1206           sp.append(atos.substring(p));\r
1207           sp.append("\n");\r
1208         }\r
1209         p = cp + 1;\r
1210       } while (p > 0);\r
1211     }\r
1212     return sp.toString();\r
1213   }\r
1214 }\r