From d1260eebd13ac542199328e147c340cedbcefef3 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Wed, 9 Jun 2010 00:56:07 -0400 Subject: [PATCH] first update of io for 2.8 --- project/build/XSbt.scala | 5 +- util/io/Hash.scala | 4 +- util/io/{FileUtilities.scala => IO.scala} | 66 ++++++++----------- util/io/NameFilter.scala | 2 +- util/io/PathMapper.scala | 10 +-- util/io/Paths.scala | 4 +- util/io/{OpenResource.scala => Using.scala} | 39 +++++------ .../test/scala/NameFilterSpecification.scala | 22 +++---- util/io/src/test/scala/WithFiles.scala | 4 +- 9 files changed, 75 insertions(+), 81 deletions(-) rename util/io/{FileUtilities.scala => IO.scala} (86%) rename util/io/{OpenResource.scala => Using.scala} (78%) diff --git a/project/build/XSbt.scala b/project/build/XSbt.scala index f714f5d53..dc4cd1700 100644 --- a/project/build/XSbt.scala +++ b/project/build/XSbt.scala @@ -82,8 +82,9 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths } trait TestDependencies extends Project { - val sc = "org.scala-tools.testing" %% "scalacheck" % "1.6" % "test" - val sp = "org.scala-tools.testing" % "specs" % "1.6.0" % "test" + val sc = "org.scala-tools.testing" %% "scalacheck" % "1.7" % "test" + val sp = "org.scala-tools.testing" %% "specs" % "1.6.5-SNAPSHOT" % "test" + val snaps = ScalaToolsSnapshots } class StandardTaskProject(info: ProjectInfo) extends Base(info) { diff --git a/util/io/Hash.scala b/util/io/Hash.scala index 49d95f740..d8e3832f4 100644 --- a/util/io/Hash.scala +++ b/util/io/Hash.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2009 Mark Harrah */ -package xsbt +package sbt import java.io.{ByteArrayInputStream, File, InputStream} @@ -35,7 +35,7 @@ object Hash /** Calculates the SHA-1 hash of the given String.*/ def apply(s: String): Array[Byte] = apply(new ByteArrayInputStream(s.getBytes("UTF-8"))) /** Calculates the SHA-1 hash of the given file.*/ - def apply(file: File): Array[Byte] = OpenResource.fileInputStream(file)(apply) + def apply(file: File): Array[Byte] = Using.fileInputStream(file)(apply) /** Calculates the SHA-1 hash of the given stream, closing it when finished.*/ def apply(stream: InputStream): Array[Byte] = { diff --git a/util/io/FileUtilities.scala b/util/io/IO.scala similarity index 86% rename from util/io/FileUtilities.scala rename to util/io/IO.scala index db879d5ad..9b3502fc6 100644 --- a/util/io/FileUtilities.scala +++ b/util/io/IO.scala @@ -1,10 +1,10 @@ /* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah + * Copyright 2008, 2009, 2010 Mark Harrah */ -package xsbt +package sbt -import OpenResource._ -import ErrorHandling.translate +import Using._ +import xsbt.ErrorHandling.translate import java.io.{ByteArrayOutputStream, BufferedWriter, File, FileInputStream, InputStream, OutputStream} import java.net.{URI, URISyntaxException, URL} @@ -15,7 +15,7 @@ import scala.collection.mutable.HashSet import scala.reflect.{Manifest => SManifest} import Function.tupled -object FileUtilities +object IO { /** The maximum number of times a unique temporary filename is attempted to be created.*/ private val MaximumTries = 10 @@ -26,7 +26,7 @@ object FileUtilities private val BufferSize = 8192 private val Newline = System.getProperty("line.separator") - def utf8 = Charset.forName("UTF-8") + val utf8 = Charset.forName("UTF-8") def classLocation(cl: Class[_]): URL = { @@ -91,8 +91,7 @@ object FileUtilities else error(failBase) } - def unzip(from: File, toDirectory: File): Set[File] = unzip(from, toDirectory, AllPassFilter) - def unzip(from: File, toDirectory: File, filter: NameFilter): Set[File] = fileInputStream(from)(in => unzip(in, toDirectory, filter)) + def unzip(from: File, toDirectory: File, filter: NameFilter = AllPassFilter): Set[File] = fileInputStream(from)(in => unzip(in, toDirectory, filter)) def unzip(from: InputStream, toDirectory: File, filter: NameFilter): Set[File] = { createDirectory(toDirectory) @@ -187,7 +186,7 @@ object FileUtilities create(0) } - private[xsbt] def jars(dir: File): Iterable[File] = listFiles(dir, GlobFilter("*.jar")) + private[sbt] def jars(dir: File): Iterable[File] = listFiles(dir, GlobFilter("*.jar")) def delete(files: Iterable[File]): Unit = files.foreach(delete) def delete(file: File) @@ -246,21 +245,24 @@ object FileUtilities { def add(sourceFile: File, name: String) { - if(sourceFile.isDirectory) - () - else if(sourceFile.exists) - { - val nextEntry = createEntry(normalizeName(name)) - nextEntry.setTime(sourceFile.lastModified) - output.putNextEntry(nextEntry) - transferAndClose(new FileInputStream(sourceFile), output) - } - else + if(!sourceFile.exists) error("Source " + sourceFile + " does not exist.") + val isDirectory = sourceFile.isDirectory + val relName = if(isDirectory) normalizeDirName(name) else normalizeName(name) + val nextEntry = createEntry(relName) + nextEntry.setTime(sourceFile.lastModified) + output.putNextEntry(nextEntry) + if(!isDirectory) + transfer(new FileInputStream(sourceFile), output) } - sources.foreach(tupled(add)) + sources.foreach((add _).tupled) output.closeEntry() } + private def normalizeDirName(name: String) = + { + val norm1 = normalizeName(name) + if(norm1.endsWith("/")) norm1 else (norm1 + "/") + } private def normalizeName(name: String) = { val sep = File.separatorChar @@ -308,10 +310,8 @@ object FileUtilities { val cp = baseFile.getAbsolutePath assert(cp.length > 0) - if(cp.charAt(cp.length - 1) == File.separatorChar) - Some(cp) - else - Some(cp + File.separatorChar) + val normalized = if(cp.charAt(cp.length - 1) == File.separatorChar) cp else cp + File.separatorChar + Some(normalized) } else None @@ -344,9 +344,7 @@ object FileUtilities } } def defaultCharset = utf8 - def write(toFile: File, content: String): Unit = write(toFile, content, defaultCharset) - def write(toFile: File, content: String, charset: Charset): Unit = write(toFile, content, charset, false) - def write(file: File, content: String, charset: Charset, append: Boolean): Unit = + def write(file: File, content: String, charset: Charset = defaultCharset, append: Boolean = false): Unit = writeCharset(file, content, charset, append) { _.write(content) } def writeCharset[T](file: File, content: String, charset: Charset, append: Boolean)(f: BufferedWriter => T): T = @@ -357,17 +355,14 @@ object FileUtilities error("String cannot be encoded by charset " + charset.name) } - def read(file: File): String = read(file, defaultCharset) - def read(file: File, charset: Charset): String = + def read(file: File, charset: Charset = defaultCharset): String = { val out = new ByteArrayOutputStream(file.length.toInt) fileInputStream(file){ in => transfer(in, out) } out.toString(charset.name) } /** doesn't close the InputStream */ - def read(in: InputStream): String = read(in, defaultCharset) - /** doesn't close the InputStream */ - def read(in: InputStream, charset: Charset): String = + def readStream(in: InputStream, charset: Charset = defaultCharset): String = { val out = new ByteArrayOutputStream transfer(in, out) @@ -383,8 +378,7 @@ object FileUtilities } // Not optimized for large files - def readLines(file: File): List[String] = readLines(file, defaultCharset) - def readLines(file: File, charset: Charset): List[String] = + def readLines(file: File, charset: Charset = defaultCharset): List[String] = { fileReader(charset)(file){ in => def readLine(accum: List[String]): List[String] = @@ -395,9 +389,7 @@ object FileUtilities readLine(Nil) } } - def writeLines(file: File, lines: Seq[String]): Unit = writeLines(file, lines, defaultCharset) - def writeLines(file: File, lines: Seq[String], charset: Charset): Unit = writeLines(file, lines, charset, false) - def writeLines(file: File, lines: Seq[String], charset: Charset, append: Boolean): Unit = + def writeLines(file: File, lines: Seq[String], charset: Charset = defaultCharset, append: Boolean = false): Unit = writeCharset(file, lines.headOption.getOrElse(""), charset, append) { w => lines.foreach { line => w.write(line); w.newLine() } } diff --git a/util/io/NameFilter.scala b/util/io/NameFilter.scala index 1499337d5..7856030dc 100644 --- a/util/io/NameFilter.scala +++ b/util/io/NameFilter.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2008, 2009 Mark Harrah */ -package xsbt +package sbt import java.io.File import java.util.regex.Pattern diff --git a/util/io/PathMapper.scala b/util/io/PathMapper.scala index e836374cc..4d3ab2ee4 100644 --- a/util/io/PathMapper.scala +++ b/util/io/PathMapper.scala @@ -1,16 +1,16 @@ /* sbt -- Simple Build Tool * Copyright 2008, 2009 Mark Harrah */ -package xsbt +package sbt import java.io.File trait PathMapper extends NotNull { def apply(file: File): String - def apply(files: Set[File]): Iterable[(File,String)] = files.projection.map(f => (f,apply(f))) + def apply(files: Set[File]): Iterable[(File,String)] = files.view.map(f => (f,apply(f))) } -final case class RelativePathMapper(base: File) extends PMapper(file => FileUtilities.relativize(base, file).getOrElse(file.getPath)) +final case class RelativePathMapper(base: File) extends PMapper(file => IO.relativize(base, file).getOrElse(file.getPath)) final case object BasicPathMapper extends PMapper(_.getPath) final case object FlatPathMapper extends PMapper(_.getName) class PMapper(val f: File => String) extends PathMapper @@ -22,7 +22,7 @@ object PathMapper val basic: PathMapper = BasicPathMapper def relativeTo(base: File): PathMapper = RelativePathMapper(base) def rebase(oldBase: File, newBase: File): PathMapper = - new PMapper(file => if(file == oldBase) "." else FileUtilities.relativize(oldBase, file).getOrElse(error(file + " not a descendent of " + oldBase))) + new PMapper(file => if(file == oldBase) "." else IO.relativize(oldBase, file).getOrElse(error(file + " not a descendent of " + oldBase))) val flat = FlatPathMapper def apply(f: File => String): PathMapper = new PMapper(f) } @@ -30,7 +30,7 @@ object PathMapper trait FileMapper extends NotNull { def apply(file: File): File - def apply(files: Set[File]): Iterable[(File,File)] = files.projection.map(f => (f,apply(f))) + def apply(files: Set[File]): Iterable[(File,File)] = files.view.map(f => (f,apply(f))) } class FMapper(f: File => File) extends FileMapper { diff --git a/util/io/Paths.scala b/util/io/Paths.scala index f70a63cb6..79ea148b4 100644 --- a/util/io/Paths.scala +++ b/util/io/Paths.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2008, 2009, 2010 Mark Harrah */ -package xsbt +package sbt import java.io.File import java.net.URL @@ -28,7 +28,7 @@ trait PathBase extends NotNull def urls: Array[URL] = files.toArray[File].map(_.toURI.toURL) def x(mapper: PathMapper): Iterable[(File,String)] = mapper(files) def x(mapper: FileMapper): Iterable[(File,File)] = mapper(files) - def *(filter: java.io.FileFilter): Set[File] = files.flatMap(FileUtilities.listFiles(filter)) + def *(filter: java.io.FileFilter): Set[File] = files.flatMap(IO.listFiles(filter)) def **(filter: java.io.FileFilter): Set[File] = files.filter(filter.accept) ++ files.flatMap(_ * AllPassFilter ** filter) def *** = **(AllPassFilter) def abs = files.map(_.getAbsoluteFile) diff --git a/util/io/OpenResource.scala b/util/io/Using.scala similarity index 78% rename from util/io/OpenResource.scala rename to util/io/Using.scala index d7a052a28..be93e25f0 100644 --- a/util/io/OpenResource.scala +++ b/util/io/Using.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2008, 2009 Mark Harrah */ -package xsbt +package sbt import java.io.{Closeable, File, FileInputStream, FileOutputStream, InputStream, OutputStream} import java.io.{ByteArrayOutputStream, InputStreamReader, OutputStreamWriter} @@ -13,10 +13,10 @@ import java.nio.channels.FileChannel import java.util.jar.{Attributes, JarEntry, JarFile, JarInputStream, JarOutputStream, Manifest} import java.util.zip.{GZIPOutputStream, ZipEntry, ZipFile, ZipInputStream, ZipOutputStream} -import ErrorHandling.translate -import OpenResource._ +import xsbt.ErrorHandling.translate +import Using._ -abstract class OpenResource[Source, T] extends NotNull +abstract class Using[Source, T] extends NotNull { protected def open(src: Source): T def apply[R](src: Source)(f: T => R): R = @@ -28,57 +28,58 @@ abstract class OpenResource[Source, T] extends NotNull protected def close(out: T): Unit } import scala.reflect.{Manifest => SManifest} -abstract class WrapOpenResource[Source, T](implicit srcMf: SManifest[Source], targetMf: SManifest[T]) extends OpenResource[Source, T] +abstract class WrapUsing[Source, T](implicit srcMf: SManifest[Source], targetMf: SManifest[T]) extends Using[Source, T] { protected def label[S](m: SManifest[S]) = m.erasure.getSimpleName protected def openImpl(source: Source): T protected final def open(source: Source): T = translate("Error wrapping " + label(srcMf) + " in " + label(targetMf) + ": ") { openImpl(source) } } -trait OpenFile[T] extends OpenResource[File, T] +trait OpenFile[T] extends Using[File, T] { protected def openImpl(file: File): T protected final def open(file: File): T = { val parent = file.getParentFile if(parent != null) - FileUtilities.createDirectory(parent) + IO.createDirectory(parent) openImpl(file) } } -object OpenResource +object Using { - def wrap[Source, T<: Closeable](openF: Source => T)(implicit srcMf: SManifest[Source], targetMf: SManifest[T]): OpenResource[Source,T] = - wrap(openF, _.close) - def wrap[Source, T](openF: Source => T, closeF: T => Unit)(implicit srcMf: SManifest[Source], targetMf: SManifest[T]): OpenResource[Source,T] = - new WrapOpenResource[Source, T] + def wrap[Source, T<: Closeable](openF: Source => T)(implicit srcMf: SManifest[Source], targetMf: SManifest[T]): Using[Source,T] = + wrap(openF, closeCloseable) + def wrap[Source, T](openF: Source => T, closeF: T => Unit)(implicit srcMf: SManifest[Source], targetMf: SManifest[T]): Using[Source,T] = + new WrapUsing[Source, T] { def openImpl(source: Source) = openF(source) def close(t: T) = closeF(t) } - def resource[Source, T <: Closeable](openF: Source => T): OpenResource[Source,T] = - resource(openF, _.close) - def resource[Source, T](openF: Source => T, closeF: T => Unit): OpenResource[Source,T] = - new OpenResource[Source,T] + def resource[Source, T <: Closeable](openF: Source => T): Using[Source,T] = + resource(openF, closeCloseable) + def resource[Source, T](openF: Source => T, closeF: T => Unit): Using[Source,T] = + new Using[Source,T] { def open(s: Source) = openF(s) def close(s: T) = closeF(s) } - def file[T <: Closeable](openF: File => T): OpenFile[T] = file(openF, _.close()) + def file[T <: Closeable](openF: File => T): OpenFile[T] = file(openF, closeCloseable) def file[T](openF: File => T, closeF: T => Unit): OpenFile[T] = new OpenFile[T] { def openImpl(file: File) = openF(file) def close(t: T) = closeF(t) } + private def closeCloseable[T <: Closeable]: T => Unit = _.close() - def fileOutputStream(append: Boolean) = file(f => new FileOutputStream(f, append)) + def fileOutputStream(append: Boolean = false) = file(f => new FileOutputStream(f, append)) def fileInputStream = file(f => new FileInputStream(f)) def urlInputStream = resource( (u: URL) => translate("Error opening " + u + ": ")(u.openStream)) def fileOutputChannel = file(f => new FileOutputStream(f).getChannel) def fileInputChannel = file(f => new FileInputStream(f).getChannel) - def fileWriter(charset: Charset, append: Boolean) = + def fileWriter(charset: Charset = IO.utf8, append: Boolean = false) = file(f => new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f, append), charset)) ) def fileReader(charset: Charset) = file(f => new BufferedReader(new InputStreamReader(new FileInputStream(f), charset)) ) def jarFile(verify: Boolean) = file(f => new JarFile(f, verify), (_: JarFile).close()) diff --git a/util/io/src/test/scala/NameFilterSpecification.scala b/util/io/src/test/scala/NameFilterSpecification.scala index 515230f2c..0976decd4 100644 --- a/util/io/src/test/scala/NameFilterSpecification.scala +++ b/util/io/src/test/scala/NameFilterSpecification.scala @@ -1,37 +1,37 @@ /* sbt -- Simple Build Tool * Copyright 2008 Mark Harrah */ -package xsbt +package sbt import org.scalacheck._ import Prop._ object NameFilterSpecification extends Properties("NameFilter") { - specify("All pass accepts everything", (s: String) => AllPassFilter.accept(s)) - specify("Exact filter matches provided string", - (s1: String, s2: String) => (new ExactFilter(s1)).accept(s2) == (s1 == s2) ) - specify("Exact filter matches valid string", (s: String) => (new ExactFilter(s)).accept(s) ) + property("All pass accepts everything") = forAll{ (s: String) => AllPassFilter.accept(s) } + property("Exact filter matches provided string") = forAll { + (s1: String, s2: String) => (new ExactFilter(s1)).accept(s2) == (s1 == s2) } + property("Exact filter matches valid string") = forAll{ (s: String) => (new ExactFilter(s)).accept(s) } - specify("Glob filter matches provided string if no *s", + property("Glob filter matches provided string if no *s") = forAll { (s1: String, s2: String) => { val stripped = stripAsterisksAndControl(s1) (GlobFilter(stripped).accept(s2) == (stripped == s2)) - }) - specify("Glob filter matches valid string if no *s", + } } + property("Glob filter matches valid string if no *s") = forAll { (s: String) => { val stripped = stripAsterisksAndControl(s) GlobFilter(stripped).accept(stripped) - }) + }} - specify("Glob filter matches valid", + property("Glob filter matches valid") = forAll { (list: List[String]) => { val stripped = list.map(stripAsterisksAndControl) GlobFilter(stripped.mkString("*")).accept(stripped.mkString) - }) + }} /** Raw control characters are stripped because they are not allowed in expressions. * Asterisks are stripped because they are added under the control of the tests.*/ diff --git a/util/io/src/test/scala/WithFiles.scala b/util/io/src/test/scala/WithFiles.scala index 649f96175..19456ae27 100644 --- a/util/io/src/test/scala/WithFiles.scala +++ b/util/io/src/test/scala/WithFiles.scala @@ -1,7 +1,7 @@ -package xsbt +package sbt import java.io.File -import FileUtilities.{withTemporaryDirectory, write} +import IO.{withTemporaryDirectory, write} object WithFiles {