Formatting
[jalview.git] / src / jalview / io / FeaturesFile.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2007 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.util.*;\r
23 \r
24 import jalview.datamodel.*;\r
25 import jalview.schemes.*;\r
26 \r
27 /**\r
28  * DOCUMENT ME!\r
29  *\r
30  * @author $author$\r
31  * @version $Revision$\r
32  */\r
33 public class FeaturesFile\r
34     extends AlignFile\r
35 {\r
36   /**\r
37    * Creates a new FeaturesFile object.\r
38    */\r
39   public FeaturesFile()\r
40   {\r
41   }\r
42 \r
43   /**\r
44    * Creates a new FeaturesFile object.\r
45    *\r
46    * @param inFile DOCUMENT ME!\r
47    * @param type DOCUMENT ME!\r
48    *\r
49    * @throws IOException DOCUMENT ME!\r
50    */\r
51   public FeaturesFile(String inFile, String type)\r
52       throws IOException\r
53   {\r
54     super(inFile, type);\r
55   }\r
56 \r
57   /**\r
58    * The Application can render HTML, but the applet will\r
59    * remove HTML tags and replace links with %LINK%\r
60    * Both need to read links in HTML however\r
61    *\r
62    * @throws IOException DOCUMENT ME!\r
63    */\r
64   public boolean parse(AlignmentI align,\r
65                        Hashtable colours,\r
66                        boolean removeHTML)\r
67   {\r
68     return parse(align, colours, null, removeHTML);\r
69   }\r
70 \r
71   /**\r
72    * The Application can render HTML, but the applet will\r
73    * remove HTML tags and replace links with %LINK%\r
74    * Both need to read links in HTML however\r
75    *\r
76    * @throws IOException DOCUMENT ME!\r
77    */\r
78   public boolean parse(AlignmentI align,\r
79                        Hashtable colours,\r
80                        Hashtable featureLink,\r
81                        boolean removeHTML)\r
82   {\r
83     String line = null;\r
84     try\r
85     {\r
86       SequenceI seq = null;\r
87       String type, desc, token = null;\r
88 \r
89       int index, start, end;\r
90       float score;\r
91       StringTokenizer st;\r
92       SequenceFeature sf;\r
93       String featureGroup = null, groupLink = null;\r
94       Hashtable typeLink = new Hashtable();\r
95 \r
96       boolean GFFFile = true;\r
97 \r
98       while ( (line = nextLine()) != null)\r
99       {\r
100         if (line.startsWith("#"))\r
101         {\r
102           continue;\r
103         }\r
104 \r
105         st = new StringTokenizer(line, "\t");\r
106         if (st.countTokens() > 1 && st.countTokens() < 4)\r
107         {\r
108           GFFFile = false;\r
109           type = st.nextToken();\r
110           if (type.equalsIgnoreCase("startgroup"))\r
111           {\r
112             featureGroup = st.nextToken();\r
113             if (st.hasMoreElements())\r
114             {\r
115               groupLink = st.nextToken();\r
116               featureLink.put(featureGroup, groupLink);\r
117             }\r
118           }\r
119           else if (type.equalsIgnoreCase("endgroup"))\r
120           {\r
121             //We should check whether this is the current group,\r
122             //but at present theres no way of showing more than 1 group\r
123             st.nextToken();\r
124             featureGroup = null;\r
125             groupLink = null;\r
126           }\r
127           else\r
128           {\r
129             UserColourScheme ucs = new UserColourScheme(st.nextToken());\r
130             colours.put(type, ucs.findColour('A'));\r
131             if (st.hasMoreElements())\r
132             {\r
133               String link = st.nextToken();\r
134               typeLink.put(type, link);\r
135               if (featureLink == null)\r
136               {\r
137                 featureLink = new Hashtable();\r
138               }\r
139               featureLink.put(type, link);\r
140             }\r
141 \r
142           }\r
143           continue;\r
144         }\r
145 \r
146         while (st.hasMoreElements())\r
147         {\r
148 \r
149           if (GFFFile)\r
150           {\r
151             // Still possible this is an old Jalview file,\r
152             // which does not have type colours at the beginning\r
153             token = st.nextToken();\r
154             seq = align.findName(token);\r
155             if (seq != null)\r
156             {\r
157               desc = st.nextToken();\r
158               type = st.nextToken();\r
159               start = Integer.parseInt(st.nextToken());\r
160               end = Integer.parseInt(st.nextToken());\r
161               try\r
162               {\r
163                 score = new Float(st.nextToken()).floatValue();\r
164               }\r
165               catch (NumberFormatException ex)\r
166               {\r
167                 score = 0;\r
168               }\r
169 \r
170               sf = new SequenceFeature(type, desc, start, end, score, null);\r
171 \r
172               try\r
173               {\r
174                 sf.setValue("STRAND", st.nextToken());\r
175                 sf.setValue("FRAME", st.nextToken());\r
176               }\r
177               catch (Exception ex)\r
178               {}\r
179 \r
180               if (st.hasMoreTokens())\r
181               {\r
182                 StringBuffer attributes = new StringBuffer();\r
183                 while (st.hasMoreTokens())\r
184                 {\r
185                   attributes.append("\t" + st.nextElement());\r
186                 }\r
187                 sf.setValue("ATTRIBUTES", attributes.toString());\r
188               }\r
189 \r
190               seq.addSequenceFeature(sf);\r
191 \r
192               break;\r
193             }\r
194           }\r
195 \r
196           if (GFFFile && seq == null)\r
197           {\r
198             desc = token;\r
199           }\r
200           else\r
201           {\r
202             desc = st.nextToken();\r
203           }\r
204 \r
205           token = st.nextToken();\r
206           if (!token.equals("ID_NOT_SPECIFIED"))\r
207           {\r
208             seq = align.findName(token);\r
209             st.nextToken();\r
210           }\r
211           else\r
212           {\r
213             try\r
214             {\r
215               index = Integer.parseInt(st.nextToken());\r
216               seq = align.getSequenceAt(index);\r
217             }\r
218             catch (NumberFormatException ex)\r
219             {\r
220               seq = null;\r
221             }\r
222           }\r
223 \r
224           if (seq == null)\r
225           {\r
226             System.out.println("Sequence not found: " + line);\r
227             break;\r
228           }\r
229 \r
230           start = Integer.parseInt(st.nextToken());\r
231           end = Integer.parseInt(st.nextToken());\r
232 \r
233           type = st.nextToken();\r
234 \r
235           if (!colours.containsKey(type))\r
236           {\r
237             // Probably the old style groups file\r
238             UserColourScheme ucs = new UserColourScheme(type);\r
239             colours.put(type, ucs.findColour('A'));\r
240           }\r
241 \r
242           sf = new SequenceFeature(type, desc, "", start, end, featureGroup);\r
243 \r
244           seq.addSequenceFeature(sf);\r
245 \r
246           if (groupLink != null && removeHTML)\r
247           {\r
248             sf.addLink(groupLink);\r
249             sf.description += "%LINK%";\r
250           }\r
251           if (typeLink.containsKey(type) && removeHTML)\r
252           {\r
253             sf.addLink(typeLink.get(type).toString());\r
254             sf.description += "%LINK%";\r
255           }\r
256 \r
257           parseDescriptionHTML(sf, removeHTML);\r
258 \r
259           //If we got here, its not a GFFFile\r
260           GFFFile = false;\r
261         }\r
262       }\r
263     }\r
264     catch (Exception ex)\r
265     {\r
266       System.out.println(line);\r
267       System.out.println("Error parsing feature file: " + ex + "\n" + line);\r
268       return false;\r
269     }\r
270 \r
271     return true;\r
272   }\r
273 \r
274   public void parseDescriptionHTML(SequenceFeature sf, boolean removeHTML)\r
275   {\r
276     if (sf.getDescription() == null)\r
277     {\r
278       return;\r
279     }\r
280 \r
281     if (removeHTML && sf.getDescription().toUpperCase().indexOf("<HTML>") == -1)\r
282     {\r
283       removeHTML = false;\r
284     }\r
285 \r
286     StringBuffer sb = new StringBuffer();\r
287     StringTokenizer st = new StringTokenizer(sf.getDescription(), "<");\r
288     String token, link;\r
289     int startTag;\r
290     String tag = null;\r
291     while (st.hasMoreElements())\r
292     {\r
293       token = st.nextToken("&>");\r
294       if (token.equalsIgnoreCase("html") || token.startsWith("/"))\r
295       {\r
296         continue;\r
297       }\r
298 \r
299       tag = null;\r
300       startTag = token.indexOf("<");\r
301 \r
302       if (startTag > -1)\r
303       {\r
304         tag = token.substring(startTag + 1);\r
305         token = token.substring(0, startTag);\r
306       }\r
307 \r
308       if (tag != null && tag.toUpperCase().startsWith("A HREF="))\r
309       {\r
310         if (token.length() > 0)\r
311         {\r
312           sb.append(token);\r
313         }\r
314         link = tag.substring(tag.indexOf("\"") + 1, tag.length() - 1);\r
315         String label = st.nextToken("<>");\r
316         sf.addLink(label + "|" + link);\r
317         sb.append(label + "%LINK%");\r
318       }\r
319       else if (tag != null && tag.equalsIgnoreCase("br"))\r
320       {\r
321         sb.append("\n");\r
322       }\r
323       else if (token.startsWith("lt;"))\r
324       {\r
325         sb.append("<" + token.substring(3));\r
326       }\r
327       else if (token.startsWith("gt;"))\r
328       {\r
329         sb.append(">" + token.substring(3));\r
330       }\r
331       else if (token.startsWith("amp;"))\r
332       {\r
333         sb.append("&" + token.substring(4));\r
334       }\r
335       else\r
336       {\r
337         sb.append(token);\r
338       }\r
339     }\r
340 \r
341     if (removeHTML)\r
342     {\r
343       sf.description = sb.toString();\r
344     }\r
345 \r
346   }\r
347 \r
348   /**\r
349    * DOCUMENT ME!\r
350    *\r
351    * @param s DOCUMENT ME!\r
352    * @param len DOCUMENT ME!\r
353    * @param gaps DOCUMENT ME!\r
354    * @param displayId DOCUMENT ME!\r
355    *\r
356    * @return DOCUMENT ME!\r
357    */\r
358   public String printJalviewFormat(SequenceI[] seqs,\r
359                                    Hashtable visible)\r
360   {\r
361     StringBuffer out = new StringBuffer();\r
362     SequenceFeature[] next;\r
363 \r
364     if (visible == null || visible.size() < 1)\r
365     {\r
366       return "No Features Visible";\r
367     }\r
368 \r
369     Enumeration en = visible.keys();\r
370     String type;\r
371     int color;\r
372     while (en.hasMoreElements())\r
373     {\r
374       type = en.nextElement().toString();\r
375       color = Integer.parseInt(visible.get(type).toString());\r
376       out.append(type + "\t"\r
377                  + jalview.util.Format.getHexString(\r
378                      new java.awt.Color(color))\r
379                  + "\n");\r
380     }\r
381 \r
382     //Work out which groups are both present and visible\r
383     Vector groups = new Vector();\r
384     int groupIndex = 0;\r
385 \r
386     for (int i = 0; i < seqs.length; i++)\r
387     {\r
388       next = seqs[i].getSequenceFeatures();\r
389       if (next != null)\r
390       {\r
391         for (int j = 0; j < next.length; j++)\r
392         {\r
393           if (!visible.containsKey(next[j].type))\r
394           {\r
395             continue;\r
396           }\r
397 \r
398           if (next[j].featureGroup != null\r
399               && !groups.contains(next[j].featureGroup))\r
400           {\r
401             groups.addElement(next[j].featureGroup);\r
402           }\r
403         }\r
404       }\r
405     }\r
406 \r
407     String group = null;\r
408 \r
409     do\r
410     {\r
411 \r
412       if (groups.size() > 0 && groupIndex < groups.size())\r
413       {\r
414         group = groups.elementAt(groupIndex).toString();\r
415         out.append("\nSTARTGROUP\t" + group + "\n");\r
416       }\r
417       else\r
418       {\r
419         group = null;\r
420       }\r
421 \r
422       for (int i = 0; i < seqs.length; i++)\r
423       {\r
424         next = seqs[i].getSequenceFeatures();\r
425         if (next != null)\r
426         {\r
427           for (int j = 0; j < next.length; j++)\r
428           {\r
429             if (!visible.containsKey(next[j].type))\r
430             {\r
431               continue;\r
432             }\r
433 \r
434             if (group != null\r
435                 && (next[j].featureGroup == null\r
436                     || !next[j].featureGroup.equals(group))\r
437                 )\r
438             {\r
439               continue;\r
440             }\r
441 \r
442             if (group == null && next[j].featureGroup != null)\r
443             {\r
444               continue;\r
445             }\r
446 \r
447             if (next[j].description == null || next[j].description.equals(""))\r
448             {\r
449               out.append(next[j].type + "\t");\r
450             }\r
451             else\r
452             {\r
453               if (next[j].links != null\r
454                   && next[j].getDescription().indexOf("<html>") == -1)\r
455               {\r
456                 out.append("<html>");\r
457               }\r
458 \r
459               out.append(next[j].description + " ");\r
460               if (next[j].links != null)\r
461               {\r
462                 for (int l = 0; l < next[j].links.size(); l++)\r
463                 {\r
464                   String label = next[j].links.elementAt(l).toString();\r
465                   String href = label.substring(label.indexOf("|") + 1);\r
466                   label = label.substring(0, label.indexOf("|"));\r
467 \r
468                   if (next[j].description.indexOf(href) == -1)\r
469                   {\r
470                     out.append("<a href=\""\r
471                                + href\r
472                                + "\">"\r
473                                + label\r
474                                + "</a>");\r
475                   }\r
476                 }\r
477 \r
478                 if (next[j].getDescription().indexOf("</html>") == -1)\r
479                 {\r
480                   out.append("</html>");\r
481                 }\r
482               }\r
483 \r
484               out.append("\t");\r
485             }\r
486 \r
487             out.append(seqs[i].getName() + "\t-1\t"\r
488                        + next[j].begin + "\t"\r
489                        + next[j].end + "\t"\r
490                        + next[j].type + "\n"\r
491                 );\r
492           }\r
493         }\r
494       }\r
495 \r
496       if (group != null)\r
497       {\r
498         out.append("ENDGROUP\t" + group + "\n");\r
499         groupIndex++;\r
500       }\r
501       else\r
502       {\r
503         break;\r
504       }\r
505 \r
506     }\r
507     while (groupIndex < groups.size() + 1);\r
508 \r
509     return out.toString();\r
510   }\r
511 \r
512   public String printGFFFormat(SequenceI[] seqs, Hashtable visible)\r
513   {\r
514     StringBuffer out = new StringBuffer();\r
515     SequenceFeature[] next;\r
516     String source;\r
517 \r
518     for (int i = 0; i < seqs.length; i++)\r
519     {\r
520       if (seqs[i].getSequenceFeatures() != null)\r
521       {\r
522         next = seqs[i].getSequenceFeatures();\r
523         for (int j = 0; j < next.length; j++)\r
524         {\r
525           if (!visible.containsKey(next[j].type))\r
526           {\r
527             continue;\r
528           }\r
529 \r
530           source = next[j].featureGroup;\r
531           if (source == null)\r
532           {\r
533             source = next[j].getDescription();\r
534           }\r
535 \r
536           out.append(seqs[i].getName() + "\t"\r
537                      + source + "\t"\r
538                      + next[j].type + "\t"\r
539                      + next[j].begin + "\t"\r
540                      + next[j].end + "\t"\r
541                      + next[j].score + "\t"\r
542               );\r
543 \r
544           if (next[j].getValue("STRAND") != null)\r
545           {\r
546             out.append(next[j].getValue("STRAND") + "\t");\r
547           }\r
548           else\r
549           {\r
550             out.append(".\t");\r
551           }\r
552 \r
553           if (next[j].getValue("FRAME") != null)\r
554           {\r
555             out.append(next[j].getValue("FRAME"));\r
556           }\r
557           else\r
558           {\r
559             out.append(".");\r
560           }\r
561 \r
562           if (next[j].getValue("ATTRIBUTES") != null)\r
563           {\r
564             out.append(next[j].getValue("ATTRIBUTES"));\r
565           }\r
566 \r
567           out.append("\n");\r
568 \r
569         }\r
570       }\r
571     }\r
572 \r
573     return out.toString();\r
574   }\r
575 \r
576   public void parse()\r
577   {\r
578     //IGNORED\r
579   }\r
580 \r
581   /**\r
582    * DOCUMENT ME!\r
583    *\r
584    * @return DOCUMENT ME!\r
585    */\r
586   public String print()\r
587   {\r
588     return "USE printGFFFormat() or printJalviewFormat()";\r
589   }\r
590 }\r