Clover icon

Coverage Report

  1. Project Clover database Fri Jun 19 2026 11:35:32 BST
  2. Package jalview.io

File FileParse.java

 

Coverage histogram

../../img/srcFileCovDistChart6.png
37% of files have more coverage

Code metrics

86
210
31
1
784
533
94
0.45
6.77
31
3.03

Classes

Class Line # Actions
FileParse 56 210 94
0.541284454.1%
 

Contributing tests

This file is covered by 340 tests. .

Source view

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 java.io.BufferedInputStream;
24    import java.io.BufferedReader;
25    import java.io.ByteArrayInputStream;
26    import java.io.File;
27    import java.io.FileInputStream;
28    import java.io.FileNotFoundException;
29    import java.io.FileReader;
30    import java.io.IOException;
31    import java.io.InputStream;
32    import java.io.InputStreamReader;
33    import java.io.Reader;
34    import java.io.StringReader;
35    import java.net.HttpURLConnection;
36    import java.net.MalformedURLException;
37    import java.net.SocketTimeoutException;
38    import java.net.URL;
39    import java.net.URLConnection;
40    import java.net.UnknownHostException;
41    import java.util.zip.GZIPInputStream;
42   
43    import jalview.api.AlignExportSettingsI;
44    import jalview.api.AlignViewportI;
45    import jalview.api.AlignmentViewPanel;
46    import jalview.api.FeatureSettingsModelI;
47    import jalview.bin.Console;
48    import jalview.util.HttpUtils;
49    import jalview.util.MessageManager;
50    import jalview.util.Platform;
51   
52    /**
53    * implements a random access wrapper around a particular datasource, for
54    * passing to identifyFile and AlignFile objects.
55    */
 
