Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  2. Package jalview.io

File FileParse.java

 

Coverage histogram

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

Code metrics

84
218
34
1
808
528
93
0.43
6.41
34
2.74

Classes

Class Line # Actions
FileParse 52 218 93
0.5238095552.4%
 

Contributing tests

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