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