-
Notifications
You must be signed in to change notification settings - Fork 243
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Re-Ordering priority in SeekableStreamFactory #1693
Changes from 4 commits
6cdcbd4
b949659
9eca91a
5376f55
652eda2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -253,6 +253,10 @@ private URI getURIForString(final String pathString) { | |
tempURI = getCachedPath().toUri(); | ||
} | ||
} catch (URISyntaxException uriException) { | ||
//check that the uri wasn't a badly encoded absolute uri of some sort | ||
//if you don't do this it will be treated as a badly formed file:/ url | ||
assertNoNonFileScheme(pathString, uriException); | ||
|
||
// the input string isn't a valid URI; assume its a local (non-URI) file reference, and | ||
// use the URI resulting from the corresponding Path | ||
try { | ||
|
@@ -276,5 +280,25 @@ private URI getURIForString(final String pathString) { | |
|
||
return tempURI; | ||
} | ||
|
||
/** | ||
* check that there isn't a non file scheme at the start of the path | ||
* @param pathString | ||
* @param cause | ||
*/ | ||
private static void assertNoNonFileScheme(String pathString, URISyntaxException cause){ | ||
final String[] split = pathString.split(":"); | ||
if(split.length > 1){ | ||
if(split[0] == null || split[0].isEmpty()){ | ||
throw new IllegalArgumentException("Malformed url " + pathString + " includes an empty scheme." + | ||
"\nCheck that it is fully encoded.", cause); | ||
} | ||
if(!split[0].equals("file")){ | ||
throw new IllegalArgumentException("Malformed url " + pathString + " includes a scheme: " + split[0] + ":/ but was an invalid URI." + | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. :/ -> :// |
||
"\nCheck that it is fully encoded.", cause); | ||
} | ||
} | ||
|
||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,12 +23,15 @@ | |
*/ | ||
package htsjdk.samtools.seekablestream; | ||
|
||
import htsjdk.samtools.util.IOUtil; | ||
import java.io.File; | ||
import htsjdk.io.HtsPath; | ||
import htsjdk.io.IOPath; | ||
import htsjdk.tribble.TribbleException; | ||
|
||
import java.io.IOException; | ||
import java.net.URI; | ||
import java.net.URL; | ||
import java.nio.channels.SeekableByteChannel; | ||
import java.nio.file.Path; | ||
import java.util.Set; | ||
import java.util.function.Function; | ||
|
||
/** | ||
|
@@ -40,6 +43,14 @@ | |
public class SeekableStreamFactory{ | ||
|
||
private static final ISeekableStreamFactory DEFAULT_FACTORY; | ||
private static final String HTTP = "http"; | ||
private static final String HTTPS = "https"; | ||
private static final String FTP = "ftp"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See below -- recommend including "://" in these constants to guard against files named after a protocol There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. instead of adding them to the constants which are also used directly compared against getScheme the isHandledBy method now appends '://' to them. |
||
/** | ||
* the set of url schemes that have special support in htsjdk that isn't through a FileSystemProvider | ||
*/ | ||
private static final Set<String> URL_SCHEMES_WITH_LEGACY_SUPPORT = Set.of(HTTP, FTP, HTTPS); | ||
public static final String FILE_SCHEME = "file"; | ||
private static ISeekableStreamFactory currentFactory; | ||
|
||
static{ | ||
|
@@ -61,9 +72,28 @@ public static ISeekableStreamFactory getInstance(){ | |
* Does this path point to a regular file on disk and not something like a URL? | ||
* @param path the path to test | ||
* @return true if the path is to a file on disk | ||
* @deprecated this method is simplistic and no longer particularly useful since IOPath allows similar access to | ||
* various non-file data sources, internal use has been replaced with {@link #isBeingHandledByLegacyUrlSupport(String)} | ||
*/ | ||
@Deprecated | ||
public static boolean isFilePath(final String path) { | ||
return ! ( path.startsWith("http:") || path.startsWith("https:") || path.startsWith("ftp:") ); | ||
return !canBeHandledByLegacyUrlSupport(path); | ||
} | ||
|
||
/** | ||
* is this path being handled by one of the legacy SeekableStream types (http(s) / ftp) | ||
* | ||
* @param path a path to check | ||
* @return if the path is not being handled by a FileSystemProvider and it can be read by legacy streams | ||
*/ | ||
public static boolean isBeingHandledByLegacyUrlSupport(final String path){ | ||
return !new HtsPath(path).hasFileSystemProvider() //if we have a provider for it that's what we'll use | ||
&& canBeHandledByLegacyUrlSupport(path); // otherwise we fall back to the special handlers | ||
} | ||
|
||
//is this onee of the url types that has legacy htsjdk support built in? | ||
public static boolean canBeHandledByLegacyUrlSupport(final String path) { | ||
return URL_SCHEMES_WITH_LEGACY_SUPPORT.stream().anyMatch(path::startsWith); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't you be checking that the path starts with "http://" (for example) instead of "http"? What if there's a file named "http"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
} | ||
|
||
private static class DefaultSeekableStreamFactory implements ISeekableStreamFactory { | ||
|
@@ -79,7 +109,7 @@ public SeekableStream getStreamFor(final String path) throws IOException { | |
} | ||
|
||
/** | ||
* The wrapper will only be applied to the stream if the stream is treated as a {@link java.nio.file.Path} | ||
* The wrapper will only be applied to the stream if the stream is treated as a {@link Path} | ||
* | ||
* This currently means any uri with a scheme that is not http, https, ftp, or file will have the wrapper applied to it | ||
* | ||
|
@@ -89,26 +119,30 @@ public SeekableStream getStreamFor(final String path) throws IOException { | |
@Override | ||
public SeekableStream getStreamFor(final String path, | ||
Function<SeekableByteChannel, SeekableByteChannel> wrapper) throws IOException { | ||
// todo -- add support for SeekableBlockInputStream | ||
|
||
if (path.startsWith("http:") || path.startsWith("https:")) { | ||
final URL url = new URL(path); | ||
return new SeekableHTTPStream(url); | ||
} else if (path.startsWith("ftp:")) { | ||
return new SeekableFTPStream(new URL(path)); | ||
} else if (path.startsWith("file:")) { | ||
try { | ||
// convert to URI in order to obtain a decoded version of the path string suitable | ||
// for use with the File constructor | ||
final String decodedPath = new URI(path).getPath(); | ||
return new SeekableFileStream(new File(decodedPath)); | ||
} catch (java.net.URISyntaxException e) { | ||
throw new IllegalArgumentException(String.format("The input string %s contains a URI scheme but is not a valid URI", path), e); | ||
} | ||
} else if (IOUtil.hasScheme(path)) { | ||
return new SeekablePathStream(IOUtil.getPath(path), wrapper); | ||
return getStreamFor(new HtsPath(path), wrapper); | ||
} | ||
|
||
|
||
/** | ||
* The wrapper will only be applied to the stream if the stream is treated as a non file:// {@link Path} | ||
* | ||
* This has a fall back to htsjdk's built in http and ftp providers if no FileSystemProvder is available for them | ||
* | ||
* @param path an IOPath to be opened | ||
* @param wrapper a wrapper to apply to the stream allowing direct transformations on the byte stream to be applied | ||
* @throws IOException | ||
*/ | ||
public static SeekableStream getStreamFor(final IOPath path, Function<SeekableByteChannel, SeekableByteChannel> wrapper) throws IOException { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If there aren't already tests covering |
||
if(path.hasFileSystemProvider()) { | ||
return path.getScheme().equals(FILE_SCHEME) | ||
? new SeekableFileStream(path.toPath().toFile()) //don't apply the wrapper to local files | ||
: new SeekablePathStream(path.toPath(), wrapper); | ||
} else { | ||
return new SeekableFileStream(new File(path)); | ||
return switch(path.getScheme()){ | ||
case HTTP, HTTPS -> new SeekableHTTPStream(new URL(path.getRawInputString())); | ||
case FTP -> new SeekableFTPStream((new URL(path.getRawInputString()))); | ||
default -> throw new TribbleException("Unknown path type. No FileSystemProvider available for " + path.getRawInputString()); | ||
}; | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
file://