seq size cant be less than 2 for tree
[jalview.git] / src / jalview / io / FeaturesFile.java
1 /*\r
2 * Jalview - A Sequence Alignment Editor and Viewer\r
3 * Copyright (C) 2005 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 jalview.datamodel.*;\r
22 \r
23 import java.io.*;\r
24 import java.util.*;\r
25 import jalview.schemes.UserColourScheme;\r
26 \r
27 \r
28 /**\r
29  * DOCUMENT ME!\r
30  *\r
31  * @author $author$\r
32  * @version $Revision$\r
33  */\r
34 public class FeaturesFile 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     /**\r
45      * Creates a new FeaturesFile object.\r
46      *\r
47      * @param inFile DOCUMENT ME!\r
48      * @param type DOCUMENT ME!\r
49      *\r
50      * @throws IOException DOCUMENT ME!\r
51      */\r
52     public FeaturesFile(String inFile, String type)\r
53        throws IOException\r
54     {\r
55         super(inFile, type);\r
56     }\r
57 \r
58     /**\r
59      * The Application can render HTML, but the applet will\r
60      * remove HTML tags and replace links with %LINK%\r
61      * Both need to read links in HTML however\r
62      *\r
63      * @throws IOException DOCUMENT ME!\r
64      */\r
65     public boolean parse(AlignmentI align,\r
66                          Hashtable colours,\r
67                          boolean removeHTML)\r
68     {\r
69       return parse(align, colours, null, removeHTML);\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             continue;\r
102 \r
103           st = new StringTokenizer(line, "\t");\r
104           if (st.countTokens()>1 && st.countTokens() < 4 )\r
105           {\r
106             GFFFile = false;\r
107             type = st.nextToken();\r
108             if (type.equalsIgnoreCase("startgroup"))\r
109             {\r
110               featureGroup = st.nextToken();\r
111               if (st.hasMoreElements())\r
112               {\r
113                 groupLink = st.nextToken();\r
114                 featureLink.put(featureGroup, groupLink);\r
115               }\r
116             }\r
117             else if (type.equalsIgnoreCase("endgroup"))\r
118             {\r
119               //We should check whether this is the current group,\r
120               //but at present theres no way of showing more than 1 group\r
121               st.nextToken();\r
122               featureGroup = null;\r
123               groupLink = null;\r
124             }\r
125             else\r
126             {\r
127               UserColourScheme ucs = new UserColourScheme(st.nextToken());\r
128               colours.put(type, ucs.findColour("A"));\r
129               if (st.hasMoreElements())\r
130               {\r
131                 String link = st.nextToken();\r
132                 typeLink.put(type, link);\r
133                 if(featureLink==null)\r
134                   featureLink = new Hashtable();\r
135                 featureLink.put(type, link);\r
136               }\r
137 \r
138             }\r
139             continue;\r
140           }\r
141 \r
142           while (st.hasMoreElements())\r
143           {\r
144 \r
145             if(GFFFile)\r
146             {\r
147               // Still possible this is an old Jalview file,\r
148               // which does not have type colours at the beginning\r
149               token = st.nextToken();\r
150               seq = align.findName(token);\r
151               if(seq != null)\r
152               {\r
153                 desc = st.nextToken();\r
154                 type = st.nextToken();\r
155                 start = Integer.parseInt(st.nextToken());\r
156                 end = Integer.parseInt(st.nextToken());\r
157                 try\r
158                 {\r
159                   score =  new Float(st.nextToken()).floatValue();\r
160                 }\r
161                 catch (NumberFormatException ex)\r
162                 {\r
163                   score = 0;\r
164                 }\r
165 \r
166                 sf = new SequenceFeature(type, desc, start, end, score, null);\r
167 \r
168                 try\r
169                 {\r
170                   sf.setValue("STRAND", st.nextToken());\r
171                   sf.setValue("FRAME", st.nextToken());\r
172                 }\r
173                 catch (Exception ex)\r
174                 {}\r
175 \r
176                 seq.getDatasetSequence().addSequenceFeature(sf);\r
177 \r
178                 break;\r
179               }\r
180             }\r
181 \r
182             if(GFFFile && seq==null)\r
183             {\r
184               desc = token;\r
185             }\r
186             else\r
187               desc = st.nextToken();\r
188 \r
189 \r
190             token = st.nextToken();\r
191             if (!token.equals("ID_NOT_SPECIFIED"))\r
192             {\r
193               seq = align.findName(token);\r
194               st.nextToken();\r
195             }\r
196             else\r
197             {\r
198               try{\r
199                 index = Integer.parseInt(st.nextToken());\r
200                 seq = align.getSequenceAt(index);\r
201               }\r
202               catch(NumberFormatException ex)\r
203               {\r
204                 seq = null;\r
205               }\r
206             }\r
207 \r
208             if(seq==null)\r
209             {\r
210               System.out.println("Sequence not found: "+line);\r
211               break;\r
212             }\r
213 \r
214             start = Integer.parseInt(st.nextToken());\r
215             end = Integer.parseInt(st.nextToken());\r
216 \r
217             type = st.nextToken();\r
218 \r
219             if (!colours.containsKey(type))\r
220             {\r
221               // Probably the old style groups file\r
222               UserColourScheme ucs = new UserColourScheme(type);\r
223               colours.put(type, ucs.findColour("A"));\r
224             }\r
225 \r
226             sf = new SequenceFeature(type, desc, "", start, end, featureGroup);\r
227 \r
228             seq.addSequenceFeature(sf);\r
229 \r
230             if(groupLink!=null && removeHTML)\r
231             {\r
232               sf.addLink(groupLink);\r
233               sf.description += "%LINK%";\r
234             }\r
235             if(typeLink.containsKey(type) && removeHTML)\r
236             {\r
237               sf.addLink(typeLink.get(type).toString());\r
238               sf.description += "%LINK%";\r
239             }\r
240 \r
241             parseDescriptionHTML(sf, removeHTML);\r
242 \r
243             //If we got here, its not a GFFFile\r
244             GFFFile = false;\r
245           }\r
246         }\r
247       }\r
248       catch (Exception ex)\r
249       {\r
250         System.out.println(line);\r
251         ex.printStackTrace();\r
252         System.out.println("Error parsing feature file: " + ex +"\n"+line);\r
253         return false;\r
254       }\r
255 \r
256       return true;\r
257     }\r
258 \r
259     void parseDescriptionHTML(SequenceFeature sf, boolean removeHTML)\r
260     {\r
261       StringBuffer sb = new StringBuffer();\r
262       StringTokenizer st = new StringTokenizer(sf.getDescription(), "<");\r
263       String token,  link;\r
264       while(st.hasMoreElements())\r
265       {\r
266         token = st.nextToken("<>");\r
267         if(token.equalsIgnoreCase("html") || token.startsWith("/"))\r
268           continue;\r
269 \r
270         if(token.startsWith("a href="))\r
271         {\r
272           link = token.substring(token.indexOf("\"")+1, token.length()-1);\r
273           String label = st.nextToken("<>");\r
274           sf.addLink(label+"|"+link);\r
275           sb.append(label+"%LINK%");\r
276         }\r
277         else if(token.equalsIgnoreCase("br"))\r
278           sb.append("\n");\r
279         else\r
280           sb.append(token);\r
281       }\r
282 \r
283       if(removeHTML)\r
284         sf.description = sb.toString();\r
285   }\r
286 \r
287     /**\r
288      * DOCUMENT ME!\r
289      *\r
290      * @param s DOCUMENT ME!\r
291      * @param len DOCUMENT ME!\r
292      * @param gaps DOCUMENT ME!\r
293      * @param displayId DOCUMENT ME!\r
294      *\r
295      * @return DOCUMENT ME!\r
296      */\r
297     public String printJalviewFormat(SequenceI [] seqs,\r
298                                      Hashtable visible)\r
299     {\r
300         StringBuffer out = new StringBuffer();\r
301         SequenceFeature [] next;\r
302 \r
303         if(visible==null || visible.size()<1)\r
304           return "No Features Visible";\r
305 \r
306         Enumeration en = visible.keys();\r
307         String type;\r
308         int color;\r
309         while( en.hasMoreElements() )\r
310         {\r
311           type = en.nextElement().toString();\r
312           color = Integer.parseInt( visible.get(type).toString() );\r
313           out.append(type + "\t"\r
314                      + jalview.util.Format.getHexString(\r
315                          new java.awt.Color(color)  )\r
316                      +"\n");\r
317         }\r
318 \r
319         //Work out which groups are both present and visible\r
320         Vector groups = new Vector();\r
321         int groupIndex = 0;\r
322 \r
323         for(int i=0; i<seqs.length; i++)\r
324         {\r
325           next = seqs[i].getSequenceFeatures();\r
326           if(next!=null)\r
327           {\r
328             for(int j=0; j<next.length; j++)\r
329             {\r
330               if (!visible.containsKey(next[j].type))\r
331                 continue;\r
332 \r
333               if (    next[j].featureGroup != null\r
334                   && !groups.contains(next[j].featureGroup))\r
335                 groups.addElement(next[j].featureGroup);\r
336             }\r
337           }\r
338         }\r
339 \r
340         String group = null;\r
341 \r
342         do\r
343         {\r
344 \r
345 \r
346           if (groups.size() > 0 && groupIndex < groups.size())\r
347           {\r
348             group = groups.elementAt(groupIndex).toString();\r
349             out.append("\nSTARTGROUP\t" + group + "\n");\r
350           }\r
351           else\r
352             group = null;\r
353 \r
354           for (int i = 0; i < seqs.length; i++)\r
355           {\r
356             next = seqs[i].getSequenceFeatures();\r
357             if (next != null)\r
358             {\r
359               for (int j = 0; j < next.length; j++)\r
360               {\r
361                 if (!visible.containsKey(next[j].type))\r
362                   continue;\r
363 \r
364                 if (group != null\r
365                     && (next[j].featureGroup==null\r
366                         || !next[j].featureGroup.equals(group))\r
367                   )\r
368                   continue;\r
369 \r
370                 if(group==null && next[j].featureGroup!=null)\r
371                   continue;\r
372 \r
373                 if(next[j].description==null || next[j].description.equals(""))\r
374                   out.append(next[j].type+"\t");\r
375                 else\r
376                   out.append(next[j].description + "\t");\r
377 \r
378                 out.append(  seqs[i].getName() + "\t-1\t"\r
379                            + next[j].begin + "\t"\r
380                            + next[j].end + "\t"\r
381                            + next[j].type + "\n"\r
382                     );\r
383               }\r
384             }\r
385           }\r
386 \r
387           if(group!=null)\r
388           {\r
389             out.append("ENDGROUP\t"+group+"\n");\r
390             groupIndex++;\r
391           }\r
392           else\r
393             break;\r
394 \r
395         }\r
396         while(groupIndex < groups.size()+1);\r
397 \r
398 \r
399       return out.toString();\r
400     }\r
401 \r
402     public String printGFFFormat(SequenceI [] seqs, Hashtable visible)\r
403     {\r
404       StringBuffer out = new StringBuffer();\r
405       SequenceFeature [] next;\r
406 \r
407       for(int i=0; i<seqs.length; i++)\r
408       {\r
409         if(seqs[i].getSequenceFeatures()!=null)\r
410         {\r
411           next = seqs[i].getSequenceFeatures();\r
412           for(int j=0; j<next.length; j++)\r
413           {\r
414             if(!visible.containsKey(next[j].type))\r
415               continue;\r
416 \r
417             out.append(seqs[i].getName() + "\t"\r
418                        + next[j].description + "\t"\r
419                        + next[j].type  + "\t"\r
420                        + next[j].begin + "\t"\r
421                        + next[j].end   + "\t"\r
422                        + next[j].score + "\t"\r
423                       );\r
424 \r
425             if(next[j].getValue("STRAND")!=null)\r
426               out.append(next[j].getValue("STRAND")+"\t");\r
427             else\r
428               out.append(".\t");\r
429             if(next[j].getValue("FRAME")!=null)\r
430               out.append(next[j].getValue("FRAME")+"\n");\r
431             else\r
432               out.append(".\n");\r
433 \r
434           }\r
435         }\r
436       }\r
437 \r
438       return out.toString();\r
439     }\r
440 \r
441     public void parse()\r
442     {\r
443       //IGNORED\r
444     }\r
445 \r
446     /**\r
447      * DOCUMENT ME!\r
448      *\r
449      * @return DOCUMENT ME!\r
450      */\r
451     public String print()\r
452     {\r
453         return "USE printGFFFormat() or printJalviewFormat()";\r
454     }\r
455 }\r