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

Added Io.readBytesFromResources() to read binary data. #33

Merged
merged 2 commits into from
Aug 6, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
39 changes: 29 additions & 10 deletions src/main/scala/com/fulcrumgenomics/commons/io/Io.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import java.io._
import java.nio.file.{Files, Path}

import com.fulcrumgenomics.commons.CommonsDef._

import scala.collection.mutable.ArrayBuffer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unused import

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doh, thanks!

import scala.io.Source

/**
Expand Down Expand Up @@ -181,6 +183,13 @@ trait IoUtil {
/** Creates an object that will asynchronously read character data from a stream and pipe it into a sink function. */
def pipeStream(stream: InputStream, sink: String => Unit) : AsyncStreamSink = new AsyncStreamSink(stream, sink)

/** Private function to get an input stream from a resource, or fail if the resource doesn't exist. */
private def streamFromResource(name: String): InputStream = {
Seq(getClass.getResourceAsStream _, getClass.getClassLoader.getResourceAsStream _)
.flatMap(m => Option(m(name)))
.headOption.getOrElse(throw new IllegalArgumentException(s"Resource does not exist at path: $name"))
}

/** Finds a resource with a given name on the classpath and produces an iterator of lines of text from the resource.
*
* The way resources are traditionally loaded from the class path via `Class.getResource()` and
Expand All @@ -191,17 +200,27 @@ trait IoUtil {
*
* The implementation here first tries `Class.getResource` and then `ClassLoader.getResource` to enable it to
* find relative paths and also absolute paths with and without leading `/s`.
*
*/
def readLinesFromResource(name: String): Iterator[String] = {
Seq(getClass.getResourceAsStream _, getClass.getClassLoader.getResourceAsStream _)
.flatMap(m => Option(m(name)))
.headOption match {
case None =>
throw new IllegalArgumentException(s"Resource does not exist at path: $name")
case Some(in) =>
val stream = new BufferedInputStream(in, bufferSize)
Source.fromInputStream(stream).withClose(() => stream.safelyClose()).getLines
}
val stream = new BufferedInputStream(streamFromResource(name), bufferSize)
Source.fromInputStream(stream).withClose(() => stream.safelyClose()).getLines
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this too!

}

/** Finds a resource with a given name on the classpath and produces an array of the bytes from the resource.
*
* The way resources are traditionally loaded from the class path via `Class.getResource()` and
* `ClassLoader.getResource()` have confusing behaviour. Notably:
*
* * Class.getResource() interprets paths as relative, and requires a leading `/` to make them absolute
* * ClassLoader.getResource() interprets all paths a absolute and fails on leading `/`s
*
* The implementation here first tries `Class.getResource` and then `ClassLoader.getResource` to enable it to
* find relative paths and also absolute paths with and without leading `/s`.
*/
def readBytesFromResource(name: String): Array[Byte] = {
val stream = new BufferedInputStream(streamFromResource(name), bufferSize)
val array = Iterator.continually(stream.read()).takeWhile(_ != -1).map(_.toByte).toArray
stream.safelyClose()
array
}
}
Binary file not shown.
7 changes: 7 additions & 0 deletions src/test/scala/com/fulcrumgenomics/commons/io/IoTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*/
package com.fulcrumgenomics.commons.io

import java.io.{BufferedOutputStream, FileOutputStream}
import java.nio.file.{Files, Path, Paths}

import com.fulcrumgenomics.commons.util.UnitSpec
Expand Down Expand Up @@ -186,4 +187,10 @@ class IoTest extends UnitSpec {
it should "fail when the resource does not exist" in {
an[IllegalArgumentException] should be thrownBy Io.readLinesFromResource("/path/does/not/exist.json")
}

"Io.readBytesFromResource" should "correctly read binary data from a resource" in {
val expected = Range.inclusive(Byte.MinValue, Byte.MaxValue).map(_.toByte).toArray
val actual = Io.readBytesFromResource("/com/fulcrumgenomics/commons/io/to-bytes-from-resource-test.bin")
actual shouldBe expected
}
}