Merge branch 'develop' into menard
[jalview.git] / src / jalview / io / IdentifyFile.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3  * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, 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.*;
21
22 /**
23  * DOCUMENT ME!
24  * 
25  * @author $author$
26  * @version $Revision$
27  */
28 public class IdentifyFile
29 {
30   /**
31    * Identify a datasource's file content.
32    * 
33    * @note Do not use this method for stream sources - create a FileParse object
34    *       instead.
35    * 
36    * @param file
37    *          DOCUMENT ME!
38    * @param protocol
39    *          DOCUMENT ME!
40    * @return ID String
41    */
42   public String Identify(String file, String protocol)
43   {
44     String emessage = "UNIDENTIFIED FILE PARSING ERROR";
45     FileParse parser = null;
46     try
47     {
48       parser = new FileParse(file, protocol);
49       if (parser.isValid())
50       {
51         return Identify(parser);
52       }
53     } catch (Exception e)
54     {
55       System.err.println("Error whilst identifying");
56       e.printStackTrace(System.err);
57       emessage = e.getMessage();
58     }
59     if (parser != null)
60       return parser.errormessage;
61     return emessage;
62   }
63
64   public String Identify(FileParse source)
65   {
66     return Identify(source, true); // preserves original behaviour prior to
67     // version 2.3
68   }
69
70   /**
71    * Identify contents of source, closing it or resetting source to start
72    * afterwards.
73    * 
74    * @param source
75    * @param closeSource
76    * @return filetype string
77    */
78   public String Identify(FileParse source, boolean closeSource)
79   {
80     String reply = "PFAM";
81     String data;
82     int length = 0;
83     boolean lineswereskipped = false;
84     boolean isBinary = false; // true if length is non-zero and non-printable
85     // characters are encountered
86     try
87     {
88       if (!closeSource)
89       {
90         source.mark();
91       }
92       while ((data = source.nextLine()) != null)
93       {
94         length += data.length();
95         if (!lineswereskipped)
96         {
97           for (int i = 0; !isBinary && i < data.length(); i++)
98           {
99             char c = data.charAt(i);
100             isBinary = (c < 32 && c != '\t' && c != '\n' && c != '\r'
101                     && c != 5 && c != 27); // nominal binary character filter
102             // excluding CR, LF, tab,DEL and ^E
103             // for certain blast ids
104           }
105         }
106         if (isBinary)
107         {
108           // jar files are special - since they contain all sorts of random
109           // characters.
110           if (source.inFile != null)
111           {
112             String fileStr = source.inFile.getName();
113             // possibly a Jalview archive.
114             if (fileStr.lastIndexOf(".jar") > -1
115                     || fileStr.lastIndexOf(".zip") > -1)
116             {
117               reply = "Jalview";
118             }
119           }
120           if (!lineswereskipped && data.startsWith("PK"))
121           {
122             reply = "Jalview"; // archive.
123             break;
124           }
125         }
126         data = data.toUpperCase();
127
128         if ((data.indexOf("# STOCKHOLM") > -1))
129         {
130           reply = "STH";
131
132           break;
133         }
134         
135         if ((data.indexOf("<") > -1))
136         {
137           reply = "RNAML";
138           
139           break;
140         }
141
142         if ((data.length() < 1) || (data.indexOf("#") == 0))
143         {
144           lineswereskipped = true;
145           continue;
146         }
147
148         if (data.indexOf("PILEUP") > -1)
149         {
150           reply = "PileUp";
151
152           break;
153         }
154
155         if ((data.indexOf("//") == 0)
156                 || ((data.indexOf("!!") > -1) && (data.indexOf("!!") < data
157                         .indexOf("_MULTIPLE_ALIGNMENT "))))
158         {
159           reply = "MSF";
160
161           break;
162         }
163         else if (data.indexOf("CLUSTAL") > -1)
164         {
165           reply = "CLUSTAL";
166
167           break;
168         }
169         
170         
171         else if (data.indexOf(">") > -1)
172         {
173           // FASTA, PIR file or BLC file
174           boolean checkPIR = false, starterm = false;
175           if ((data.indexOf(">P1;") > -1) || (data.indexOf(">DL;") > -1))
176           {
177             // watch for PIR file attributes
178             checkPIR = true;
179             reply = "PIR";
180           }
181           // could also be BLC file, read next line to confirm
182           data = source.nextLine();
183
184           if (data.indexOf(">") > -1)
185           {
186             reply = "BLC";
187           }
188           else
189           {
190             // Is this a single line BLC file?
191             String data1 = source.nextLine();
192             String data2 = source.nextLine();
193             if (checkPIR)
194             {
195               starterm = (data1 != null && data1.indexOf("*") > -1)
196                       || (data2 != null && data2.indexOf("*") > -1);
197             }
198             if (data2 != null && data.indexOf("*") > -1)
199             {
200               if (data.indexOf("*") == data2.indexOf("*"))
201               {
202                 reply = "BLC";
203               }
204               // otherwise can still possibly be a PIR file
205             }
206             else
207             {
208               reply = "FASTA";
209               // TODO : AMSA File is indicated if there is annotation in the
210               // FASTA file - but FASTA will automatically generate this at the
211               // mo.
212               if (!checkPIR)
213               {
214                 break;
215               }
216             }
217           }
218           // final check for PIR content. require
219           // >P1;title\n<blah>\nterminated sequence to occur at least once.
220
221           // TODO the PIR/fasta ambiguity may be the use case that is needed to
222           // have
223           // a 'Parse as type XXX' parameter for the applet/application.
224           if (checkPIR)
225           {
226             String dta = null;
227             if (!starterm)
228             {
229               do
230               {
231                 try
232                 {
233                   dta = source.nextLine();
234                 } catch (IOException ex)
235                 {
236                 }
237                 ;
238                 if (dta != null && dta.indexOf("*") > -1)
239                 {
240                   starterm = true;
241                 }
242               } while (dta != null && !starterm);
243             }
244             if (starterm)
245             {
246               reply = "PIR";
247               break;
248             }
249             else
250             {
251               reply = "FASTA"; // probably a bad choice!
252             }
253           }
254           // read as a FASTA (probably)
255           break;
256         }
257         else if (data.indexOf("HEADER") == 0 || data.indexOf("ATOM") == 0)
258         {
259           reply = "PDB";
260           break;
261         }
262         /*
263          * // TODO comment out SimpleBLAST identification for Jalview 2.4.1 else
264          * if (!lineswereskipped && data.indexOf("BLAST")<4) { reply =
265          * "SimpleBLAST"; break;
266          * 
267          * } // end comments for Jalview 2.4.1
268          */
269         else if (!lineswereskipped && data.charAt(0) != '*'
270                 && data.charAt(0) != ' '
271                 && data.indexOf(":") < data.indexOf(",")) // &&
272         // data.indexOf(",")<data.indexOf(",",
273         // data.indexOf(",")))
274         {
275           // file looks like a concise JNet file
276           reply = "JnetFile";
277           break;
278         }
279
280         lineswereskipped = true; // this means there was some junk before any
281         // key file signature
282       }
283       if (closeSource)
284       {
285         source.close();
286       }
287       else
288       {
289         source.reset(); // so the file can be parsed from the beginning again.
290       }
291     } catch (Exception ex)
292     {
293       System.err.println("File Identification failed!\n" + ex);
294       return source.errormessage;
295     }
296     if (length == 0)
297     {
298       System.err
299               .println("File Identification failed! - Empty file was read.");
300       return "EMPTY DATA FILE";
301     }
302     return reply;
303   }
304
305   public static void main(String[] args)
306   {
307          
308     for (int i = 0; args != null && i < args.length; i++)
309     {
310       IdentifyFile ider = new IdentifyFile();
311       String type = ider.Identify(args[i], AppletFormatAdapter.FILE);
312       System.out.println("Type of " + args[i] + " is " + type);
313     }
314     if (args == null || args.length == 0)
315     {
316       System.err.println("Usage: <Filename> [<Filename> ...]");
317     }
318   }
319 }