8a97e392d60edabbe12102d0bd31ae27831a747e
[jalview.git] / src / jalview / io / FileParse.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.*;
22 import java.net.*;
23 /** 
24  * implements a random access wrapper around a particular datasource, for passing to
25  * identifyFile and AlignFile objects.
26  */
27 public class FileParse
28 {
29   public File inFile=null;
30   protected char suffixSeparator = '#';
31   /**
32    * '#' separated string tagged on to end of filename 
33    * or url that was clipped off to resolve to valid filename
34    */
35   protected String suffix=null; 
36   protected String type=null;
37   protected BufferedReader dataIn=null;
38   protected String errormessage="UNITIALISED SOURCE";
39   protected boolean error=true;
40   protected String warningMessage=null;
41   /**
42    * size of readahead buffer used for when initial stream position is marked.
43    */
44   final int READAHEAD_LIMIT=2048;
45   public FileParse()
46   {
47   }
48   /**
49    * Attempt to open a file as a datasource.
50    * Sets error and errormessage if fileStr was invalid.
51    * @param fileStr
52    * @return this.error (true if the source was invalid)
53    */
54   private boolean checkFileSource(String fileStr) throws IOException {
55     error=false;
56     this.inFile = new File(fileStr);
57     // check to see if it's a Jar file in disguise.
58     if (!inFile.exists()) {
59       errormessage = "FILE NOT FOUND";
60       error=true;
61     }
62     if (!inFile.canRead()) {
63       errormessage = "FILE CANNOT BE OPENED FOR READING";
64       error=true;
65     }
66     if (inFile.isDirectory()) {
67       // this is really a 'complex' filetype - but we don't handle directory reads yet.
68       errormessage = "FILE IS A DIRECTORY";
69       error=true;
70     }
71     if (!error) {
72       dataIn = new BufferedReader(new FileReader(fileStr));
73     }
74     return error;
75   }
76   private boolean checkURLSource(String fileStr) throws IOException, MalformedURLException
77   {
78     errormessage = "URL NOT FOUND";
79     URL url = new URL(fileStr);
80     dataIn = new BufferedReader(new InputStreamReader(url.openStream()));
81     return false;
82   }
83   /**
84    * sets the suffix string (if any) and returns remainder (if suffix was detected) 
85    * @param fileStr
86    * @return truncated fileStr or null
87    */
88   private String extractSuffix(String fileStr) {
89     // first check that there wasn't a suffix string tagged on.
90     int sfpos = fileStr.lastIndexOf(suffixSeparator);
91     if (sfpos>-1 && sfpos<fileStr.length()-1) {
92       suffix = fileStr.substring(sfpos+1);
93       // System.err.println("DEBUG: Found Suffix:"+suffix);
94       return fileStr.substring(0,sfpos);
95     }
96     return null;
97   }
98   /**
99    * Create a datasource for input to Jalview.
100    * See AppletFormatAdapter for the types of sources that are handled.
101    * @param fileStr - datasource locator/content
102    * @param type - protocol of source 
103    * @throws MalformedURLException
104    * @throws IOException
105    */
106   public FileParse(String fileStr, String type)
107       throws MalformedURLException, IOException
108   {
109     this.type = type;
110     error=false;
111
112     if (type.equals(AppletFormatAdapter.FILE))
113     {
114       if (checkFileSource(fileStr)) {  
115         String suffixLess = extractSuffix(fileStr);
116         if (suffixLess!=null)
117         {
118           if (checkFileSource(suffixLess))
119           {
120             throw new IOException("Problem opening "+inFile+" (also tried "+suffixLess+") : "+errormessage);
121           }
122         } else
123         {
124           throw new IOException("Problem opening "+inFile+" : "+errormessage);
125         }
126       }
127     }
128     else if (type.equals(AppletFormatAdapter.URL))
129     {
130       try {
131       try {
132         checkURLSource(fileStr);
133         if (suffixSeparator=='#')
134           extractSuffix(fileStr); // URL lref is stored for later reference.
135       } catch (IOException e) {
136         String suffixLess = extractSuffix(fileStr);
137         if (suffixLess==null)
138         {
139           throw(e);
140         } else {
141           try {
142             checkURLSource(suffixLess);
143           }
144           catch (IOException e2) {
145             errormessage = "BAD URL WITH OR WITHOUT SUFFIX";
146             throw(e); // just pass back original - everything was wrong.
147           }
148         }
149       }
150       }
151       catch (Exception e)
152       {
153         errormessage = "CANNOT ACCESS DATA AT URL '"+fileStr+"' ("+e.getMessage()+")";
154         error=true;
155       }
156     }
157     else if (type.equals(AppletFormatAdapter.PASTE))
158     {
159       errormessage = "PASTE INACCESSIBLE!";
160       dataIn = new BufferedReader(new StringReader(fileStr));
161     }
162     else if (type.equals(AppletFormatAdapter.CLASSLOADER))
163     {
164       errormessage = "RESOURCE CANNOT BE LOCATED";
165       java.io.InputStream is = getClass().getResourceAsStream("/" + fileStr);
166       if (is==null) {
167         String suffixLess = extractSuffix(fileStr);
168         if (suffixLess!=null)
169           is = getClass().getResourceAsStream("/" + suffixLess);
170       }
171       if (is != null)
172       {
173         dataIn = new BufferedReader(new java.io.InputStreamReader(is));
174       } else {
175         error = true;
176       }
177     }
178     if (dataIn==null)
179     {
180       // pass up the reason why we have no source to read from
181       throw new IOException("Failed to read data from source:\n"+errormessage);
182     }
183     error=false;
184     dataIn.mark(READAHEAD_LIMIT);
185   }
186   public String nextLine()
187       throws IOException
188   {
189     if (!error)
190       return dataIn.readLine();
191     throw new IOException("Invalid Source Stream:"+errormessage);
192   }
193
194   public boolean isValid()
195   {
196     return !error;
197   }
198   /**
199    * closes the datasource and tidies up.
200    * source will be left in an error state
201    */
202   public void close() throws IOException
203   {
204     errormessage="EXCEPTION ON CLOSE";
205     error=true;
206     dataIn.close();
207     dataIn=null;
208     errormessage="SOURCE IS CLOSED";
209   }
210   /**
211    * rewinds the datasource the beginning.
212    *
213    */
214   public void reset() throws IOException
215   {
216     if (dataIn!=null && !error) {
217       dataIn.reset();
218     } else {
219       throw new IOException("Implementation Error: Reset called for invalid source.");
220     }
221   }
222   /**
223    * 
224    * @return true if there is a warning for the user
225    */
226   public boolean hasWarningMessage() {
227     return (warningMessage!=null && warningMessage.length()>0);
228   }
229   /**
230    * 
231    * @return empty string or warning message about file that was just parsed.
232    */
233   public String getWarningMessage() {
234     return warningMessage;
235   }
236 }