stockholm added as readable format (Lauren Lui commit)
[jalview.git] / src / jalview / io / AppletFormatAdapter.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5)
3  * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
10  * 
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.io;
19
20 import java.io.File;
21
22 import jalview.datamodel.*;
23
24 /**
25  * A low level class for alignment and feature IO with alignment formatting
26  * methods used by both applet and application for generating flat alignment
27  * files. It also holds the lists of magic format names that the applet and
28  * application will allow the user to read or write files with.
29  * 
30  * @author $author$
31  * @version $Revision$
32  */
33 public class AppletFormatAdapter
34 {
35   /**
36    * List of valid format strings used in the isValidFormat method
37    */
38   public static final String[] READABLE_FORMATS = new String[]
39   { "BLC", "CLUSTAL", "FASTA", "MSF", "PileUp", "PIR", "PFAM", "STH",
40       "PDB", "JnetFile" }; // , "SimpleBLAST" };
41
42   /**
43    * List of valid format strings for use by callers of the formatSequences
44    * method
45    */
46   public static final String[] WRITEABLE_FORMATS = new String[]
47   { "BLC", "CLUSTAL", "FASTA", "MSF", "PileUp", "PIR", "PFAM", "AMSA" };
48
49   /**
50    * List of extensions corresponding to file format types in WRITABLE_FNAMES
51    * that are writable by the application.
52    */
53   public static final String[] WRITABLE_EXTENSIONS = new String[]
54   { "fa, fasta, fastq", "aln", "pfam", "msf", "pir", "blc", "amsa", "jar" };
55
56   /**
57    * List of writable formats by the application. Order must correspond with the
58    * WRITABLE_EXTENSIONS list of formats.
59    */
60   public static final String[] WRITABLE_FNAMES = new String[]
61   { "Fasta", "Clustal", "PFAM", "MSF", "PIR", "BLC", "AMSA", "Jalview" };
62
63   /**
64    * List of readable format file extensions by application in order
65    * corresponding to READABLE_FNAMES
66    */
67   public static final String[] READABLE_EXTENSIONS = new String[]
68   { "fa, fasta, fastq", "aln", "pfam", "msf", "pir", "blc", "amsa", "jar", "sto" }; // ,
69                                                                              // ".blast"
70                                                                              // };
71
72   /**
73    * List of readable formats by application in order corresponding to
74    * READABLE_EXTENSIONS
75    */
76   public static final String[] READABLE_FNAMES = new String[]
77   { "Fasta", "Clustal", "PFAM", "MSF", "PIR", "BLC", "AMSA", "Jalview", "Stockholm" };// ,
78                                                                          // "SimpleBLAST"
79                                                                          // };
80
81   public static String INVALID_CHARACTERS = "Contains invalid characters";
82
83   // TODO: make these messages dynamic
84   public static String SUPPORTED_FORMATS = "Formats currently supported are\n"
85           + prettyPrint(READABLE_FORMATS);
86
87   /**
88    * 
89    * @param els
90    * @return grammatically correct(ish) list consisting of els elements.
91    */
92   public static String prettyPrint(String[] els)
93   {
94     StringBuffer list = new StringBuffer();
95     for (int i = 0, iSize = els.length - 1; i < iSize; i++)
96     {
97       list.append(els[i]);
98       list.append(",");
99     }
100     list.append(" and " + els[els.length - 1] + ".");
101     return list.toString();
102   }
103
104   public static String FILE = "File";
105
106   public static String URL = "URL";
107
108   public static String PASTE = "Paste";
109
110   public static String CLASSLOADER = "ClassLoader";
111
112   AlignFile afile = null;
113
114   String inFile;
115
116   /**
117    * check that this format is valid for reading
118    * 
119    * @param format
120    *          a format string to be compared with READABLE_FORMATS
121    * @return true if format is readable
122    */
123   public static final boolean isValidFormat(String format)
124   {
125     return isValidFormat(format, false);
126   }
127
128   /**
129    * validate format is valid for IO
130    * 
131    * @param format
132    *          a format string to be compared with either READABLE_FORMATS or
133    *          WRITEABLE_FORMATS
134    * @param forwriting
135    *          when true, format is checked for containment in WRITEABLE_FORMATS
136    * @return true if format is valid
137    */
138   public static final boolean isValidFormat(String format,
139           boolean forwriting)
140   {
141     boolean valid = false;
142     String[] format_list = (forwriting) ? WRITEABLE_FORMATS
143             : READABLE_FORMATS;
144     for (int i = 0; i < format_list.length; i++)
145     {
146       if (format_list[i].equalsIgnoreCase(format))
147       {
148         return true;
149       }
150     }
151
152     return valid;
153   }
154
155   /**
156    * Constructs the correct filetype parser for a characterised datasource
157    * 
158    * @param inFile
159    *          data/data location
160    * @param type
161    *          type of datasource
162    * @param format
163    *          File format of data provided by datasource
164    * 
165    * @return DOCUMENT ME!
166    */
167   public Alignment readFile(String inFile, String type, String format)
168           throws java.io.IOException
169   {
170     // TODO: generalise mapping between format string and io. class instances
171     // using Constructor.invoke reflection
172     this.inFile = inFile;
173     try
174     {
175       if (format.equals("FASTA"))
176       {
177         afile = new FastaFile(inFile, type);
178       }
179       else if (format.equals("MSF"))
180       {
181         afile = new MSFfile(inFile, type);
182       }
183       else if (format.equals("PileUp"))
184       {
185         afile = new PileUpfile(inFile, type);
186       }
187       else if (format.equals("CLUSTAL"))
188       {
189         afile = new ClustalFile(inFile, type);
190       }
191       else if (format.equals("BLC"))
192       {
193         afile = new BLCFile(inFile, type);
194       }
195       else if (format.equals("PIR"))
196       {
197         afile = new PIRFile(inFile, type);
198       }
199       else if (format.equals("PFAM"))
200       {
201         afile = new PfamFile(inFile, type);
202       }
203       else if (format.equals("JnetFile"))
204       {
205         afile = new JPredFile(inFile, type);
206         ((JPredFile) afile).removeNonSequences();
207       }
208       else if (format.equals("PDB"))
209       {
210         afile = new MCview.PDBfile(inFile, type);
211       }
212       else if (format.equals("STH"))
213       {
214         afile = new StockholmFile(inFile, type);
215       }
216       else if (format.equals("SimpleBLAST"))
217       {
218         afile = new SimpleBlastFile(inFile, type);
219       }
220
221       Alignment al = new Alignment(afile.getSeqsAsArray());
222
223       afile.addAnnotations(al);
224
225       return al;
226     } catch (Exception e)
227     {
228       e.printStackTrace();
229       System.err.println("Failed to read alignment using the '" + format
230               + "' reader.\n" + e);
231
232       if (e.getMessage() != null
233               && e.getMessage().startsWith(INVALID_CHARACTERS))
234       {
235         throw new java.io.IOException(e.getMessage());
236       }
237
238       // Finally test if the user has pasted just the sequence, no id
239       if (type.equalsIgnoreCase("Paste"))
240       {
241         try
242         {
243           // Possible sequence is just residues with no label
244           afile = new FastaFile(">UNKNOWN\n" + inFile, "Paste");
245           Alignment al = new Alignment(afile.getSeqsAsArray());
246           afile.addAnnotations(al);
247           return al;
248
249         } catch (Exception ex)
250         {
251           if (ex.toString().startsWith(INVALID_CHARACTERS))
252           {
253             throw new java.io.IOException(e.getMessage());
254           }
255
256           ex.printStackTrace();
257         }
258       }
259
260       // If we get to this stage, the format was not supported
261       throw new java.io.IOException(SUPPORTED_FORMATS);
262     }
263   }
264
265   /**
266    * Constructs the correct filetype parser for an already open datasource
267    * 
268    * @param source
269    *          an existing datasource
270    * @param format
271    *          File format of data that will be provided by datasource
272    * 
273    * @return DOCUMENT ME!
274    */
275   public Alignment readFromFile(FileParse source, String format)
276           throws java.io.IOException
277   {
278     // TODO: generalise mapping between format string and io. class instances
279     // using Constructor.invoke reflection
280     // This is exactly the same as the readFile method except we substitute
281     // 'inFile, type' with 'source'
282     this.inFile = source.getInFile();
283     String type = source.type;
284     try
285     {
286       if (format.equals("FASTA"))
287       {
288         afile = new FastaFile(source);
289       }
290       else if (format.equals("MSF"))
291       {
292         afile = new MSFfile(source);
293       }
294       else if (format.equals("PileUp"))
295       {
296         afile = new PileUpfile(source);
297       }
298       else if (format.equals("CLUSTAL"))
299       {
300         afile = new ClustalFile(source);
301       }
302       else if (format.equals("BLC"))
303       {
304         afile = new BLCFile(source);
305       }
306       else if (format.equals("PIR"))
307       {
308         afile = new PIRFile(source);
309       }
310       else if (format.equals("PFAM"))
311       {
312         afile = new PfamFile(source);
313       }
314       else if (format.equals("JnetFile"))
315       {
316         afile = new JPredFile(source);
317         ((JPredFile) afile).removeNonSequences();
318       }
319       else if (format.equals("PDB"))
320       {
321         afile = new MCview.PDBfile(source);
322       }
323       else if (format.equals("STH"))
324       {
325         afile = new StockholmFile(source);
326       }
327       else if (format.equals("SimpleBLAST"))
328       {
329         afile = new SimpleBlastFile(source);
330       }
331
332       Alignment al = new Alignment(afile.getSeqsAsArray());
333
334       afile.addAnnotations(al);
335
336       return al;
337     } catch (Exception e)
338     {
339       e.printStackTrace();
340       System.err.println("Failed to read alignment using the '" + format
341               + "' reader.\n" + e);
342
343       if (e.getMessage() != null
344               && e.getMessage().startsWith(INVALID_CHARACTERS))
345       {
346         throw new java.io.IOException(e.getMessage());
347       }
348
349       // Finally test if the user has pasted just the sequence, no id
350       if (type.equalsIgnoreCase("Paste"))
351       {
352         try
353         {
354           // Possible sequence is just residues with no label
355           afile = new FastaFile(">UNKNOWN\n" + inFile, "Paste");
356           Alignment al = new Alignment(afile.getSeqsAsArray());
357           afile.addAnnotations(al);
358           return al;
359
360         } catch (Exception ex)
361         {
362           if (ex.toString().startsWith(INVALID_CHARACTERS))
363           {
364             throw new java.io.IOException(e.getMessage());
365           }
366
367           ex.printStackTrace();
368         }
369       }
370
371       // If we get to this stage, the format was not supported
372       throw new java.io.IOException(SUPPORTED_FORMATS);
373     }
374   }
375
376   /**
377    * Construct an output class for an alignment in a particular filetype TODO:
378    * allow caller to detect errors and warnings encountered when generating
379    * output
380    * 
381    * @param format
382    *          string name of alignment format
383    * @param alignment
384    *          the alignment to be written out
385    * @param jvsuffix
386    *          passed to AlnFile class controls whether /START-END is added to
387    *          sequence names
388    * 
389    * @return alignment flat file contents
390    */
391   public String formatSequences(String format, AlignmentI alignment,
392           boolean jvsuffix)
393   {
394     try
395     {
396       AlignFile afile = null;
397
398       if (format.equalsIgnoreCase("FASTA"))
399       {
400         afile = new FastaFile();
401       }
402       else if (format.equalsIgnoreCase("MSF"))
403       {
404         afile = new MSFfile();
405       }
406       else if (format.equalsIgnoreCase("PileUp"))
407       {
408         afile = new PileUpfile();
409       }
410       else if (format.equalsIgnoreCase("CLUSTAL"))
411       {
412         afile = new ClustalFile();
413       }
414       else if (format.equalsIgnoreCase("BLC"))
415       {
416         afile = new BLCFile();
417       }
418       else if (format.equalsIgnoreCase("PIR"))
419       {
420         afile = new PIRFile();
421       }
422       else if (format.equalsIgnoreCase("PFAM"))
423       {
424         afile = new PfamFile();
425       }
426       else if (format.equalsIgnoreCase("STH"))
427       {
428         afile = new StockholmFile();
429       }
430       else if (format.equalsIgnoreCase("AMSA"))
431       {
432         afile = new AMSAFile(alignment);
433       }
434       else
435       {
436         throw new Exception(
437                 "Implementation error: Unknown file format string");
438       }
439
440       afile.addJVSuffix(jvsuffix);
441
442       afile.setSeqs(alignment.getSequencesArray());
443
444       String afileresp = afile.print();
445       if (afile.hasWarningMessage())
446       {
447         System.err.println("Warning raised when writing as " + format
448                 + " : " + afile.getWarningMessage());
449       }
450       return afileresp;
451     } catch (Exception e)
452     {
453       System.err.println("Failed to write alignment as a '" + format
454               + "' file\n");
455       e.printStackTrace();
456     }
457
458     return null;
459   }
460
461   public static void main(String[] args)
462   {
463     int i = 0;
464     while (i < args.length)
465     {
466       File f = new File(args[i]);
467       if (f.exists())
468       {
469         try
470         {
471           System.out.println("Reading file: " + f);
472           AppletFormatAdapter afa = new AppletFormatAdapter();
473           Runtime r = Runtime.getRuntime();
474           System.gc();
475           long memf = -r.totalMemory() + r.freeMemory();
476           long t1 = -System.currentTimeMillis();
477           Alignment al = afa.readFile(args[i], FILE, new IdentifyFile()
478                   .Identify(args[i], FILE));
479           t1 += System.currentTimeMillis();
480           System.gc();
481           memf += r.totalMemory() - r.freeMemory();
482           if (al != null)
483           {
484             System.out.println("Alignment contains " + al.getHeight()
485                     + " sequences and " + al.getWidth() + " columns.");
486             try
487             {
488               System.out.println(new AppletFormatAdapter().formatSequences(
489                       "FASTA", al, true));
490             } catch (Exception e)
491             {
492               System.err
493                       .println("Couln't format the alignment for output as a FASTA file.");
494               e.printStackTrace(System.err);
495             }
496           }
497           else
498           {
499             System.out.println("Couldn't read alignment");
500           }
501           System.out.println("Read took " + (t1 / 1000.0) + " seconds.");
502           System.out
503                   .println("Difference between free memory now and before is "
504                           + (memf / (1024.0 * 1024.0) * 1.0) + " MB");
505
506         } catch (Exception e)
507         {
508           System.err.println("Exception when dealing with " + i
509                   + "'th argument: " + args[i] + "\n" + e);
510         }
511       }
512       else
513       {
514         System.err.println("Ignoring argument '" + args[i] + "' (" + i
515                 + "'th)- not a readable file.");
516       }
517       i++;
518     }
519
520   }
521 }