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