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