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