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