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