diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/CompileServer.scala | 7 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/CompileSocket.scala | 16 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/Interpreter.scala | 9 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ScriptRunner.scala | 58 | ||||
-rw-r--r-- | src/library/scala/Option.scala | 3 | ||||
-rw-r--r-- | src/library/scala/PartialFunction.scala | 8 | ||||
-rw-r--r-- | src/library/scala/collection/immutable/StringVector.scala | 1 | ||||
-rw-r--r-- | src/library/scala/io/Directory.scala | 59 | ||||
-rw-r--r-- | src/library/scala/io/File.scala | 126 | ||||
-rw-r--r-- | src/library/scala/io/FileOperationException.scala | 13 | ||||
-rw-r--r-- | src/library/scala/io/Path.scala | 151 | ||||
-rw-r--r-- | src/library/scala/io/Process.scala | 2 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/DirectRunner.scala | 11 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/nest/FileManager.scala | 8 |
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 |