summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2009-08-30 20:42:17 +0000
committerPaul Phillips <paulp@improving.org>2009-08-30 20:42:17 +0000
commitfe334907b39f63c6a010466f5b1f43fcee156756 (patch)
tree430efe143cfd61d5f926715d1743c7d6d6dc2b66
parent61635f0f584ac1b8a10ef2fff238b39aa7d21b83 (diff)
downloadscala-fe334907b39f63c6a010466f5b1f43fcee156756.tar.gz
scala-fe334907b39f63c6a010466f5b1f43fcee156756.tar.bz2
scala-fe334907b39f63c6a010466f5b1f43fcee156756.zip
Large Path/File/Directory checkin.
of the functionality that one can straightforwardly obtain from the capabilities of java.io.File in java 5, but written with an eye on the significantly more capable (if not significantly more appealing) nio2 API in openjdk.
-rw-r--r--src/compiler/scala/tools/nsc/CompileServer.scala7
-rw-r--r--src/compiler/scala/tools/nsc/CompileSocket.scala16
-rw-r--r--src/compiler/scala/tools/nsc/Interpreter.scala9
-rw-r--r--src/compiler/scala/tools/nsc/ScriptRunner.scala58
-rw-r--r--src/library/scala/Option.scala3
-rw-r--r--src/library/scala/PartialFunction.scala8
-rw-r--r--src/library/scala/collection/immutable/StringVector.scala1
-rw-r--r--src/library/scala/io/Directory.scala59
-rw-r--r--src/library/scala/io/File.scala126
-rw-r--r--src/library/scala/io/FileOperationException.scala13
-rw-r--r--src/library/scala/io/Path.scala151
-rw-r--r--src/library/scala/io/Process.scala2
-rw-r--r--src/partest/scala/tools/partest/nest/DirectRunner.scala11
-rw-r--r--src/partest/scala/tools/partest/nest/FileManager.scala8
14 files changed, 281 insertions, 191 deletions
diff --git a/src/compiler/scala/tools/nsc/CompileServer.scala b/src/compiler/scala/tools/nsc/CompileServer.scala
index 7cd4be214a..82d2eb04b9 100644
--- a/src/compiler/scala/tools/nsc/CompileServer.scala
+++ b/src/compiler/scala/tools/nsc/CompileServer.scala
@@ -55,7 +55,7 @@ class StandardCompileServer extends SocketServer
}
override def timeout() {
- if (!compileSocket.portFile(port).file.exists())
+ if (!compileSocket.portFile(port).exists)
fatal("port file no longer exists; skipping cleanup")
}
@@ -142,11 +142,10 @@ class StandardCompileServer extends SocketServer
}
/** A directory holding redirected output */
- private val redirectDir = File(compileSocket.tmpDir) / "output-redirects"
- redirectDir.mkdirs
+ private val redirectDir = (compileSocket.tmpDir / "output-redirects").createDirectory
private def redirect(setter: PrintStream => Unit, filename: String): Unit =
- setter(new PrintStream(redirectDir / filename bufferedOutput()))
+ setter(new PrintStream((redirectDir / filename).createFile.bufferedOutput()))
def main(args: Array[String]) {
redirect(System.setOut, "scala-compile-server-out.log")
diff --git a/src/compiler/scala/tools/nsc/CompileSocket.scala b/src/compiler/scala/tools/nsc/CompileSocket.scala
index fb4d4c50dc..eeec1e7bf4 100644
--- a/src/compiler/scala/tools/nsc/CompileSocket.scala
+++ b/src/compiler/scala/tools/nsc/CompileSocket.scala
@@ -12,7 +12,7 @@ import java.util.regex.Pattern
import java.net._
import java.security.SecureRandom
-import scala.io.File
+import scala.io.{ File, Path }
import scala.util.control.Exception.catching
// class CompileChannel { }
@@ -59,8 +59,7 @@ class CompileSocket {
/** A temporary directory to use */
val tmpDir = {
val udir = Option(Properties.userName) getOrElse "shared"
- val f = (File(Properties.tmpDir) / "scala-devel" / udir).file
- f.mkdirs()
+ val f = (Path(Properties.tmpDir) / "scala-devel" / udir).createDirectory()
if (f.isDirectory && f.canWrite) {
info("[Temp directory: " + f + "]")
@@ -70,8 +69,7 @@ class CompileSocket {
}
/* A directory holding port identification files */
- val portsDir = File(tmpDir) / dirName
- portsDir.mkdirs
+ val portsDir = (tmpDir / dirName).createDirectory
/** Maximum number of polls for an available port */
private val MaxAttempts = 100
@@ -103,11 +101,11 @@ class CompileSocket {
}
/** The port identification file */
- def portFile(port: Int) = portsDir / port.toString
+ def portFile(port: Int) = portsDir / File(port.toString)
/** Poll for a server port number; return -1 if none exists yet */
private def pollPort(): Int =
- portsDir.iterator.toList match {
+ portsDir.list.toList match {
case Nil => -1
case p :: xs =>
xs forall (_.delete())
@@ -141,7 +139,7 @@ class CompileSocket {
try file writeAll List(secret) catch {
case e @ (_: FileNotFoundException | _: SecurityException) =>
- fatal("Cannot create file: %s".format(file.absolutePath))
+ fatal("Cannot create file: %s".format(file.path))
}
}
@@ -214,7 +212,7 @@ class CompileSocket {
// allow some time for the server to start up
def check = {
Thread sleep 100
- ff.file.length()
+ ff.length
}
if (Iterator continually check take 50 find (_ > 0) isEmpty) {
ff.delete()
diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/compiler/scala/tools/nsc/Interpreter.scala
index 3f44bcdf9b..d34c4a8640 100644
--- a/src/compiler/scala/tools/nsc/Interpreter.scala
+++ b/src/compiler/scala/tools/nsc/Interpreter.scala
@@ -957,15 +957,6 @@ object Interpreter {
intLoop.closeInterpreter
}
- /** Delete a directory tree recursively. Use with care! */
- private[nsc] def deleteRecursively(path: File): Unit =
- if (path.exists) {
- if (path.isDirectory)
- path.listFiles foreach deleteRecursively
-
- path.delete
- }
-
/** Heuristically strip interpreter wrapper prefixes
* from an interpreter output string.
*/
diff --git a/src/compiler/scala/tools/nsc/ScriptRunner.scala b/src/compiler/scala/tools/nsc/ScriptRunner.scala
index 0a7b0aeeec..f99ea24031 100644
--- a/src/compiler/scala/tools/nsc/ScriptRunner.scala
+++ b/src/compiler/scala/tools/nsc/ScriptRunner.scala
@@ -12,7 +12,7 @@ import java.io.{
FileReader, InputStreamReader, PrintWriter, FileWriter,
IOException
}
-import scala.io.File
+import scala.io.{ Directory, File, Path }
// import scala.io.arm.ManagedResource
import java.io.{ File => JFile }
import java.lang.reflect.InvocationTargetException
@@ -71,12 +71,12 @@ object ScriptRunner
}
/** Choose a jar filename to hold the compiled version of a script. */
- private def jarFileFor(scriptFile: String): JFile = {
+ private def jarFileFor(scriptFile: String): File = {
val name =
if (scriptFile endsWith ".jar") scriptFile
else scriptFile + ".jar"
- File(name).file
+ File(name)
}
def copyStreams(in: InputStream, out: OutputStream) = {
@@ -93,22 +93,22 @@ object ScriptRunner
/** Try to create a jar file out of all the contents
* of the directory <code>sourcePath</code>.
*/
- private def tryMakeJar(jarFile: JFile, sourcePath: JFile) = {
- def addFromDir(jar: JarOutputStream, dir: JFile, prefix: String) {
- def addFileToJar(entry: JFile) = {
- jar putNextEntry new JarEntry(prefix + entry.getName)
- copyStreams(new FileInputStream(entry), jar)
+ private def tryMakeJar(jarFile: File, sourcePath: Directory) = {
+ def addFromDir(jar: JarOutputStream, dir: Directory, prefix: String) {
+ def addFileToJar(entry: File) = {
+ jar putNextEntry new JarEntry(prefix + entry.name)
+ copyStreams(entry.inputStream, jar)
jar.closeEntry
}
- dir.listFiles foreach { entry =>
- if (entry.isFile) addFileToJar(entry)
- else addFromDir(jar, entry, prefix + entry.getName + "/")
+ dir.list foreach { entry =>
+ if (entry.isFile) addFileToJar(entry.toFile)
+ else addFromDir(jar, entry.toDirectory, prefix + entry.name + "/")
}
}
try {
- val jar = new JarOutputStream(File(jarFile).outputStream())
+ val jar = new JarOutputStream(jarFile.outputStream())
addFromDir(jar, sourcePath, "")
jar.close
}
@@ -118,7 +118,7 @@ object ScriptRunner
}
/** Read the entire contents of a file as a String. */
- private def contentsOfFile(filename: String) = File(filename).toSource().mkString
+ private def contentsOfFile(filename: String) = File(filename).slurp()
/** Find the length of the header in the specified file, if
* there is one. The header part starts with "#!" or "::#!"
@@ -249,16 +249,14 @@ object ScriptRunner
scriptFile: String)
(handler: String => Boolean): Boolean =
{
- import Interpreter.deleteRecursively
-
/** Compiles the script file, and returns the directory with the compiled
* class files, if the compilation succeeded.
*/
- def compile: Option[JFile] = {
- val compiledPath = File tempdir "scalascript"
+ def compile: Option[Directory] = {
+ val compiledPath = Directory makeTemp "scalascript"
// delete the directory after the user code has finished
- addShutdownHook(deleteRecursively(compiledPath.file))
+ addShutdownHook(compiledPath.deleteRecursively())
settings.outdir.value = compiledPath.path
@@ -269,37 +267,37 @@ object ScriptRunner
val wrapped = wrappedScript(scriptMain(settings), scriptFile, compiler getSourceFile _)
cr compileSources List(wrapped)
- if (reporter.hasErrors) None else Some(compiledPath.file)
+ if (reporter.hasErrors) None else Some(compiledPath)
}
- else if (compileWithDaemon(settings, scriptFile)) Some(compiledPath.file)
+ else if (compileWithDaemon(settings, scriptFile)) Some(compiledPath)
else None
}
if (settings.savecompiled.value) {
- val jarFile = File(jarFileFor(scriptFile))
+ val jarFile = jarFileFor(scriptFile)
def jarOK = jarFile.canRead && (jarFile isFresher File(scriptFile))
def recompile() = {
- jarFile.delete
+ jarFile.delete()
compile match {
case Some(compiledPath) =>
- tryMakeJar(jarFile.file, compiledPath)
+ tryMakeJar(jarFile, compiledPath)
if (jarOK) {
- deleteRecursively(compiledPath)
- handler(jarFile.absolutePath)
+ compiledPath.deleteRecursively()
+ handler(jarFile.toAbsolute.path)
}
// jar failed; run directly from the class files
- else handler(compiledPath.getPath)
+ else handler(compiledPath.path)
case _ => false
}
}
- if (jarOK) handler(jarFile.absolutePath) // pre-compiled jar is current
- else recompile() // jar old - recompile the script.
+ if (jarOK) handler(jarFile.toAbsolute.path) // pre-compiled jar is current
+ else recompile() // jar old - recompile the script.
}
// don't use a cache jar at all--just use the class files
- else compile map (cp => handler(cp.getPath)) getOrElse false
+ else compile map (cp => handler(cp.path)) getOrElse false
}
/** Run a script after it has been compiled
@@ -368,7 +366,7 @@ object ScriptRunner
command: String,
scriptArgs: List[String]) : Boolean =
{
- val scriptFile = File.tempfile("scalacmd", ".scala")
+ val scriptFile = File.makeTemp("scalacmd", ".scala")
// save the command to the file
scriptFile writeAll List(command)
diff --git a/src/library/scala/Option.scala b/src/library/scala/Option.scala
index e40fc22829..df2d9639c1 100644
--- a/src/library/scala/Option.scala
+++ b/src/library/scala/Option.scala
@@ -29,6 +29,9 @@ object Option
@experimental
def apply[A](x: A): Option[A] = if (x == null) None else Some(x)
+ // For methods which return -1 on failure
+ // def fromReturnValue(value: Int): Option[Int] = if (value < 0) None else Some(value)
+
class NullableOption[A >: Null <: AnyRef](x: Option[A]) {
/** The option's value if it is nonempty, or <code>null</code> if it is empty.
* The use of null of course is discouraged, but code written to use Options
diff --git a/src/library/scala/PartialFunction.scala b/src/library/scala/PartialFunction.scala
index 189d76df53..247741c5d6 100644
--- a/src/library/scala/PartialFunction.scala
+++ b/src/library/scala/PartialFunction.scala
@@ -51,11 +51,11 @@ trait PartialFunction[-A, +B] extends AnyRef with (A => B) {
* <pre>
* import PartialFunction._
*
- * def strangeConditional(other: Any): Boolean = ?(other) {
+ * def strangeConditional(other: Any): Boolean = cond(other) {
* case x: String if x == "abc" || x == "def" => true
* case x: Int => true
* }
- * def onlyInt(v: Any): Option[Int] = opt(v) { case x: Int => x }
+ * def onlyInt(v: Any): Option[Int] = condOpt(v) { case x: Int => x }
* </pre>
*
* @author Paul Phillips
@@ -72,7 +72,7 @@ object PartialFunction
* @param pf the partial function
* @return true, iff <code>x</code> is in the domain of pf && pf(x) == true
*/
- def ?[T](x: T)(pf: PartialFunction[T, Boolean]): Boolean =
+ def cond[T](x: T)(pf: PartialFunction[T, Boolean]): Boolean =
(pf isDefinedAt x) && pf(x)
/** Transforms a PartialFunction[T,U] `pf' into Function1[T,Option[U]] `f'
@@ -84,7 +84,7 @@ object PartialFunction
* @param pf the PartialFunction[T,U]
* @return Some(pf(x)) iff (pf isDefinedAt x) and None otherwise
*/
- def opt[T,U](x: T)(pf: PartialFunction[T, U]): Option[U] =
+ def condOpt[T,U](x: T)(pf: PartialFunction[T, U]): Option[U] =
if (pf isDefinedAt x) Some(pf(x)) else None
// If only getOrElse were a bit less unwieldy...
diff --git a/src/library/scala/collection/immutable/StringVector.scala b/src/library/scala/collection/immutable/StringVector.scala
index 9dd64abf8a..3a3f2c0944 100644
--- a/src/library/scala/collection/immutable/StringVector.scala
+++ b/src/library/scala/collection/immutable/StringVector.scala
@@ -152,6 +152,7 @@ object StringVector
while (i >= 0) {
if (p(s charAt i))
return i
+ i -= 1
}
-1
}
diff --git a/src/library/scala/io/Directory.scala b/src/library/scala/io/Directory.scala
index 5451d1eaf8..13e0d15e45 100644
--- a/src/library/scala/io/Directory.scala
+++ b/src/library/scala/io/Directory.scala
@@ -13,48 +13,61 @@ import collection.Traversable
object Directory
{
- def apply(fileName: String) = new Directory(new JFile(fileName))
- def apply(file: JFile) = new Directory(file)
- def apply(file: File) = new Directory(file.file)
+ def apply(path: Path) = path.toDirectory
+
+ // Like File.makeTemp but creates a directory instead
+ def makeTemp(prefix: String = Path.randomPrefix, suffix: String = null, dir: JFile = null): Directory = {
+ val path = File.makeTemp(prefix, suffix, dir)
+ path.delete()
+ path.createDirectory()
+ }
}
-import Directory._
+import Path._
/** An abstraction for directories.
*
* @author Paul Phillips
* @since 2.8
*/
-class Directory(val file: JFile) extends collection.Iterable[File]
+class Directory(jfile: JFile) extends Path(jfile)
{
- /** At creation time we enforce that if the path in question
- * exists, it is a directory. Obviously you can fool it by
- * changing the situation after this instance is created, so
- * don't consider this a lasting guarantee.
- */
- require(!file.exists() || file.isDirectory())
+ override def toDirectory: Directory = this
+ override def toFile: File = new File(jfile)
+ override def create(): Boolean = jfile.mkdirs()
+ override def isValid = jfile.isDirectory() || !jfile.exists()
/** An iterator over the contents of this directory.
*/
- def iterator: Iterator[File] =
- file.listFiles match {
+ def list: Iterator[Path] =
+ jfile.listFiles match {
case null => Iterator.empty
- case xs => xs.iterator map File.apply
+ case xs => xs.iterator map Path.apply
}
+ def dirs: Iterator[Directory] = list filterMap { case x: Directory => x }
+ def files: Iterator[File] = list filterMap { case x: File => x }
+
+ def deepList(depth: Int = 1): Iterator[Path] =
+ if (depth == 0) Iterator.empty
+ else list ++ (dirs flatMap (_ deepList (depth - 1)))
+
/** An iterator over the directories underneath this directory,
* to the (optionally) given depth.
*/
def subdirs(depth: Int = 1): Iterator[Directory] =
- if (depth == 0) Iterator.empty else {
- val (d1, d2) = iterator filter (_.file.isDirectory) map Directory.apply duplicate
+ deepList(depth) filterMap { case x: Directory => x }
- d1 ++ (d2 flatMap (_ subdirs (depth - 1)))
+ /** Deletes the directory recursively. Returns false on failure.
+ * Use with caution!
+ */
+ def deleteRecursively(): Boolean = deleteRecursively(jfile)
+ private def deleteRecursively(f: JFile): Boolean = {
+ if (f.isDirectory) f.listFiles match {
+ case null =>
+ case xs => xs foreach deleteRecursively
}
-
- override def toString() = "Directory(%s)".format(file.getCanonicalPath())
- override def equals(other: Any) = other match {
- case x: Directory => this.file == x.file
- case _ => false
+ f.delete()
}
- override def hashCode = file.hashCode
+
+ override def toString() = "Directory(%s)".format(path)
}
diff --git a/src/library/scala/io/File.scala b/src/library/scala/io/File.scala
index f1cdb452dc..23ab25cfb1 100644
--- a/src/library/scala/io/File.scala
+++ b/src/library/scala/io/File.scala
@@ -17,143 +17,87 @@ import collection.Traversable
object File
{
- final val extensionRegex = """^.*\.([^.]+)$""".r
- implicit def fileWrapper(file: JFile): File = new File(file)
+ def apply(path: Path) = path.toFile
- def apply(fileName: String) = new File(new JFile(fileName))
- def apply(file: JFile) = new File(file)
-
- private def randomPrefix = {
- import scala.util.Random.nextInt
- import Character.isJavaIdentifierPart
-
- (Iterator continually nextInt)
- . map (x => ((x % 60) + 'A').toChar)
- . filter (isJavaIdentifierPart)
- . take (6)
- . mkString
- }
// Create a temporary file
- def tempfile(prefix: String = randomPrefix, suffix: String = null, dir: JFile = null) =
+ def makeTemp(prefix: String = Path.randomPrefix, suffix: String = null, dir: JFile = null) =
apply(JFile.createTempFile(prefix, suffix, dir))
-
- // Like tempfile but creates a directory instead
- def tempdir(prefix: String = randomPrefix, suffix: String = null, dir: JFile = null) = {
- val file = tempfile(prefix, suffix, dir)
- file.delete()
- file.mkdirs()
- file
- }
-
}
-import File._
+import Path._
-/** An abstraction for files. For now it is little more than a wrapper
- * around java.io.File.
+/** An abstraction for files. For character data, a Codec
+ * can be supplied at either creation time or when a method
+ * involving character data is called (with the latter taking
+ * precdence if supplied.) If neither is available, the value
+ * of scala.io.Codec.default is used.
*
* @author Paul Phillips
* @since 2.8
*/
-class File(val file: JFile)(implicit val codec: Codec = Codec.default) extends collection.Iterable[File]
+class File(jfile: JFile)(implicit val creationCodec: Codec = null)
+extends Path(jfile)
{
- def name = file.getName()
- def path = file.getPath()
- def absolutePath = file.getAbsolutePath()
- def delete() = file.delete()
- def mkdirs() = file.mkdirs()
- def isFile = file.isFile()
- def canRead = file.canRead()
- def canWrite = file.canWrite()
-
- def isFresher(other: File) = file.lastModified > other.file.lastModified
-
- /** If file is a directory, an iterator over its contents.
- * If not, an empty iterator.
- */
- def iterator: Iterator[File] =
- if (file.isDirectory) file.listFiles.iterator map (x => new File(x))
- else Iterator.empty
+ private def getCodec(): Codec =
+ if (creationCodec == null) Codec.default else creationCodec
- /** Convenience function for iterating over the lines in the file.
- */
- def lines(): Iterator[String] = toSource().getLines()
+ override def toDirectory: Directory = new Directory(jfile)
+ override def toFile: File = this
+ override def create(): Boolean = jfile.createNewFile()
+ override def isValid = jfile.isFile() || !jfile.exists()
- /** Convenience function for iterating over the bytes in a file.
- * Note they are delivered as Ints as they come from the read() call,
- * you can map (_.toByte) if you would really prefer Bytes.
+ /** Convenience functions for iterating over the bytes in a file.
*/
- def bytes(): Iterator[Int] = {
+ def bytesAsInts(): Iterator[Int] = {
val in = bufferedInput()
Iterator continually in.read() takeWhile (_ != -1)
}
- /** Convenience function to import entire file into a String.
+ def bytes(): Iterator[Byte] = bytesAsInts() map (_.toByte)
+ def chars(codec: Codec = getCodec()) = (Source fromFile jfile)(codec)
+
+ /** Convenience function for iterating over the lines in the file.
*/
- def slurp() = toSource().mkString
+ def lines(codec: Codec = getCodec()): Iterator[String] = chars(codec).getLines()
- /** Deletes the file or directory recursively. Returns false if it failed.
- * Use with caution!
+ /** Convenience function to import entire file into a String.
*/
- def deleteRecursively(): Boolean = deleteRecursively(file)
- private def deleteRecursively(f: JFile): Boolean = {
- if (f.isDirectory) f.listFiles match {
- case null =>
- case xs => xs foreach deleteRecursively
- }
- f.delete()
- }
+ def slurp(codec: Codec = getCodec()) = chars(codec).mkString
/** Obtains an InputStream. */
- def inputStream() = new FileInputStream(file)
+ def inputStream() = new FileInputStream(jfile)
def bufferedInput() = new BufferedInputStream(inputStream())
/** Obtains a OutputStream. */
- def outputStream(append: Boolean = false) = new FileOutputStream(file, append)
+ def outputStream(append: Boolean = false) = new FileOutputStream(jfile, append)
def bufferedOutput(append: Boolean = false) = new BufferedOutputStream(outputStream(append))
/** Obtains an InputStreamReader wrapped around a FileInputStream.
*/
- def reader() =
+ def reader(codec: Codec = getCodec()) =
new InputStreamReader(inputStream, codec.charSet)
/** Obtains an OutputStreamWriter wrapped around a FileOutputStream.
* This should behave like a less broken version of java.io.FileWriter,
* in that unlike the java version you can specify the encoding.
*/
- def writer(append: Boolean = false) =
+ def writer(append: Boolean = false, codec: Codec = getCodec()) =
new OutputStreamWriter(outputStream(append), codec.charSet)
/** Wraps a BufferedReader around the result of reader().
*/
- def bufferedReader() = new BufferedReader(reader())
+ def bufferedReader(codec: Codec = getCodec()) = new BufferedReader(reader(codec))
/** Wraps a BufferedWriter around the result of writer().
*/
- def bufferedWriter(append: Boolean = false) = new BufferedWriter(writer(append))
+ def bufferedWriter(append: Boolean = false, codec: Codec = getCodec()) =
+ new BufferedWriter(writer(append, codec))
/** Writes all the Strings in the given iterator to the file. */
- def writeAll(xs: Traversable[String], append: Boolean = false): Unit = {
- val out = bufferedWriter(append)
+ def writeAll(xs: Traversable[String], append: Boolean = false, codec: Codec = getCodec()): Unit = {
+ val out = bufferedWriter(append, codec)
try xs foreach (out write _)
finally out close
}
- /** Attempts to return the file extension. */
- def extension = file.getName match {
- case extensionRegex(x) => Some(x)
- case _ => None
- }
-
- /** Creates a Source from this file. */
- def toSource(): Source = (Source fromFile file)(codec)
-
- /** Creates a new File with the specified path appended. */
- def /(child: String) = new File(new JFile(file, child))
-
- override def toString() = file.toString
- override def equals(other: Any) = other match {
- case x: File => this.file == x.file
- case _ => false
- }
- override def hashCode = file.hashCode
+ override def toString() = "File(%s)".format(path)
}
diff --git a/src/library/scala/io/FileOperationException.scala b/src/library/scala/io/FileOperationException.scala
new file mode 100644
index 0000000000..6ee4c91447
--- /dev/null
+++ b/src/library/scala/io/FileOperationException.scala
@@ -0,0 +1,13 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2009, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+// $Id$
+
+package scala.io
+
+case class FileOperationException(msg: String) extends RuntimeException(msg)
diff --git a/src/library/scala/io/Path.scala b/src/library/scala/io/Path.scala
index 257b09a4c5..41ecfceee5 100644
--- a/src/library/scala/io/Path.scala
+++ b/src/library/scala/io/Path.scala
@@ -8,18 +8,155 @@
package scala.io
-import collection.Traversable
+import java.io.{
+ FileInputStream, FileOutputStream, BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter,
+ BufferedInputStream, BufferedOutputStream, File => JFile }
+import java.net.{ URI, URL }
+import collection.{ Sequence, Traversable }
+import collection.immutable.{ StringVector => SV }
+import PartialFunction._
+import util.Random.nextASCIIString
+
+/** An abstraction for filesystem paths. The differences between
+ * Path, File, and Directory are primarily to communicate intent.
+ * Since the filesystem can change at any time, there is no way to
+ * reliably associate Files only with files and so on. Any Path
+ * can be converted to a File or Directory (and thus gain access to
+ * the additional entity specific methods) by calling toFile or
+ * toDirectory, which has no effect on the filesystem.
+ *
+ * Also available are createFile and createDirectory, which attempt
+ * to create the path in question.
+ *
+ * @author Paul Phillips
+ * @since 2.8
+ */
object Path
{
- //
- // def canonicalize
- def apply(path: String) = new Path(path)
+ // not sure whether this will be problematic
+ implicit def string2path(s: String): Path = apply(s)
+ implicit def jfile2path(jfile: JFile): Path = apply(jfile)
+
+ // java 7 style, we don't use it yet
+ // object AccessMode extends Enumeration("AccessMode") {
+ // val EXECUTE, READ, WRITE = Value
+ // }
+ // def checkAccess(modes: AccessMode*): Boolean = {
+ // modes foreach {
+ // case EXECUTE => throw new Exception("Unsupported") // can't check in java 5
+ // case READ => if (!jfile.canRead()) return false
+ // case WRITE => if (!jfile.canWrite()) return false
+ // }
+ // true
+ // }
+
+ def roots: List[Path] = JFile.listRoots().toList map Path.apply
+
+ def apply(path: String): Path = apply(new JFile(path))
+ def apply(jfile: JFile): Path = {
+ if (jfile.isFile) new File(jfile)
+ else if (jfile.isDirectory) new Directory(jfile)
+ else new Path(jfile)
+ }
+ private[io] def randomPrefix = nextASCIIString(6)
+ private[io] def fail(msg: String) = throw FileOperationException(msg)
}
import Path._
-// The path constructor is private so we can enforce that
-// the value of `path' is always in its canonical form.
-class Path private (val path: String)
+/** The Path constructor is private so we can enforce some
+ * semantics regarding how a Path might relate to the world.
+ */
+class Path private[io] (val jfile: JFile)
{
+ val separator = JFile.separatorChar
+
+ // Validation: this verifies that the type of this object and the
+ // contents of the filesystem are in agreement. All objects are
+ // valid except File objects whose path points to a directory and
+ // Directory objects whose path points to a file.
+ def isValid: Boolean = true
+
+ // conversions
+ def toFile: File = new File(jfile)
+ def toDirectory: Directory = new Directory(jfile)
+ def toAbsolute: Path = if (isAbsolute) this else Path(jfile.getAbsolutePath())
+ def toURI: URI = jfile.toURI()
+ def toURL: URL = toURI.toURL()
+
+ /** Creates a new Path with the specified path appended. Assumes
+ * the type of the new component implies the type of the result.
+ */
+ def /(child: Path): Path = new Path(new JFile(jfile, child.path))
+ def /(child: Directory): Directory = /(child: Path).toDirectory
+ def /(child: File): File = /(child: Path).toFile
+
+ // identity
+ def name: String = jfile.getName()
+ def path: String = jfile.getPath()
+ def normalize: Path = Path(jfile.getCanonicalPath())
+ // todo -
+ // def resolve(other: Path): Path
+ // def relativize(other: Path): Path
+
+ // derived from identity
+ def root: Option[Path] = roots find (this startsWith _)
+ def segments: List[String] = (path split separator).toList filterNot (_.isEmpty)
+ def parent: Option[Path] = Option(jfile.getParent()) map Path.apply
+ def parents: List[Path] = parent match {
+ case None => Nil
+ case Some(p) => p :: p.parents
+ }
+ // if name ends with an extension (e.g. "foo.jpg") returns the extension ("jpg")
+ def extension: Option[String] =
+ condOpt(SV.lastIndexWhere(name, _ == '.')) {
+ case idx if idx != -1 => SV.drop(name, idx + 1)
+ }
+ // Alternative approach:
+ // (Option fromReturnValue SV.lastIndexWhere(name, _ == '.') map (x => SV.drop(name, x + 1))
+
+ // Boolean tests
+ def canRead = jfile.canRead()
+ def canWrite = jfile.canWrite()
+ def exists = jfile.exists()
+ def notExists = try !jfile.exists() catch { case ex: SecurityException => false }
+
+ def isFile = jfile.isFile()
+ def isDirectory = jfile.isDirectory()
+ def isAbsolute = jfile.isAbsolute()
+ def isHidden = jfile.isHidden()
+
+ // Information
+ def lastModified = jfile.lastModified()
+ def length = jfile.length()
+
+ // Boolean path comparisons
+ def endsWith(other: Path) = segments endsWith other.segments
+ def startsWith(other: Path) = segments startsWith other.segments
+ def isSame(other: Path) = toAbsolute == other.toAbsolute
+ def isFresher(other: Path) = lastModified > other.lastModified
+
+ // creations
+ def create(): Boolean = true
+ def createDirectory(): Directory =
+ if (jfile.mkdirs()) new Directory(jfile)
+ else fail("Failed to create new directory.")
+ def createFile(): File =
+ if (jfile.createNewFile()) new File(jfile)
+ else fail("Failed to create new file.")
+
+ // deletions
+ def delete() = jfile.delete()
+ def deleteIfExists() = if (jfile.exists()) delete() else false
+
+ // todo
+ // def copyTo(target: Path, options ...): Boolean
+ // def moveTo(target: Path, options ...): Boolean
+
+ override def toString() = "Path(%s)".format(path)
+ override def equals(other: Any) = other match {
+ case x: Path => path == x.path
+ case _ => false
+ }
+ override def hashCode() = path.hashCode()
}
diff --git a/src/library/scala/io/Process.scala b/src/library/scala/io/Process.scala
index 28209648f2..99b9549abd 100644
--- a/src/library/scala/io/Process.scala
+++ b/src/library/scala/io/Process.scala
@@ -63,7 +63,7 @@ object Process
def withCwd(cwd: File): this.type = {
if (cwd != null)
- pb directory cwd.file
+ pb directory cwd.jfile
this
}
diff --git a/src/partest/scala/tools/partest/nest/DirectRunner.scala b/src/partest/scala/tools/partest/nest/DirectRunner.scala
index 3cc84b8d89..160fee3d5a 100644
--- a/src/partest/scala/tools/partest/nest/DirectRunner.scala
+++ b/src/partest/scala/tools/partest/nest/DirectRunner.scala
@@ -11,6 +11,7 @@ package nest
import java.io.{File, PrintStream, FileOutputStream, BufferedReader,
InputStreamReader, StringWriter, PrintWriter}
import java.util.StringTokenizer
+import scala.io.Directory
import scala.actors.Actor._
import scala.actors.TIMEOUT
@@ -66,13 +67,9 @@ trait DirectRunner {
fails += 1
}
}
- logsToDelete.foreach { log =>
- NestUI.verbose("deleting "+log)
- fileManager.deleteRecursive(log)
- }
- outdirsToDelete.foreach { outdir =>
- NestUI.verbose("deleting "+outdir)
- fileManager.deleteRecursive(outdir)
+ for (x <- logsToDelete ::: outdirsToDelete) {
+ NestUI.verbose("deleting "+x)
+ Directory(x).deleteRecursively()
}
(succs, fails)
diff --git a/src/partest/scala/tools/partest/nest/FileManager.scala b/src/partest/scala/tools/partest/nest/FileManager.scala
index bdce6c8439..f5cc52c555 100644
--- a/src/partest/scala/tools/partest/nest/FileManager.scala
+++ b/src/partest/scala/tools/partest/nest/FileManager.scala
@@ -10,6 +10,7 @@ package nest
import java.io.{File, FilenameFilter, IOException, StringWriter}
import java.net.URI
+import scala.io.Directory
trait FileManager {
@@ -18,12 +19,7 @@ trait FileManager {
if (inx < 0) name else name.substring(0, inx)
}
- def deleteRecursive(dir: File) {
- if (dir.isDirectory) {
- for (file <- dir.list) deleteRecursive(new File(dir, file))
- }
- dir.delete
- }
+ def deleteRecursive(dir: File) { Directory(dir).deleteRecursively() }
/**
* Compares two files using a Java implementation of the GNU diff