Skip to content
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

new class: VCFReaderFactory a factory for VCFReader #1551

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/main/java/htsjdk/samtools/Defaults.java
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ public class Defaults {
*/
public static final boolean DISABLE_SNAPPY_COMPRESSOR;

/** Custom VCF reader factory .
* Expected format: <fully qualified factory class name>
* Default = "".
*/
public static final String CUSTOM_VCF_READER_FACTORY;


public static final String SAMJDK_PREFIX = "samjdk.";
static {
Expand All @@ -134,6 +140,7 @@ public class Defaults {
SAM_FLAG_FIELD_FORMAT = SamFlagField.valueOf(getStringProperty("sam_flag_field_format", SamFlagField.DECIMAL.name()));
SRA_LIBRARIES_DOWNLOAD = getBooleanProperty("sra_libraries_download", false);
DISABLE_SNAPPY_COMPRESSOR = getBooleanProperty(DISABLE_SNAPPY_PROPERTY_NAME, false);
CUSTOM_VCF_READER_FACTORY = getStringProperty("vcf_reader_factory", "");
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/main/java/htsjdk/variant/vcf/VCFReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

/**
* Interface for reading VCF/BCF files.
* @see VCFReaderFactory
*/
public interface VCFReader extends Closeable, Iterable<VariantContext> {

Expand Down
108 changes: 108 additions & 0 deletions src/main/java/htsjdk/variant/vcf/VCFReaderFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package htsjdk.variant.vcf;

import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;

import htsjdk.samtools.Defaults;
import htsjdk.samtools.util.IOUtil;
import htsjdk.samtools.util.Log;
import htsjdk.samtools.util.RuntimeIOException;
import htsjdk.samtools.util.StringUtil;

/**
*
* @author Pierre Lindenbaum
* A Factory opening {@link VCFReader}
*
*/
public abstract class VCFReaderFactory {
private final static Log LOG = Log.getInstance(VCFReaderFactory.class);

private static final VCFReaderFactory DEFAULT_FACTORY;
private static VCFReaderFactory currentFactory;

static {
DEFAULT_FACTORY = createDefaultVcfReaderFactory();
currentFactory = DEFAULT_FACTORY;
}

/** set the current instance of VCFReaderFactory */
public static void setInstance(final VCFReaderFactory factory) {
currentFactory = factory;
}

/** get the current instance of VCFReaderFactory */
public static VCFReaderFactory getInstance() {
return currentFactory;
}

/** get the default instance of VCFReaderFactory which
* opens VCF files using {@link VCFFileReader}
*/
public static VCFReaderFactory getDefaultInstance() {
return DEFAULT_FACTORY;
}

/**
* Open VCFReader that will or will not assert the presence of an index as
* desired
*/
public abstract VCFReader open(final Path vcfPath, boolean requireIndex);

/**
* Open VCFReader that will or will not assert the presence of an index as
* desired
*/
public VCFReader open(final File vcfFile, boolean requireIndex) {
return open(IOUtil.toPath(vcfFile), requireIndex);
}

/**
* Open VCFReader that will or will not assert the presence of an index as
* desired
*/
public VCFReader open(final String vcfUri, boolean requireIndex) {
if (IOUtil.isUrl(vcfUri)) {
throw new RuntimeIOException("VCFReaderFactory(" + this.getClass()
+ ") cannot open URI: " + vcfUri);
}
return open(Paths.get(vcfUri), requireIndex);
}

@Override
public String toString() {
return "VCFReaderFactory(" + this.getClass() + ")";
}

/**
* creates the default instance of VCFReaderFactory A new instance from a
* specific class it's name is defined by the java property
* {@link Defaults}.CUSTOM_VCF_READER_FACTORY . Otherwise, we return the
* default instance of VCFReaderFactory
*/
private static VCFReaderFactory createDefaultVcfReaderFactory() {
final String factoryClassName = Defaults.CUSTOM_VCF_READER_FACTORY;
if (!StringUtil.isBlank(factoryClassName)) {
try {
LOG.info("Attempting to load factory class " + factoryClassName);
final Class<?> clazz = Class.forName(factoryClassName);
return VCFReaderFactory.class.cast(clazz.newInstance());
} catch (Exception e) {
throw new IllegalArgumentException("Cannot load " + factoryClassName, e);
}
}
return new DefaultVCFReaderFactory();
}

/**
* default instance of a VCFReaderFactory. It returns a
* {@link VCFFileReader}
*/
private static class DefaultVCFReaderFactory extends VCFReaderFactory {
@Override
public VCFReader open(final Path vcfPath, boolean requireIndex) {
return new VCFFileReader(vcfPath, requireIndex);
}
}
}
77 changes: 77 additions & 0 deletions src/test/java/htsjdk/variant/vcf/VCFReaderFactoryTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package htsjdk.variant.vcf;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import htsjdk.HtsjdkTest;
import htsjdk.samtools.util.RuntimeIOException;


/**
* Created by Pierre Lindenbaum
*/
public class VCFReaderFactoryTest extends HtsjdkTest {
private static final File TEST_DATA_DIR = new File("src/test/resources/htsjdk/variant/");

@DataProvider(name = "queryableData")
public Iterator<Object[]> queryableData() throws IOException {
List<Object[]> tests = new ArrayList<>();
tests.add(new Object[] { new File(TEST_DATA_DIR, "NA12891.fp.vcf"), false });
tests.add(new Object[] { new File(TEST_DATA_DIR, "NA12891.vcf"), false });
tests.add(new Object[] { VCFUtils.createTemporaryIndexedVcfFromInput(
new File(TEST_DATA_DIR, "NA12891.vcf"),
"fingerprintcheckertest.tmp."), true });
tests.add(new Object[] { VCFUtils.createTemporaryIndexedVcfFromInput(
new File(TEST_DATA_DIR, "NA12891.vcf.gz"),
"fingerprintcheckertest.tmp."), true });
return tests.iterator();
}

@Test(dataProvider = "queryableData")
public void testFileIsQueriable(final File vcf, final boolean expectedQueryable)
throws Exception {
try (VCFReader reader = VCFReaderFactory.getInstance().open(vcf, false)) {
Assert.assertEquals(reader.isQueryable(), expectedQueryable);
}
}

@Test(dataProvider = "queryableData")
public void testPathIsQueriable(final File vcf, final boolean expectedQueryable)
throws Exception {
try (VCFReader reader = VCFReaderFactory.getInstance().open(vcf.toPath(), false)) {
Assert.assertEquals(reader.isQueryable(), expectedQueryable);
}
}

@Test(dataProvider = "queryableData")
public void testUriIsQueriable(final File vcf, final boolean expectedQueryable)
throws Exception {
try (VCFReader reader = VCFReaderFactory.getInstance().open(vcf.toString(), false)) {
Assert.assertEquals(reader.isQueryable(), expectedQueryable);
}
}

@Test
public void testInstances() {
Assert.assertNotNull(VCFReaderFactory.getInstance());
Assert.assertNotNull(VCFReaderFactory.getDefaultInstance());
final VCFReaderFactory custom = new VCFReaderFactory() {
public VCFReader open(Path vcfPath, boolean requireIndex) {
throw new RuntimeIOException("cannot open anything");
}
};
VCFReaderFactory.setInstance(custom);
Assert.assertTrue(custom == VCFReaderFactory.getInstance());
VCFReaderFactory.setInstance(VCFReaderFactory.getDefaultInstance());
Assert.assertFalse(custom == VCFReaderFactory.getInstance());
}

}