56    public class FileParse
57    {
58    protected static final String SPACE = " ";
59   
60    protected static final String TAB = "\t";
61   
62    /**
63    * text specifying source of data. usually filename or url.
64    */
65    private String dataName = "unknown source";
66   
67    public File inFile = null;
68   
69    private byte[] bytes; // from JavaScript
70   
 
71  0 toggle public byte[] getBytes()
72    {
73  0 return bytes;
74    }
75   
76    /**
77    * a viewport associated with the current file operation. May be null. May
78    * move to different object.
79    */
80    private AlignViewportI viewport;
81   
82    /**
83    * specific settings for exporting data from the current context
84    */
85    private AlignExportSettingsI exportSettings;
86   
87    /**
88    * sequence counter for FileParse object created from same data source
89    */
90    public int index = 1;
91   
92    /**
93    * separator for extracting specific 'frame' of a datasource for formats that
94    * support multiple records (e.g. BLC, Stockholm, etc)
95    */
96    protected char suffixSeparator = '#';
97   
98    /**
99    * character used to write newlines
100    */
101    protected String newline = System.getProperty("line.separator");
102   
 
103  177 toggle public void setNewlineString(String nl)
104    {
105  177 newline = nl;
106    }
107   
 
108  0 toggle public String getNewlineString()
109    {
110  0 return newline;
111    }
112   
113    /**
114    * '#' separated string tagged on to end of filename or url that was clipped
115    * off to resolve to valid filename
116    */
117    protected String suffix = null;
118   
119    protected DataSourceType dataSourceType = null;
120   
121    protected BufferedReader dataIn = null;
122   
123    protected String errormessage = "UNINITIALISED SOURCE";
124   
125    protected boolean error = true;
126   
127    protected String warningMessage = null;
128   
129    /**
130    * size of readahead buffer used for when initial stream position is marked.
131    */
132    final int READAHEAD_LIMIT = 2048;
133   
 
134  274 toggle public FileParse()
135    {
136    }
137   
138    /**
139    * Create a new FileParse instance reading from the same datasource starting
140    * at the current position. WARNING! Subsequent reads from either object will
141    * affect the read position of the other, but not the error state.
142    *
143    * @param from
144    */
 
145  918 toggle public FileParse(FileParse from) throws IOException
146    {
147  918 if (from == null)
148    {
149  0 throw new Error(MessageManager
150    .getString("error.implementation_error_null_fileparse"));
151    }
152  918 if (from == this)
153    {
154  0 return;
155    }
156  918 index = ++from.index;
157  918 inFile = from.inFile;
158  918 suffixSeparator = from.suffixSeparator;
159  918 suffix = from.suffix;
160  918 errormessage = from.errormessage; // inherit potential error messages
161  918 error = false; // reset any error condition.
162  918 dataSourceType = from.dataSourceType;
163  918 dataIn = from.dataIn;
164  918 if (dataIn != null)
165    {
166  918 mark();
167    }
168  918 dataName = from.dataName;
169    }
170   
171    /**
172    * Attempt to open a file as a datasource. Sets error and errormessage if
173    * fileStr was invalid.
174    *
175    * @param fileStr
176    * @return this.error (true if the source was invalid)
177    */
 
178  987 toggle private boolean checkFileSource(String fileStr) throws IOException
179    {
180  987 error = false;
181  987 this.inFile = new File(fileStr);
182    // check to see if it's a Jar file in disguise.
183  987 if (!inFile.exists())
184    {
185  0 errormessage = "FILE NOT FOUND";
186  0 error = true;
187    }
188  987 if (!inFile.canRead())
189    {
190  0 errormessage = "FILE CANNOT BE OPENED FOR READING";
191  0 error = true;
192    }
193  987 if (inFile.isDirectory())
194    {
195    // this is really a 'complex' filetype - but we don't handle directory
196    // reads yet.
197  0 errormessage = "FILE IS A DIRECTORY";
198  0 error = true;
199    }
200  987 if (!error)
201    {
202  987 try
203    {
204  987 dataIn = Platform.checkForGzipStream(new FileInputStream(fileStr));
205  987 dataName = fileStr;
206    } catch (Exception x)
207    {
208  0 warningMessage = "Failed to resolve " + fileStr
209    + " as a data source. (" + x.getMessage() + ")";
210    // x.printStackTrace();
211  0 error = true;
212    }
213  987 ;
214    }
215  987 return error;
216    }
217   
218    /**
219    * Tries to read from the given URL. If successful, saves a reader to the
220    * response in field {@code dataIn}, otherwise (on exception, or HTTP response
221    * status not 200), throws an exception.
222    * <p>
223    * If the response status includes
224    *
225    * <pre>
226    * Content-Type : application/x-gzip
227    * </pre>
228    *
229    * then tries to read as gzipped content.
230    *
231    * @param urlStr
232    * @throws IOException
233    * @throws MalformedURLException
234    */
 
235  14 toggle private void checkURLSource(String urlStr)
236    throws IOException, MalformedURLException
237    {
238  14 errormessage = "URL NOT FOUND";
239  14 URL url = new URL(urlStr);
240  14 URLConnection _conn = HttpUtils.openConnection(url);
241  14 if (_conn instanceof HttpURLConnection)
242    {
243  12 HttpURLConnection conn = HttpUtils
244    .followConnection((HttpURLConnection) _conn);
245  12 int rc = conn.getResponseCode();
246  12 if (rc != HttpURLConnection.HTTP_OK)
247    {
248  0 throw new FileNotFoundException("Response status from " + urlStr
249    + " was " + conn.getResponseCode());
250    }
251  12 _conn = conn;
252    }
253    else
254    {
255  2 try
256    {
257  2 dataIn = Platform.checkForGzipStream(_conn.getInputStream());
258  2 dataName = urlStr;
259    } catch (IOException ex)
260    {
261  0 throw new IOException("Failed to handle non-HTTP URI stream", ex);
262    } catch (Exception ex)
263    {
264  0 throw new IOException(
265    "Failed to determine type of input stream for given URI",
266    ex);
267    }
268  2 return;
269    }
270  12 String encoding = _conn.getContentEncoding();
271  12 String contentType = _conn.getContentType();
272  12 boolean isgzipped = false;
273  12 isgzipped |=(contentType!=null && ("application/x-gzip".equalsIgnoreCase(contentType) || contentType.endsWith("gzip")));
274   
275  12 isgzipped |= (encoding!=null && "gzip".equals(encoding));
276   
277  12 Exception e = null;
278   
279  12 InputStream inputStream = _conn.getInputStream();
280  12 if (isgzipped)
281    {
282  0 try
283    {
284  0 dataIn = Platform.getGzipReader(inputStream);
285  0 dataName = urlStr;
286    } catch (Exception e1)
287    {
288  0 throw new IOException(MessageManager
289    .getString("exception.failed_to_resolve_gzip_stream"), e);
290    }
291  0 return;
292    }
293   
294  12 dataIn = new BufferedReader(new InputStreamReader(inputStream));
295  12 dataName = urlStr;
296  12 return;
297    }
298   
299    /**
300    * sets the suffix string (if any) and returns remainder (if suffix was
301    * detected)
302    *
303    * @param fileStr
304    * @return truncated fileStr or null
305    */
 
306  14 toggle private String extractSuffix(String fileStr)
307    {
308    // first check that there wasn't a suffix string tagged on.
309  14 int sfpos = fileStr.lastIndexOf(suffixSeparator);
310  14 if (sfpos > -1 && sfpos < fileStr.length() - 1)
311    {
312  0 suffix = fileStr.substring(sfpos + 1);
313    // jalview.bin.Console.errPrintln("DEBUG: Found Suffix:"+suffix);
314  0 return fileStr.substring(0, sfpos);
315    }
316  14 return null;
317    }
318   
319    /**
320    * not for general use, creates a fileParse object for an existing reader with
321    * configurable values for the origin and the type of the source
322    */
 
323  0 toggle public FileParse(BufferedReader source, String originString,
324    DataSourceType sourceType)
325    {
326  0 dataSourceType = sourceType;
327  0 error = false;
328  0 inFile = null;
329  0 dataName = originString;
330  0 dataIn = source;
331  0 try
332    {
333  0 if (dataIn.markSupported())
334    {
335  0 dataIn.mark(READAHEAD_LIMIT);
336    }
337    } catch (IOException q)
338    {
339   
340    }
341    }
342   
343    /**
344    * Create a datasource for input to Jalview. See AppletFormatAdapter for the
345    * types of sources that are handled.
346    *
347    * @param file
348    * - datasource locator/content as File or String
349    * @param sourceType
350    * - protocol of source
351    * @throws MalformedURLException
352    * @throws IOException
353    */
 
354  1698 toggle public FileParse(Object file, DataSourceType sourceType)
355    throws MalformedURLException, FileNotFoundException, IOException
356    {
357  1698 if (file instanceof File)
358    {
359  16 parse((File) file, ((File) file).getPath(), sourceType, true);
360    }
361    else
362    {
363  1682 parse(null, file.toString(), sourceType, false);
364    }
365    }
366   
 
367  1698 toggle private void parse(File file, String fileStr, DataSourceType sourceType,
368    boolean isFileObject) throws FileNotFoundException, IOException
369    {
370  1698 bytes = Platform.getFileBytes(file);
371  1698 dataSourceType = sourceType;
372  1698 error = false;
373  1698 boolean filenotfound = false;
374   
375  1698 if (sourceType == DataSourceType.FILE)
376    {
377   
378  987 if (bytes != null)
379    {
380    // this will be from JavaScript
381  0 inFile = file;
382  0 dataIn = new BufferedReader(
383    new InputStreamReader(new ByteArrayInputStream(bytes)));
384  0 dataName = fileStr;
385    }
386  987 else if (checkFileSource(fileStr))
387    {
388  0 String suffixLess = extractSuffix(fileStr);
389  0 if (suffixLess != null)
390    {
391  0 if (checkFileSource(suffixLess))
392    {
393  0 throw new IOException(MessageManager.formatMessage(
394    "exception.problem_opening_file_also_tried",
395    new String[]
396    { inFile.getName(), suffixLess, errormessage }));
397    }
398    }
399    else
400    {
401  0 throw new IOException(MessageManager.formatMessage(
402    "exception.problem_opening_file", new String[]
403    { inFile.getName(), errormessage }));
404    }
405    }
406    }
407  711 else if (sourceType == DataSourceType.RELATIVE_URL)
408    {
409    // BH 2018 hack for no support for access-origin
410  0 bytes = Platform.getFileAsBytes(fileStr);
411  0 dataIn = new BufferedReader(
412    new InputStreamReader(new ByteArrayInputStream(bytes)));
413  0 dataName = fileStr;
414   
415    }
416  711 else if (sourceType == DataSourceType.URL)
417    {
418  14 try
419    {
420  14 try
421    {
422  14 checkURLSource(fileStr);
423  14 if (suffixSeparator == '#')
424    {
425  14 extractSuffix(fileStr); // URL lref is stored for later reference.
426    }
427    } catch (IOException e)
428    {
429  0 String suffixLess = extractSuffix(fileStr);
430  0 if (suffixLess == null)
431    {
432  0 if (e instanceof FileNotFoundException
433    || e instanceof UnknownHostException
434    || e instanceof SocketTimeoutException)
435    {
436  0 errormessage = "File at URL '" + fileStr + "' not found";
437  0 filenotfound = true;
438    }
439  0 throw (e);
440    }
441    else
442    {
443  0 try
444    {
445  0 checkURLSource(suffixLess);
446    } catch (IOException e2)
447    {
448  0 errormessage = "BAD URL WITH OR WITHOUT SUFFIX '" + fileStr
449    + "'";
450  0 if (e instanceof FileNotFoundException
451    || e instanceof UnknownHostException
452    || e instanceof SocketTimeoutException)
453    {
454  0 filenotfound = true;
455    }
456  0 throw (e); // just pass back original - everything was wrong.
457    }
458    }
459    }
460    } catch (Exception e)
461    {
462  0 errormessage = "CANNOT ACCESS DATA AT URL '" + fileStr + "' ("
463    + e.getMessage() + ")";
464  0 error = true;
465    }
466    }
467  697 else if (sourceType == DataSourceType.PASTE)
468    {
469  325 errormessage = "PASTE INACCESSIBLE!";
470  325 dataIn = new BufferedReader(new StringReader(fileStr));
471  325 dataName = "Paste";
472    }
473  372 else if (sourceType == DataSourceType.CLASSLOADER)
474    {
475  372 errormessage = "RESOURCE CANNOT BE LOCATED";
476  372 InputStream is = getClass().getResourceAsStream("/" + fileStr);
477  372 if (is == null)
478    {
479  0 String suffixLess = extractSuffix(fileStr);
480  0 if (suffixLess != null)
481    {
482  0 is = getClass().getResourceAsStream("/" + suffixLess);
483    }
484    }
485  372 if (is != null)
486    {
487  372 dataIn = new BufferedReader(new InputStreamReader(is));
488  372 dataName = fileStr;
489    }
490    else
491    {
492  0 error = true;
493    }
494    }
495    else
496    {
497  0 errormessage = "PROBABLE IMPLEMENTATION ERROR : Datasource Type given as '"
498  0 + (sourceType != null ? sourceType : "null") + "'";
499  0 error = true;
500    }
501  1698 if (dataIn == null || error)
502    {
503    // pass up the reason why we have no source to read from
504  0 if (filenotfound)
505    {
506  0 throw new FileNotFoundException(MessageManager
507    .formatMessage("label.url_not_found", new String[]
508    { errormessage }));
509    }
510  0 throw new IOException(MessageManager.formatMessage(
511    "exception.failed_to_read_data_from_source", new String[]
512    { errormessage }));
513    }
514  1698 error = false;
515  1698 dataIn.mark(READAHEAD_LIMIT);
516    }
517   
518    /**
519    * mark the current position in the source as start for the purposes of it
520    * being analysed by IdentifyFile().identify
521    *
522    * @throws IOException
523    */
 
524  922 toggle public void mark() throws IOException
525    {
526  922 if (dataIn != null)
527    {
528  922 dataIn.mark(READAHEAD_LIMIT);
529    }
530    else
531    {
532  0 throw new IOException(
533    MessageManager.getString("exception.no_init_source_stream"));
534    }
535    }
536   
 
537  87606 toggle public String nextLine() throws IOException
538    {
539  87607 if (!error)
540    {
541  87607 return dataIn.readLine();
542    }
543  0 throw new IOException(MessageManager
544    .formatMessage("exception.invalid_source_stream", new String[]
545    { errormessage }));
546    }
547   
548    /**
549    *
550    * @return true if this FileParse is configured for Export only
551    */
 
552  0 toggle public boolean isExporting()
553    {
554  0 return !error && dataIn == null;
555    }
556   
557    /**
558    *
559    * @return true if the data source is valid
560    */
 
561  481 toggle public boolean isValid()
562    {
563  481 return !error;
564    }
565   
566    /**
567    * closes the datasource and tidies up. source will be left in an error state
568    */
 
569  466 toggle public void close() throws IOException
570    {
571  466 errormessage = "EXCEPTION ON CLOSE";
572  466 error = true;
573  466 dataIn.close();
574  466 dataIn = null;
575  466 errormessage = "SOURCE IS CLOSED";
576    }
577   
578    /**
579    * Rewinds the datasource to the marked point if possible
580    *
581    * @param bytesRead
582    *
583    */
 
584  0 toggle public void reset(int bytesRead) throws IOException
585    {
586  0 if (bytesRead >= READAHEAD_LIMIT)
587    {
588  0 jalview.bin.Console.errPrintln(String.format(
589    "File reset error: read %d bytes but reset limit is %d",
590    bytesRead, READAHEAD_LIMIT));
591    }
592  0 if (dataIn != null && !error)
593    {
594  0 dataIn.reset();
595    }
596    else
597    {
598  0 throw new IOException(MessageManager.getString(
599    "error.implementation_error_reset_called_for_invalid_source"));
600    }
601    }
602   
603    /**
604    *
605    * @return true if there is a warning for the user
606    */
 
607  180 toggle public boolean hasWarningMessage()
608    {
609  180 return (warningMessage != null && warningMessage.length() > 0);
610    }
611   
612    /**
613    *
614    * @return empty string or warning message about file that was just parsed.
615    */
 
616  9 toggle public String getWarningMessage()
617    {
618  9 return warningMessage;
619    }
620   
 
621  268 toggle public String getInFile()
622    {
623  268 if (inFile != null)
624    {
625  246 return inFile.getAbsolutePath() + " (" + index + ")";
626    }
627    else
628    {
629  22 return "From Paste + (" + index + ")";
630    }
631    }
632   
633    /**
634    * @return the dataName
635    */
 
636  915 toggle public String getDataName()
637    {
638  915 return dataName;
639    }
640   
641    /**
642    * set the (human readable) name or URI for this datasource
643    *
644    * @param dataname
645    */
 
646  0 toggle protected void setDataName(String dataname)
647    {
648  0 dataName = dataname;
649    }
650   
651    /**
652    * get the underlying bufferedReader for this data source.
653    *
654    * @return null if no reader available
655    * @throws IOException
656    */
 
657  269 toggle public Reader getReader()
658    {
659  269 if (dataIn != null) // Probably don't need to test for readiness &&
660    // dataIn.ready())
661    {
662  269 return dataIn;
663    }
664  0 return null;
665    }
666   
 
667  29 toggle public AlignViewportI getViewport()
668    {
669  29 return viewport;
670    }
671   
 
672  128 toggle public void setViewport(AlignViewportI viewport)
673    {
674  128 this.viewport = viewport;
675    }
676   
677    /**
678    * @return the currently configured exportSettings for writing data.
679    */
 
680  8 toggle public AlignExportSettingsI getExportSettings()
681    {
682  8 return exportSettings;
683    }
684   
685    /**
686    * Set configuration for export of data.
687    *
688    * @param exportSettings
689    * the exportSettings to set
690    */
 
691  177 toggle public void setExportSettings(AlignExportSettingsI exportSettings)
692    {
693  177 this.exportSettings = exportSettings;
694    }
695   
696    /**
697    * method overridden by complex file exporter/importers which support
698    * exporting visualisation and layout settings for a view
699    *
700    * @param avpanel
701    */
 
702  176 toggle public void configureForView(AlignmentViewPanel avpanel)
703    {
704  176 if (avpanel != null)
705    {
706  128 setViewport(avpanel.getAlignViewport());
707    }
708    // could also set export/import settings
709    }
710   
711    /**
712    * Returns the preferred feature colour configuration if there is one, else
713    * null
714    *
715    * @return
716    */
 
717  312 toggle public FeatureSettingsModelI getFeatureColourScheme()
718    {
719  312 return null;
720    }
721   
 
722  0 toggle public DataSourceType getDataSourceType()
723    {
724  0 return dataSourceType;
725    }
726   
727    /**
728    * Returns a buffered reader for the input object. Returns null, or throws
729    * IOException, on failure.
730    *
731    * @param file
732    * a File, or a String which is a name of a file
733    * @param sourceType
734    * @return
735    * @throws IOException
736    */
 
737  36 toggle public BufferedReader getBufferedReader(Object file,
738    DataSourceType sourceType) throws IOException
739    {
740  36 BufferedReader in = null;
741  36 byte[] bytes;
742   
743  36 switch (sourceType)
744    {
745  17 case FILE:
746  17 if (file instanceof String)
747    {
748  17 return new BufferedReader(new FileReader((String) file));
749    }
750  0 bytes = Platform.getFileBytes((File) file);
751  0 if (bytes != null)
752    {
753  0 return new BufferedReader(
754    new InputStreamReader(new ByteArrayInputStream(bytes)));
755    }
756  0 return new BufferedReader(new FileReader((File) file));
757  0 case URL:
758  0 URL url = new URL(file.toString());
759  0 in = new BufferedReader(
760    new InputStreamReader(HttpUtils.openStream(url)));
761  0 break;
762  0 case RELATIVE_URL: // JalviewJS only
763  0 bytes = Platform.getFileAsBytes(file.toString());
764  0 if (bytes != null)
765    {
766  0 in = new BufferedReader(
767    new InputStreamReader(new ByteArrayInputStream(bytes)));
768    }
769  0 break;
770  19 case PASTE:
771  19 in = new BufferedReader(new StringReader(file.toString()));
772  19 break;
773  0 case CLASSLOADER:
774  0 InputStream is = getClass().getResourceAsStream("/" + file);
775  0 if (is != null)
776    {
777  0 in = new BufferedReader(new InputStreamReader(is));
778    }
779  0 break;
780    }
781   
782  19 return in;
783    }
784    }