package htsjdk.samtools;
import htsjdk.samtools.seekablestream.SeekableFileStream;
import htsjdk.samtools.seekablestream.SeekableHTTPStream;
import htsjdk.samtools.seekablestream.SeekableStream;
import htsjdk.samtools.util.Lazy;
import htsjdk.samtools.util.RuntimeIOException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
/**
* Describes a SAM-like resource, including its data (where the records are), and optionally an index.
* <p/>
* A data or index source may originate from a {@link java.io.File}, {@link java.io.InputStream}, {@link URL}, or
* {@link htsjdk.samtools.seekablestream.SeekableStream}; look for the appropriate overload for
* {@code htsjdk.samtools.SamInputResource#of()}.
*
* @author mccowan
*/
public class SamInputResource {
private final InputResource source;
private InputResource index;
SamInputResource(final InputResource data) {
this(data, null);
}
SamInputResource(final InputResource source, final InputResource index) {
if (source == null) throw new NullPointerException("source");
this.source = source;
this.index = index;
}
/** The resource that is the SAM data (e.g., records) */
InputResource data() {
return source;
}
/**
* The resource that is the SAM index
*
* @return null, if no index is defined for this resource
*/
InputResource indexMaybe() {
return index;
}
@Override
public String toString() {
return String.format("data=%s;index=%s", source, index);
}
/** Creates a {@link SamInputResource} reading from the provided resource, with no index. */
public static SamInputResource of(final File file) { return new SamInputResource(new FileInputResource(file)); }
/** Creates a {@link SamInputResource} reading from the provided resource, with no index. */
public static SamInputResource of(final InputStream inputStream) { return new SamInputResource(new InputStreamInputResource(inputStream)); }
/** Creates a {@link SamInputResource} reading from the provided resource, with no index. */
public static SamInputResource of(final URL url) { return new SamInputResource(new UrlInputResource(url)); }
/** Creates a {@link SamInputResource} reading from the provided resource, with no index. */
public static SamInputResource of(final SeekableStream seekableStream) { return new SamInputResource(new SeekableStreamInputResource(seekableStream)); }
/** Creates a {@link SamInputResource} from a string specifying *either* a url or a file path */
public static SamInputResource of(final String string) {
try {
URL url = new URL(string); // this will throw if its not a url
return of(url);
} catch (MalformedURLException e) {
// ignore
}
return of(new File(string));
}
/** Updates the index to point at the provided resource, then returns itself. */
public SamInputResource index(final File file) {
this.index = new FileInputResource(file);
return this;
}
/** Updates the index to point at the provided resource, then returns itself. */
public SamInputResource index(final InputStream inputStream) {
this.index = new InputStreamInputResource(inputStream);
return this;
}
/** Updates the index to point at the provided resource, then returns itself. */
public SamInputResource index(final URL url) {
this.index = new UrlInputResource(url);
return this;
}
/** Updates the index to point at the provided resource, then returns itself. */
public SamInputResource index(final SeekableStream seekableStream) {
this.index = new SeekableStreamInputResource(seekableStream);
return this;
}
}
/**
* Describes an arbitrary input source, which is something that can be accessed as either a
* {@link htsjdk.samtools.seekablestream.SeekableStream} or {@link java.io.InputStream}. A concrete implementation of this class exists for
* each of {@link InputResource.Type}.
*/
abstract class InputResource {
protected InputResource(final Type type) {this.type = type;}
enum Type {
FILE, URL, SEEKABLE_STREAM, INPUT_STREAM
}
private final Type type;
final Type type() {
return type;
}
/** Returns null if this resource cannot be represented as a {@link File}. */
abstract File asFile();
/** Returns null if this resource cannot be represented as a {@link URL}. */
abstract URL asUrl();
/** Returns null if this resource cannot be represented as a {@link htsjdk.samtools.seekablestream.SeekableStream}. */
abstract SeekableStream asUnbufferedSeekableStream();
/** All resource types support {@link java.io.InputStream} generation. */
abstract InputStream asUnbufferedInputStream();
@Override
public String toString() {
final String childToString;
switch (type()) {
case FILE:
childToString = asFile().toString();
break;
case INPUT_STREAM:
childToString = asUnbufferedInputStream().toString();
break;
case SEEKABLE_STREAM:
childToString = asUnbufferedSeekableStream().toString();
break;
case URL:
childToString = asUrl().toString();
break;
default:
throw new IllegalStateException();
}
return String.format("%s:%s", type(), childToString);
}
}
class FileInputResource extends InputResource {
final File fileResource;
final Lazy<SeekableStream> lazySeekableStream = new Lazy<SeekableStream>(new Lazy.LazyInitializer<SeekableStream>() {
@Override
public SeekableStream make() {
try {
return new SeekableFileStream(fileResource);
} catch (final FileNotFoundException e) {
throw new RuntimeIOException(e);
}
}
});
FileInputResource(final File fileResource) {
super(Type.FILE);
this.fileResource = fileResource;
}
@Override
public File asFile() {
return fileResource;
}
@Override
public URL asUrl() {
return null;
}
@Override
public SeekableStream asUnbufferedSeekableStream() {
return lazySeekableStream.get();
}
@Override
public InputStream asUnbufferedInputStream() {
return asUnbufferedSeekableStream();
}
}
class UrlInputResource extends InputResource {
final URL urlResource;
final Lazy<SeekableStream> lazySeekableStream = new Lazy<SeekableStream>(new Lazy.LazyInitializer<SeekableStream>() {
@Override
public SeekableStream make() {
return new SeekableHTTPStream(urlResource);
}
});
UrlInputResource(final URL urlResource) {
super(Type.URL);
this.urlResource = urlResource;
}
@Override
public File asFile() {
return null;
}
@Override
public URL asUrl() {
return urlResource;
}
@Override
public SeekableStream asUnbufferedSeekableStream() {
return lazySeekableStream.get();
}
@Override
public InputStream asUnbufferedInputStream() {
return asUnbufferedSeekableStream();
}
}
class SeekableStreamInputResource extends InputResource {
final SeekableStream seekableStreamResource;
SeekableStreamInputResource(final SeekableStream seekableStreamResource) {
super(Type.SEEKABLE_STREAM);
this.seekableStreamResource = seekableStreamResource;
}
@Override
File asFile() {
return null;
}
@Override
URL asUrl() {
return null;
}
@Override
SeekableStream asUnbufferedSeekableStream() {
return seekableStreamResource;
}
@Override
InputStream asUnbufferedInputStream() {
return asUnbufferedSeekableStream();
}
}
class InputStreamInputResource extends InputResource {
final InputStream inputStreamResource;
InputStreamInputResource(final InputStream inputStreamResource) {
super(Type.INPUT_STREAM);
this.inputStreamResource = inputStreamResource;
}
@Override
File asFile() {
return null;
}
@Override
URL asUrl() {
return null;
}
@Override
SeekableStream asUnbufferedSeekableStream() {
return null;
}
@Override
InputStream asUnbufferedInputStream() {
return inputStreamResource;
}
}