summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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