From fbba5364e1809de071bc479f30e4e2c8b17f5bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez-S=C3=A1nchez?= Date: Fri, 16 Sep 2016 20:15:55 +0200 Subject: [PATCH] adding additional write method to Index, fixes #430 (#683) Adding Index.write(file) to the index interface. This is a breaking change which will affect anyone who implements Index or calls TabixFile.write. This fixes an issue where IndexFactory.writeIndex was incorrectly failing to compress tabix index files. (fix #430 ) TabixIndex.write(file) now throws IOException instead of TribbleException. This makes it consistent with the write(stream) methods. Call sites must now catch or throw IOException. deprecated IndexFactory.writeIndex(index, file) because it is now redundant, replace with index.write(file) --- .../java/htsjdk/tribble/index/AbstractIndex.java | 13 +++--- src/main/java/htsjdk/tribble/index/Index.java | 8 ++++ .../java/htsjdk/tribble/index/IndexFactory.java | 16 ++------ .../htsjdk/tribble/index/tabix/TabixIndex.java | 9 ++--- .../java/htsjdk/tribble/FeatureReaderTest.java | 2 +- .../htsjdk/tribble/index/IndexFactoryTest.java | 5 +++ src/test/java/htsjdk/tribble/index/IndexTest.java | 47 ++++++++++++++++++++++ .../htsjdk/tribble/index/tabix/TabixIndexTest.java | 3 +- 8 files changed, 77 insertions(+), 26 deletions(-) diff --git a/src/main/java/htsjdk/tribble/index/AbstractIndex.java b/src/main/java/htsjdk/tribble/index/AbstractIndex.java index 42bce732d..47e31ccef 100644 --- a/src/main/java/htsjdk/tribble/index/AbstractIndex.java +++ b/src/main/java/htsjdk/tribble/index/AbstractIndex.java @@ -343,13 +343,16 @@ public void write(final LittleEndianOutputStream stream) throws IOException { } @Override + public void write(final File idxFile) throws IOException { + try(final LittleEndianOutputStream idxStream = new LittleEndianOutputStream(new BufferedOutputStream(new FileOutputStream(idxFile)))) { + write(idxStream); + } + } + + @Override public void writeBasedOnFeatureFile(final File featureFile) throws IOException { if (!featureFile.isFile()) return; - final LittleEndianOutputStream idxStream = - new LittleEndianOutputStream(new BufferedOutputStream(new FileOutputStream(Tribble.indexFile(featureFile)))); - write(idxStream); - idxStream.close(); - + write(Tribble.indexFile(featureFile)); } public void read(final LittleEndianInputStream dis) throws IOException { diff --git a/src/main/java/htsjdk/tribble/index/Index.java b/src/main/java/htsjdk/tribble/index/Index.java index 252bc95e5..ca6cc60d3 100644 --- a/src/main/java/htsjdk/tribble/index/Index.java +++ b/src/main/java/htsjdk/tribble/index/Index.java @@ -70,6 +70,14 @@ public void write(LittleEndianOutputStream stream) throws IOException; /** + * Writes the index into a file. + * + * @param idxFile Where to write the index. + * @throws IOException if the index is unable to write to the specified file + */ + public void write(final File idxFile) throws IOException; + + /** * Write an appropriately named and located Index file based on the name and location of the featureFile. * If featureFile is not a normal file, the index will silently not be written. * @param featureFile diff --git a/src/main/java/htsjdk/tribble/index/IndexFactory.java b/src/main/java/htsjdk/tribble/index/IndexFactory.java index a588220dc..3cd1b7958 100644 --- a/src/main/java/htsjdk/tribble/index/IndexFactory.java +++ b/src/main/java/htsjdk/tribble/index/IndexFactory.java @@ -41,16 +41,13 @@ import htsjdk.tribble.index.tabix.TabixIndexCreator; import htsjdk.tribble.readers.PositionalBufferedStream; import htsjdk.tribble.util.LittleEndianInputStream; -import htsjdk.tribble.util.LittleEndianOutputStream; import htsjdk.tribble.util.ParsingUtils; import htsjdk.tribble.util.TabixUtils; import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; @@ -288,18 +285,11 @@ public static LinearIndex createLinearIndex(final File inputFile, final FeatureC * @param idx * @param idxFile * @throws IOException + * @deprecated use {@link Index#write(File)} instead */ + @Deprecated public static void writeIndex(final Index idx, final File idxFile) throws IOException { - LittleEndianOutputStream stream = null; - try { - stream = new LittleEndianOutputStream(new BufferedOutputStream(new FileOutputStream(idxFile))); - idx.write(stream); - } - finally { - if(stream != null) { - stream.close(); - } - } + idx.write(idxFile); } /** diff --git a/src/main/java/htsjdk/tribble/index/tabix/TabixIndex.java b/src/main/java/htsjdk/tribble/index/tabix/TabixIndex.java index 9ab05d42e..044cefe61 100644 --- a/src/main/java/htsjdk/tribble/index/tabix/TabixIndex.java +++ b/src/main/java/htsjdk/tribble/index/tabix/TabixIndex.java @@ -201,13 +201,10 @@ public TabixFormat getFormatSpec() { * * @param tabixFile Where to write the index. */ - public void write(final File tabixFile) { - final LittleEndianOutputStream los = new LittleEndianOutputStream(new BlockCompressedOutputStream(tabixFile)); - try { + @Override + public void write(final File tabixFile) throws IOException { + try(final LittleEndianOutputStream los = new LittleEndianOutputStream(new BlockCompressedOutputStream(tabixFile))) { write(los); - los.close(); - } catch (final IOException e) { - throw new TribbleException("Exception writing " + tabixFile.getAbsolutePath(), e); } } diff --git a/src/test/java/htsjdk/tribble/FeatureReaderTest.java b/src/test/java/htsjdk/tribble/FeatureReaderTest.java index ac3405939..d62693c19 100644 --- a/src/test/java/htsjdk/tribble/FeatureReaderTest.java +++ b/src/test/java/htsjdk/tribble/FeatureReaderTest.java @@ -134,7 +134,7 @@ public void testBedNames(final File featureFile, final IndexFactory.IndexType in idxFile.delete(); } final Index idx = IndexFactory.createIndex(featureFile, codec, indexType); - IndexFactory.writeIndex(idx, idxFile); + idx.write(idxFile); idxFile.deleteOnExit(); } // else let's just hope the index exists, and if so use it diff --git a/src/test/java/htsjdk/tribble/index/IndexFactoryTest.java b/src/test/java/htsjdk/tribble/index/IndexFactoryTest.java index ba64998d3..016049f32 100644 --- a/src/test/java/htsjdk/tribble/index/IndexFactoryTest.java +++ b/src/test/java/htsjdk/tribble/index/IndexFactoryTest.java @@ -25,11 +25,14 @@ import htsjdk.samtools.SAMSequenceDictionary; import htsjdk.samtools.SAMSequenceRecord; +import htsjdk.samtools.util.IOUtil; import htsjdk.tribble.TestUtils; import htsjdk.tribble.TribbleException; import htsjdk.tribble.bed.BEDCodec; +import htsjdk.tribble.index.linear.LinearIndex; import htsjdk.tribble.index.tabix.TabixFormat; import htsjdk.tribble.index.tabix.TabixIndex; +import htsjdk.tribble.util.LittleEndianOutputStream; import htsjdk.variant.vcf.VCFCodec; import htsjdk.variant.vcf.VCFFileReader; import org.testng.Assert; @@ -37,6 +40,8 @@ import org.testng.annotations.Test; import java.io.File; +import java.io.IOException; +import java.io.OutputStream; import java.util.List; /** diff --git a/src/test/java/htsjdk/tribble/index/IndexTest.java b/src/test/java/htsjdk/tribble/index/IndexTest.java index 8104a0803..aa179a9a2 100644 --- a/src/test/java/htsjdk/tribble/index/IndexTest.java +++ b/src/test/java/htsjdk/tribble/index/IndexTest.java @@ -1,13 +1,23 @@ package htsjdk.tribble.index; +import htsjdk.samtools.util.IOUtil; +import htsjdk.tribble.FeatureCodec; import htsjdk.tribble.TestUtils; +import htsjdk.tribble.Tribble; +import htsjdk.tribble.bed.BEDCodec; import htsjdk.tribble.index.linear.LinearIndex; +import htsjdk.tribble.index.tabix.TabixFormat; +import htsjdk.tribble.index.tabix.TabixIndex; +import htsjdk.tribble.util.LittleEndianOutputStream; +import htsjdk.tribble.util.TabixUtils; +import htsjdk.variant.vcf.VCFCodec; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.io.File; import java.io.IOException; +import java.io.OutputStream; import java.util.ArrayList; import java.util.List; @@ -47,4 +57,41 @@ public void testMassiveQuery(final int start, final int mid, final int mid2, fin Assert.assertTrue(allSize >= Math.max(leftSize,rightSize), "Expected size of joint query " + allSize + " to be at least >= max of left " + leftSize + " and right queries " + rightSize); } + + + @DataProvider(name = "writeIndexData") + public Object[][] writeIndexData() { + return new Object[][]{ + {new File("src/test/resources/htsjdk/tribble/tabix/testTabixIndex.vcf"), IndexFactory.IndexType.LINEAR, new VCFCodec()}, + {new File("src/test/resources/htsjdk/tribble/tabix/testTabixIndex.vcf.gz"), IndexFactory.IndexType.TABIX, new VCFCodec()}, + {new File("src/test/resources/htsjdk/tribble/test.bed"), IndexFactory.IndexType.LINEAR, new BEDCodec()} + }; + } + + private final static OutputStream nullOutputStrem = new OutputStream() { + @Override + public void write(int b) throws IOException { } + }; + + @Test(dataProvider = "writeIndexData") + public void testWriteIndex(final File inputFile, final IndexFactory.IndexType type, final FeatureCodec codec) throws Exception { + // temp index file for this test + final File tempIndex = File.createTempFile("index", (type == IndexFactory.IndexType.TABIX) ? TabixUtils.STANDARD_INDEX_EXTENSION : Tribble.STANDARD_INDEX_EXTENSION); + tempIndex.delete(); + tempIndex.deleteOnExit(); + // create the index + final Index index = IndexFactory.createIndex(inputFile, codec, type); + Assert.assertFalse(tempIndex.exists()); + // write the index to a file + index.write(tempIndex); + Assert.assertTrue(tempIndex.exists()); + // load the generated index + final Index loadedIndex = IndexFactory.loadIndex(tempIndex.getAbsolutePath()); + // tess that the sequences and properties are the same + Assert.assertEquals(loadedIndex.getSequenceNames(), index.getSequenceNames()); + Assert.assertEquals(loadedIndex.getProperties(), index.getProperties()); + // test that write to a stream does not blows ip + index.write(new LittleEndianOutputStream(nullOutputStrem)); + } + } diff --git a/src/test/java/htsjdk/tribble/index/tabix/TabixIndexTest.java b/src/test/java/htsjdk/tribble/index/tabix/TabixIndexTest.java index 557a3987d..6981b8751 100644 --- a/src/test/java/htsjdk/tribble/index/tabix/TabixIndexTest.java +++ b/src/test/java/htsjdk/tribble/index/tabix/TabixIndexTest.java @@ -37,6 +37,7 @@ import org.testng.annotations.Test; import java.io.File; +import java.io.IOException; import java.util.Iterator; public class TabixIndexTest { @@ -71,7 +72,7 @@ public void readWriteTest(final File tabixFile) throws Exception { } @Test - public void testQueryProvidedItemsAmount() { + public void testQueryProvidedItemsAmount() throws IOException { final String VCF = "src/test/resources/htsjdk/tribble/tabix/YRI.trio.2010_07.indel.sites.vcf"; // Note that we store only compressed files final File plainTextVcfInputFile = new File(VCF);