diff --git a/src/main/java/htsjdk/samtools/liftover/LiftOver.java b/src/main/java/htsjdk/samtools/liftover/LiftOver.java index a7a721bd1b..13ea9dafbf 100644 --- a/src/main/java/htsjdk/samtools/liftover/LiftOver.java +++ b/src/main/java/htsjdk/samtools/liftover/LiftOver.java @@ -346,7 +346,7 @@ public String toString() { // Matched a chain, but entirely within a gap. return fromInterval.toString() + " (len " + fromInterval.length() + ")=>null using chain " + chainId; } - final String strand = toInterval.isNegativeStrand()? "-": "+"; + final String strand = toInterval.getStrand().encode(); return fromInterval.toString() + " (len " + fromInterval.length() + ")=>" + toInterval + "(" + strand + ") using chain " + chainId + " ; pct matched " + percentLiftedOver; } diff --git a/src/main/java/htsjdk/samtools/util/Interval.java b/src/main/java/htsjdk/samtools/util/Interval.java index 8eabcbc432..c0493745a9 100644 --- a/src/main/java/htsjdk/samtools/util/Interval.java +++ b/src/main/java/htsjdk/samtools/util/Interval.java @@ -24,6 +24,7 @@ package htsjdk.samtools.util; import htsjdk.samtools.SAMException; +import htsjdk.tribble.annotation.Strand; import java.util.Collection; @@ -101,6 +102,13 @@ public boolean isPositiveStrand() { return !this.negativeStrand; } + /** + * Return the {@link Strand} this interval is on. + */ + public Strand getStrand(){ + return isNegativeStrand() ? Strand.NEGATIVE : Strand.FORWARD; + } + /** * Returns the name of the interval, possibly null. */ @@ -210,7 +218,7 @@ public int hashCode() { } public String toString() { - return getContig() + ":" + getStart() + "-" + getEnd() + "\t" + (negativeStrand ? '-' : '+') + "\t" + ((null == name) ? '.' : name); + return getContig() + ":" + getStart() + "-" + getEnd() + "\t" + getStrand().encode() + "\t" + ((null == name) ? '.' : name); } @Override diff --git a/src/main/java/htsjdk/samtools/util/IntervalCodec.java b/src/main/java/htsjdk/samtools/util/IntervalCodec.java index d56c25d8b6..9120904ba6 100644 --- a/src/main/java/htsjdk/samtools/util/IntervalCodec.java +++ b/src/main/java/htsjdk/samtools/util/IntervalCodec.java @@ -43,7 +43,7 @@ public IntervalCodec(final SAMSequenceDictionary dict) { @Override public IntervalCodec clone() { - return new IntervalCodec(this.dict); + return new IntervalCodec(dict); } @@ -52,15 +52,15 @@ public IntervalCodec clone() { */ @Override public void setOutputStream(final OutputStream os) { - this.binaryCodec.setOutputStream(os); + binaryCodec.setOutputStream(os); } /** * Sets the output stream that records will be written to. */ public void setOutputStream(final OutputStream os, final String filename) { - this.binaryCodec.setOutputStream(os); - this.binaryCodec.setOutputFileName(filename); + binaryCodec.setOutputStream(os); + binaryCodec.setOutputFileName(filename); } /** @@ -68,15 +68,15 @@ public void setOutputStream(final OutputStream os, final String filename) { */ @Override public void setInputStream(final InputStream is) { - this.binaryCodec.setInputStream(is); + binaryCodec.setInputStream(is); } /** * Sets the input stream that records will be read from. */ public void setInputStream(final InputStream is, final String filename) { - this.binaryCodec.setInputStream(is); - this.binaryCodec.setInputFileName(filename); + binaryCodec.setInputStream(is); + binaryCodec.setInputFileName(filename); } /** @@ -86,13 +86,13 @@ public void setInputStream(final InputStream is, final String filename) { @Override public void encode(final Interval interval) { final String name = interval.getName(); - this.binaryCodec.writeInt(this.dict.getSequenceIndex(interval.getContig())); - this.binaryCodec.writeInt(interval.getStart()); - this.binaryCodec.writeInt(interval.getEnd()); - this.binaryCodec.writeBoolean(interval.isNegativeStrand()); - this.binaryCodec.writeBoolean(name != null); + binaryCodec.writeInt(dict.getSequenceIndex(interval.getContig())); + binaryCodec.writeInt(interval.getStart()); + binaryCodec.writeInt(interval.getEnd()); + binaryCodec.writeBoolean(interval.isNegativeStrand()); + binaryCodec.writeBoolean(name != null); if (name != null) { - this.binaryCodec.writeString(name, false, true); + binaryCodec.writeString(name, false, true); } } @@ -104,16 +104,16 @@ public void encode(final Interval interval) { public Interval decode() { final int sequenceIndex; try { - sequenceIndex = this.binaryCodec.readInt(); + sequenceIndex = binaryCodec.readInt(); } catch (final RuntimeEOFException e) { return null; } return new Interval( - this.dict.getSequence(sequenceIndex).getSequenceName(), - this.binaryCodec.readInt(), - this.binaryCodec.readInt(), - this.binaryCodec.readBoolean(), - (this.binaryCodec.readBoolean()) ? this.binaryCodec.readNullTerminatedString() : null + dict.getSequence(sequenceIndex).getSequenceName(), + binaryCodec.readInt(), + binaryCodec.readInt(), + binaryCodec.readBoolean(), + (binaryCodec.readBoolean()) ? binaryCodec.readNullTerminatedString() : null ); } } \ No newline at end of file diff --git a/src/main/java/htsjdk/samtools/util/IntervalListWriter.java b/src/main/java/htsjdk/samtools/util/IntervalListWriter.java index 418a980078..6baa257d1e 100644 --- a/src/main/java/htsjdk/samtools/util/IntervalListWriter.java +++ b/src/main/java/htsjdk/samtools/util/IntervalListWriter.java @@ -25,6 +25,7 @@ import htsjdk.samtools.SAMFileHeader; import htsjdk.samtools.SAMTextHeaderCodec; +import htsjdk.tribble.annotation.Strand; import java.io.BufferedWriter; import java.io.Closeable; @@ -37,9 +38,9 @@ * to write, such that they all cannot be held in memory, for example in an {@link IntervalList}. */ public class IntervalListWriter implements Closeable { + private static final char TAB = '\t'; private final BufferedWriter out; - private final FormatUtil format = new FormatUtil(); /** Creates a new writer, writing a header to the file. * @param path a path to write to. If exists it will be overwritten. @@ -52,8 +53,8 @@ public IntervalListWriter(final Path path) { * @param path a file to write to. If exists it will be overwritten. * @param header the header to write. */ - public IntervalListWriter(final Path path, final SAMFileHeader header) { - out = IOUtil.openFileForBufferedWriting(path.toFile()); + public IntervalListWriter(final Path path, final SAMFileHeader header) { + out = IOUtil.openFileForBufferedWriting(path); // Write out the header if (header != null) { @@ -67,23 +68,19 @@ public IntervalListWriter(final Path path, final SAMFileHeader header) { */ public void write(final Interval interval) throws IOException { out.write(interval.getContig()); - out.write('\t'); + out.write(TAB); out.write(Integer.toString(interval.getStart())); - out.write('\t'); + out.write(TAB); out.write(Integer.toString(interval.getEnd())); - out.write('\t'); - out.write(interval.isPositiveStrand() ? '+' : '-'); - out.write('\t'); - if(interval.getName() != null){ - out.write(interval.getName()); - } - else{ - out.write("."); - } + out.write(TAB); + out.write(interval.getStrand().encode()); + out.write(TAB); + out.write(interval.getName() != null ? interval.getName() : "."); out.newLine(); } /** Closes the writer. */ + @Override public void close() throws IOException { out.flush(); out.close(); diff --git a/src/test/java/htsjdk/samtools/util/IntervalCodecTest.java b/src/test/java/htsjdk/samtools/util/IntervalCodecTest.java index b84e185840..3fc0430016 100644 --- a/src/test/java/htsjdk/samtools/util/IntervalCodecTest.java +++ b/src/test/java/htsjdk/samtools/util/IntervalCodecTest.java @@ -59,27 +59,25 @@ public void testEndToEnd() throws IOException { expectedList.add(new Interval("chr1", 50, 150, true, "number-5")); expectedList.add(new Interval("chr1", 150, 250, false, "number-6")); - final OutputStream outputStream = new FileOutputStream(tempFile); - final IntervalCodec writeCodec = new IntervalCodec(this.dict); - writeCodec.setOutputStream(outputStream); - for (final Interval interval : expectedList.getIntervals()) { - writeCodec.encode(interval); + try (final OutputStream outputStream = new FileOutputStream(tempFile)) { + final IntervalCodec writeCodec = new IntervalCodec(this.dict); + writeCodec.setOutputStream(outputStream); + for (final Interval interval : expectedList.getIntervals()) { + writeCodec.encode(interval); + } } - outputStream.close(); final IntervalCodec readCodec = new IntervalCodec(this.dict); - final InputStream inputStream = new FileInputStream(tempFile); - readCodec.setInputStream(inputStream); - while (true) { - final Interval interval = readCodec.decode(); - if (interval == null) { - break; - } - else { + try (final InputStream inputStream = new FileInputStream(tempFile)) { + readCodec.setInputStream(inputStream); + while (true) { + final Interval interval = readCodec.decode(); + if (interval == null) { + break; + } actualList.add(interval); } } - inputStream.close(); Assert.assertEquals( actualList.getIntervals(), diff --git a/src/test/java/htsjdk/samtools/util/IntervalListWriterTest.java b/src/test/java/htsjdk/samtools/util/IntervalListWriterTest.java index d5f904b724..753cc7fade 100644 --- a/src/test/java/htsjdk/samtools/util/IntervalListWriterTest.java +++ b/src/test/java/htsjdk/samtools/util/IntervalListWriterTest.java @@ -1,5 +1,8 @@ package htsjdk.samtools.util; +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import com.google.common.jimfs.SystemJimfsFileSystemProvider; import htsjdk.HtsjdkTest; import htsjdk.samtools.SAMFileHeader; import htsjdk.samtools.SAMSequenceDictionary; @@ -10,6 +13,9 @@ import java.io.File; import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; public class IntervalListWriterTest extends HtsjdkTest { private SAMSequenceDictionary dict; @@ -26,7 +32,19 @@ void setup() { public void testEndToEnd() throws IOException { final File tempFile = File.createTempFile("IntervalListWriterTest.", ".interval_list"); tempFile.deleteOnExit(); + testEndToEnd(tempFile.toPath()); + } + + @Test + public void testEndToEndOnPath() throws IOException { + //create an in memory file system that mimics a unix one + try (FileSystem jimfs = Jimfs.newFileSystem(Configuration.unix())) { + final Path tempFile = Files.createTempFile(jimfs.getRootDirectories().iterator().next(), "IntervalListWriterTest.", ".interval_list"); + testEndToEnd(tempFile); + } + } + private void testEndToEnd(Path tempFile) throws IOException { final IntervalList expectedList = new IntervalList(this.dict); expectedList.add(new Interval("chr1", 50, 150)); @@ -36,19 +54,16 @@ public void testEndToEnd() throws IOException { expectedList.add(new Interval("chr1", 50, 150, true, "number-5")); expectedList.add(new Interval("chr1", 150, 250, false, "number-6")); - final IntervalListWriter writer = new IntervalListWriter(tempFile.toPath(), new SAMFileHeader(this.dict)); - for (final Interval interval : expectedList.getIntervals()) { - writer.write(interval); + try (final IntervalListWriter writer = new IntervalListWriter(tempFile, new SAMFileHeader(this.dict))) { + for (final Interval interval : expectedList.getIntervals()) { + writer.write(interval); + } } - writer.close(); - final IntervalList actualList = IntervalList.fromFile(tempFile); + final IntervalList actualList = IntervalList.fromPath(tempFile); final SAMSequenceDictionary actualDict = actualList.getHeader().getSequenceDictionary(); final SAMSequenceDictionary expectedDict = expectedList.getHeader().getSequenceDictionary(); Assert.assertTrue(actualDict.isSameDictionary(expectedDict)); - Assert.assertEquals( - actualList.getIntervals(), - expectedList.getIntervals() - ); + Assert.assertEquals(actualList.getIntervals(), expectedList.getIntervals()); } } diff --git a/src/test/java/htsjdk/samtools/util/IntervalTest.java b/src/test/java/htsjdk/samtools/util/IntervalTest.java new file mode 100644 index 0000000000..9a786baad5 --- /dev/null +++ b/src/test/java/htsjdk/samtools/util/IntervalTest.java @@ -0,0 +1,22 @@ +package htsjdk.samtools.util; + +import htsjdk.HtsjdkTest; +import htsjdk.tribble.annotation.Strand; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class IntervalTest extends HtsjdkTest { + @DataProvider + public Object[][] booleanProvider(){ + return new Object[][]{{true}, {false}}; + } + + @Test(dataProvider = "booleanProvider") + public void testGetStrand(boolean isNegativeStrand){ + final Interval interval = new Interval("chr1", 1, 100, isNegativeStrand, "name"); + Assert.assertEquals(isNegativeStrand, interval.isNegativeStrand()); + Assert.assertNotEquals(isNegativeStrand, interval.isPositiveStrand()); + Assert.assertEquals(isNegativeStrand, interval.getStrand() == Strand.NEGATIVE); + } +}