c7b057578fbccdb3974aec814a330f0fe3ab9ba3
[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     return isValidFormat(format, false);
117   }
118   /**
119    * validate format is valid for IO
120    * @param format a format string to be compared with either READABLE_FORMATS or WRITEABLE_FORMATS
121    * @param forwriting when true, format is checked for containment in WRITEABLE_FORMATS
122    * @return true if format is valid
123    */
124   public static final boolean isValidFormat(String format, boolean forwriting)
125   {
126     boolean valid = false;
127     String[] format_list = (forwriting) ? WRITEABLE_FORMATS : READABLE_FORMATS;
128     for (int i = 0; i < format_list.length; i++)
129     {
130       if (format_list[i].equalsIgnoreCase(format))
131       {
132         return true;
133       }
134     }
135
136     return valid;
137   }
138   
139   /**
140    * Constructs the correct filetype parser for a characterised datasource
141    *
142    * @param inFile data/data location
143    * @param type type of datasource
144    * @param format File format of data provided by datasource
145    *
146    * @return DOCUMENT ME!
147    */
148   public Alignment readFile(String inFile, String type, String format)
149       throws java.io.IOException
150   {
151     // TODO: generalise mapping between format string and io. class instances using Constructor.invoke reflection
152     this.inFile = inFile;
153     try
154     {
155       if (format.equals("FASTA"))
156       {
157         afile = new FastaFile(inFile, type);
158       }
159       else if (format.equals("MSF"))
160       {
161         afile = new MSFfile(inFile, type);
162       }
163       else if (format.equals("PileUp"))
164       {
165         afile = new PileUpfile(inFile, type);
166       }
167       else if (format.equals("CLUSTAL"))
168       {
169         afile = new ClustalFile(inFile, type);
170       }
171       else if (format.equals("BLC"))
172       {
173         afile = new BLCFile(inFile, type);
174       }
175       else if (format.equals("PIR"))
176       {
177         afile = new PIRFile(inFile, type);
178       }
179       else if (format.equals("PFAM"))
180       {
181         afile = new PfamFile(inFile, type);
182       }
183       else if (format.equals("JnetFile"))
184       {
185         afile = new JPredFile(inFile, type);
186         ( (JPredFile) afile).removeNonSequences();
187       }
188       else if (format.equals("PDB"))
189       {
190         afile = new MCview.PDBfile(inFile, type);
191       }
192       else if (format.equals("STH"))
193       {
194         afile = new StockholmFile(inFile, type);
195       }
196
197       Alignment al = new Alignment(afile.getSeqsAsArray());
198
199       afile.addAnnotations(al);
200
201       return al;
202     }
203     catch (Exception e)
204     {
205       e.printStackTrace();
206       System.err.println("Failed to read alignment using the '" + format +
207                          "' reader.\n" + e);
208
209       if (e.getMessage() != null &&
210           e.getMessage().startsWith(INVALID_CHARACTERS))
211       {
212         throw new java.io.IOException(e.getMessage());
213       }
214
215       // Finally test if the user has pasted just the sequence, no id
216       if (type.equalsIgnoreCase("Paste"))
217       {
218         try
219         {
220           // Possible sequence is just residues with no label
221           afile = new FastaFile(">UNKNOWN\n" + inFile, "Paste");
222           Alignment al = new Alignment(afile.getSeqsAsArray());
223           afile.addAnnotations(al);
224           return al;
225
226         }
227         catch (Exception ex)
228         {
229           if (ex.toString().startsWith(INVALID_CHARACTERS))
230           {
231             throw new java.io.IOException(e.getMessage());
232           }
233
234           ex.printStackTrace();
235         }
236       }
237
238       // If we get to this stage, the format was not supported
239       throw new java.io.IOException(SUPPORTED_FORMATS);
240     }
241   }
242   /**
243    * Constructs the correct filetype parser for an already open datasource
244    *
245    * @param source an existing datasource
246    * @param format File format of data that will be provided by datasource
247    *
248    * @return DOCUMENT ME!
249    */
250   public Alignment readFromFile(FileParse source, String format)
251       throws java.io.IOException
252   {
253     // TODO: generalise mapping between format string and io. class instances using Constructor.invoke reflection
254     // This is exactly the same as the readFile method except we substitute 'inFile, type' with 'source'
255     this.inFile = source.getInFile();
256     String type = source.type;
257     try
258     {
259       if (format.equals("FASTA"))
260       {
261         afile = new FastaFile(source);
262       }
263       else if (format.equals("MSF"))
264       {
265         afile = new MSFfile(source);
266       }
267       else if (format.equals("PileUp"))
268       {
269         afile = new PileUpfile(source);
270       }
271       else if (format.equals("CLUSTAL"))
272       {
273         afile = new ClustalFile(source);
274       }
275       else if (format.equals("BLC"))
276       {
277         afile = new BLCFile(source);
278       }
279       else if (format.equals("PIR"))
280       {
281         afile = new PIRFile(source);
282       }
283       else if (format.equals("PFAM"))
284       {
285         afile = new PfamFile(source);
286       }
287       else if (format.equals("JnetFile"))
288       {
289         afile = new JPredFile(source);
290         ( (JPredFile) afile).removeNonSequences();
291       }
292       else if (format.equals("PDB"))
293       {
294         afile = new MCview.PDBfile(source);
295       }
296       else if (format.equals("STH"))
297       {
298         afile = new StockholmFile(source);
299       }
300
301       Alignment al = new Alignment(afile.getSeqsAsArray());
302
303       afile.addAnnotations(al);
304
305       return al;
306     }
307     catch (Exception e)
308     {
309       e.printStackTrace();
310       System.err.println("Failed to read alignment using the '" + format +
311                          "' reader.\n" + e);
312
313       if (e.getMessage() != null &&
314           e.getMessage().startsWith(INVALID_CHARACTERS))
315       {
316         throw new java.io.IOException(e.getMessage());
317       }
318
319       // Finally test if the user has pasted just the sequence, no id
320       if (type.equalsIgnoreCase("Paste"))
321       {
322         try
323         {
324           // Possible sequence is just residues with no label
325           afile = new FastaFile(">UNKNOWN\n" + inFile, "Paste");
326           Alignment al = new Alignment(afile.getSeqsAsArray());
327           afile.addAnnotations(al);
328           return al;
329
330         }
331         catch (Exception ex)
332         {
333           if (ex.toString().startsWith(INVALID_CHARACTERS))
334           {
335             throw new java.io.IOException(e.getMessage());
336           }
337
338           ex.printStackTrace();
339         }
340       }
341
342       // If we get to this stage, the format was not supported
343       throw new java.io.IOException(SUPPORTED_FORMATS);
344     }
345   }
346   
347   /**
348    * Construct an output class for an alignment in a particular filetype
349    *
350    * @param format string name of alignment format 
351    * @param alignment the alignment to be written out
352    * @param jvsuffix passed to AlnFile class controls whether /START-END is added to sequence names
353    *
354    * @return alignment flat file contents
355    */
356   public String formatSequences(String format,
357                                 AlignmentI alignment,
358                                 boolean jvsuffix)
359   {
360     try
361     {
362       AlignFile afile = null;
363
364       if (format.equalsIgnoreCase("FASTA"))
365       {
366         afile = new FastaFile();
367       }
368       else if (format.equalsIgnoreCase("MSF"))
369       {
370         afile = new MSFfile();
371       }
372       else if (format.equalsIgnoreCase("PileUp"))
373       {
374         afile = new PileUpfile();
375       }
376       else if (format.equalsIgnoreCase("CLUSTAL"))
377       {
378         afile = new ClustalFile();
379       }
380       else if (format.equalsIgnoreCase("BLC"))
381       {
382         afile = new BLCFile();
383       }
384       else if (format.equalsIgnoreCase("PIR"))
385       {
386         afile = new PIRFile();
387       }
388       else if (format.equalsIgnoreCase("PFAM"))
389       {
390         afile = new PfamFile();
391       }
392       else if (format.equalsIgnoreCase("STH"))
393       {
394         afile = new StockholmFile();
395       }
396       else if (format.equalsIgnoreCase("AMSA"))
397       {
398         afile = new AMSAFile(alignment);
399       } else {
400         throw new Exception("Implementation error: Unknown file format string");
401       }
402
403       afile.addJVSuffix(jvsuffix);
404
405       afile.setSeqs(alignment.getSequencesArray());
406
407       return afile.print();
408     }
409     catch (Exception e)
410     {
411       System.err.println("Failed to write alignment as a '" + format +
412                          "' file\n");
413       e.printStackTrace();
414     }
415
416     return null;
417   }
418   public static void main(String[] args)
419   {
420     int i=0;
421     while (i<args.length)
422     {
423       File f = new File(args[i]);
424       if (f.exists())
425       {
426         try {
427           System.out.println("Reading file: "+f);
428           AppletFormatAdapter afa = new AppletFormatAdapter();
429           Runtime r = Runtime.getRuntime();
430           System.gc();
431           long memf = -r.totalMemory()+r.freeMemory();
432           long t1 = -System.currentTimeMillis();
433           Alignment al = afa.readFile(args[i], FILE, new IdentifyFile().Identify(args[i], FILE));
434           t1 +=System.currentTimeMillis();
435           System.gc();
436           memf += r.totalMemory()-r.freeMemory();
437           if (al!=null)
438           {
439             System.out.println("Alignment contains "+al.getHeight()+" sequences and "+al.getWidth()+" columns.");
440           } else {
441             System.out.println("Couldn't read alignment");
442           }
443           System.out.println("Read took "+(t1/1000.0)+" seconds.");
444           System.out.println("Difference between free memory now and before is "+(memf/(1024.0*1024.0)*1.0)+" MB");
445           
446         } catch (Exception e)
447         {
448           System.err.println("Exception when dealing with "+i+"'th argument: "+args[i]+"\n"+e);
449         }
450       } else {
451           System.err.println("Ignoring argument '"+args[i]+"' ("+i+"'th)- not a readable file.");
452       }
453       i++;
454     }
455     
456   }
457 }