summaryrefslogtreecommitdiff
path: root/src/reflect/scala/reflect/io
diff options
context:
space:
mode:
Diffstat (limited to 'src/reflect/scala/reflect/io')
-rw-r--r--src/reflect/scala/reflect/io/AbstractFile.scala3
-rw-r--r--src/reflect/scala/reflect/io/File.scala8
-rw-r--r--src/reflect/scala/reflect/io/Path.scala20
-rw-r--r--src/reflect/scala/reflect/io/PlainFile.scala82
-rw-r--r--src/reflect/scala/reflect/io/Streamable.scala4
-rw-r--r--src/reflect/scala/reflect/io/ZipArchive.scala82
6 files changed, 155 insertions, 44 deletions
diff --git a/src/reflect/scala/reflect/io/AbstractFile.scala b/src/reflect/scala/reflect/io/AbstractFile.scala
index bcefcc471f..ee0bc129f8 100644
--- a/src/reflect/scala/reflect/io/AbstractFile.scala
+++ b/src/reflect/scala/reflect/io/AbstractFile.scala
@@ -8,10 +8,9 @@ package scala
package reflect
package io
-import java.io.{ FileOutputStream, IOException, InputStream, OutputStream, BufferedOutputStream, ByteArrayOutputStream }
+import java.io.{ IOException, InputStream, OutputStream, BufferedOutputStream, ByteArrayOutputStream }
import java.io.{ File => JFile }
import java.net.URL
-import scala.collection.mutable.ArrayBuffer
import scala.reflect.internal.util.Statistics
/**
diff --git a/src/reflect/scala/reflect/io/File.scala b/src/reflect/scala/reflect/io/File.scala
index a9c6807e88..206861adb3 100644
--- a/src/reflect/scala/reflect/io/File.scala
+++ b/src/reflect/scala/reflect/io/File.scala
@@ -6,20 +6,16 @@
** |/ **
\* */
-
package scala
package reflect
package io
import java.io.{
- FileInputStream, FileOutputStream, BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter,
- BufferedInputStream, BufferedOutputStream, IOException, PrintStream, PrintWriter, Closeable => JCloseable,
- File => JFile
+ FileInputStream, FileOutputStream, BufferedWriter, OutputStreamWriter,
+ BufferedOutputStream, IOException, PrintWriter, File => JFile
}
-import java.nio.channels.{ Channel, FileChannel }
import scala.io.Codec
-import scala.language.{reflectiveCalls, implicitConversions}
/**
* ''Note: This library is considered experimental and should not be used unless you know what you are doing.''
*/
diff --git a/src/reflect/scala/reflect/io/Path.scala b/src/reflect/scala/reflect/io/Path.scala
index 15fce953f2..c5b5ae24ba 100644
--- a/src/reflect/scala/reflect/io/Path.scala
+++ b/src/reflect/scala/reflect/io/Path.scala
@@ -7,12 +7,11 @@ package scala
package reflect
package io
-import java.io.{
- FileInputStream, FileOutputStream, BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter,
- BufferedInputStream, BufferedOutputStream, RandomAccessFile, File => JFile }
+import scala.language.implicitConversions
+
+import java.io.{ RandomAccessFile, File => JFile }
import java.net.{ URI, URL }
import scala.util.Random.alphanumeric
-import scala.language.implicitConversions
import scala.reflect.internal.util.Statistics
/** An abstraction for filesystem paths. The differences between
@@ -108,19 +107,20 @@ class Path private[io] (val jfile: JFile) {
def /(child: Directory): Directory = /(child: Path).toDirectory
def /(child: File): File = /(child: Path).toFile
- /** If this path is a container, recursively iterate over its contents.
+ /** If this path is a directory, recursively iterate over its contents.
* The supplied condition is a filter which is applied to each element,
- * with that branch of the tree being closed off if it is true. So for
- * example if the condition is true for some subdirectory, nothing
- * under that directory will be in the Iterator; but otherwise each
- * file and subdirectory underneath it will appear.
+ * with that branch of the tree being closed off if it is false.
+ * So for example if the condition is false for some subdirectory, nothing
+ * under that directory will be in the Iterator. If it's true, all files for
+ * which the condition holds and are directly in that subdirectory are in the
+ * Iterator, and all sub-subdirectories are recursively evaluated
*/
def walkFilter(cond: Path => Boolean): Iterator[Path] =
if (isFile) toFile walkFilter cond
else if (isDirectory) toDirectory walkFilter cond
else Iterator.empty
- /** Equivalent to walkFilter(_ => false).
+ /** Equivalent to walkFilter(_ => true).
*/
def walk: Iterator[Path] = walkFilter(_ => true)
diff --git a/src/reflect/scala/reflect/io/PlainFile.scala b/src/reflect/scala/reflect/io/PlainFile.scala
index 8f24d84488..989081ebe0 100644
--- a/src/reflect/scala/reflect/io/PlainFile.scala
+++ b/src/reflect/scala/reflect/io/PlainFile.scala
@@ -7,8 +7,6 @@ package scala
package reflect
package io
-import java.io.{ FileInputStream, FileOutputStream, IOException }
-
/** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */
class PlainDirectory(givenPath: Directory) extends PlainFile(givenPath) {
override def isDirectory = true
@@ -42,7 +40,6 @@ class PlainFile(val givenPath: Path) extends AbstractFile {
override def output = givenPath.toFile.outputStream()
override def sizeOption = Some(givenPath.length.toInt)
- override def toString = path
override def hashCode(): Int = fpath.hashCode()
override def equals(that: Any): Boolean = that match {
case x: PlainFile => fpath == x.fpath
@@ -93,3 +90,82 @@ class PlainFile(val givenPath: Path) extends AbstractFile {
def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile =
new PlainFile(givenPath / name)
}
+
+private[scala] class PlainNioFile(nioPath: java.nio.file.Path) extends AbstractFile {
+ import java.nio.file._
+
+ assert(nioPath ne null)
+
+ /** Returns the underlying File if any and null otherwise. */
+ override def file: java.io.File = try {
+ nioPath.toFile
+ } catch {
+ case _: UnsupportedOperationException => null
+ }
+
+ override def underlyingSource = Some(this)
+
+ private val fpath = nioPath.toAbsolutePath.toString
+
+ /** Returns the name of this abstract file. */
+ def name = nioPath.getFileName.toString
+
+ /** Returns the path of this abstract file. */
+ def path = nioPath.toString
+
+ /** The absolute file. */
+ def absolute = new PlainNioFile(nioPath.toAbsolutePath)
+
+ override def container: AbstractFile = new PlainNioFile(nioPath.getParent)
+ override def input = Files.newInputStream(nioPath)
+ override def output = Files.newOutputStream(nioPath)
+ override def sizeOption = Some(Files.size(nioPath).toInt)
+ override def hashCode(): Int = fpath.hashCode()
+ override def equals(that: Any): Boolean = that match {
+ case x: PlainNioFile => fpath == x.fpath
+ case _ => false
+ }
+
+ /** Is this abstract file a directory? */
+ def isDirectory: Boolean = Files.isDirectory(nioPath)
+
+ /** Returns the time that this abstract file was last modified. */
+ def lastModified: Long = Files.getLastModifiedTime(nioPath).toMillis
+
+ /** Returns all abstract subfiles of this abstract directory. */
+ def iterator: Iterator[AbstractFile] = {
+ try {
+ import scala.collection.JavaConverters._
+ val it = Files.newDirectoryStream(nioPath).iterator()
+ it.asScala.map(new PlainNioFile(_))
+ } catch {
+ case _: NotDirectoryException => Iterator.empty
+ }
+ }
+
+ /**
+ * Returns the abstract file in this abstract directory with the
+ * specified name. If there is no such file, returns null. The
+ * argument "directory" tells whether to look for a directory or
+ * or a regular file.
+ */
+ def lookupName(name: String, directory: Boolean): AbstractFile = {
+ val child = nioPath.resolve(name)
+ if ((Files.isDirectory(child) && directory) || (Files.isRegularFile(child) && !directory)) new PlainNioFile(child)
+ else null
+ }
+
+ /** Does this abstract file denote an existing file? */
+ def create(): Unit = if (!exists) Files.createFile(nioPath)
+
+ /** Delete the underlying file or directory (recursively). */
+ def delete(): Unit =
+ if (Files.isRegularFile(nioPath)) Files.deleteIfExists(nioPath)
+ else if (Files.isDirectory(nioPath)) new Directory(nioPath.toFile).deleteRecursively()
+
+ /** Returns a plain file with the given name. It does not
+ * check that it exists.
+ */
+ def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile =
+ new PlainNioFile(nioPath.resolve(name))
+}
diff --git a/src/reflect/scala/reflect/io/Streamable.scala b/src/reflect/scala/reflect/io/Streamable.scala
index 99a14d1fb0..bc4031ca9b 100644
--- a/src/reflect/scala/reflect/io/Streamable.scala
+++ b/src/reflect/scala/reflect/io/Streamable.scala
@@ -7,8 +7,8 @@ package scala
package reflect
package io
-import java.net.{ URI, URL }
-import java.io.{ BufferedInputStream, InputStream, PrintStream }
+import java.net.URL
+import java.io.{ BufferedInputStream, InputStream }
import java.io.{ BufferedReader, InputStreamReader, Closeable => JCloseable }
import scala.io.{ Codec, BufferedSource, Source }
import scala.collection.mutable.ArrayBuffer
diff --git a/src/reflect/scala/reflect/io/ZipArchive.scala b/src/reflect/scala/reflect/io/ZipArchive.scala
index 0c63acb86c..f4e1633af4 100644
--- a/src/reflect/scala/reflect/io/ZipArchive.scala
+++ b/src/reflect/scala/reflect/io/ZipArchive.scala
@@ -12,8 +12,8 @@ import java.io.{ IOException, InputStream, ByteArrayInputStream, FilterInputStre
import java.io.{ File => JFile }
import java.util.zip.{ ZipEntry, ZipFile, ZipInputStream }
import java.util.jar.Manifest
-import scala.collection.{ immutable, mutable }
-import scala.collection.convert.WrapAsScala.asScalaIterator
+import scala.collection.mutable
+import scala.collection.JavaConverters._
import scala.annotation.tailrec
/** An abstraction for zip files and streams. Everything is written the way
@@ -27,6 +27,8 @@ import scala.annotation.tailrec
* ''Note: This library is considered experimental and should not be used unless you know what you are doing.''
*/
object ZipArchive {
+ private[io] val closeZipFile = sys.props.get("scala.classpath.closeZip").map(_.toBoolean).getOrElse(false)
+
/**
* @param file a File
* @return A ZipArchive if `file` is a readable zip file, otherwise null.
@@ -120,31 +122,69 @@ abstract class ZipArchive(override val file: JFile) extends AbstractFile with Eq
}
/** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */
final class FileZipArchive(file: JFile) extends ZipArchive(file) {
+ private[this] def openZipFile(): ZipFile = try {
+ new ZipFile(file)
+ } catch {
+ case ioe: IOException => throw new IOException("Error accessing " + file.getPath, ioe)
+ }
+
+ private[this] class LazyEntry(
+ name: String,
+ time: Long,
+ size: Int
+ ) extends Entry(name) {
+ override def lastModified: Long = time // could be stale
+ override def input: InputStream = {
+ val zipFile = openZipFile()
+ val entry = zipFile.getEntry(name)
+ val delegate = zipFile.getInputStream(entry)
+ new FilterInputStream(delegate) {
+ override def close(): Unit = { zipFile.close() }
+ }
+ }
+ override def sizeOption: Option[Int] = Some(size) // could be stale
+ }
+
+ // keeps a file handle open to ZipFile, which forbids file mutation
+ // on Windows, and leaks memory on all OS (typically by stopping
+ // classloaders from being garbage collected). But is slightly
+ // faster than LazyEntry.
+ private[this] class LeakyEntry(
+ zipFile: ZipFile,
+ zipEntry: ZipEntry
+ ) extends Entry(zipEntry.getName) {
+ override def lastModified: Long = zipEntry.getTime
+ override def input: InputStream = zipFile.getInputStream(zipEntry)
+ override def sizeOption: Option[Int] = Some(zipEntry.getSize.toInt)
+ }
+
lazy val (root, allDirs) = {
val root = new DirEntry("/")
val dirs = mutable.HashMap[String, DirEntry]("/" -> root)
- val zipFile = try {
- new ZipFile(file)
- } catch {
- case ioe: IOException => throw new IOException("Error accessing " + file.getPath, ioe)
- }
-
+ val zipFile = openZipFile()
val enum = zipFile.entries()
- while (enum.hasMoreElements) {
- val zipEntry = enum.nextElement
- val dir = getDir(dirs, zipEntry)
- if (zipEntry.isDirectory) dir
- else {
- class FileEntry() extends Entry(zipEntry.getName) {
- override def getArchive = zipFile
- override def lastModified = zipEntry.getTime()
- override def input = getArchive getInputStream zipEntry
- override def sizeOption = Some(zipEntry.getSize().toInt)
+ try {
+ while (enum.hasMoreElements) {
+ val zipEntry = enum.nextElement
+ val dir = getDir(dirs, zipEntry)
+ if (zipEntry.isDirectory) dir
+ else {
+ val f =
+ if (ZipArchive.closeZipFile)
+ new LazyEntry(
+ zipEntry.getName(),
+ zipEntry.getTime(),
+ zipEntry.getSize().toInt
+ )
+ else
+ new LeakyEntry(zipFile, zipEntry)
+
+ dir.entries(f.name) = f
}
- val f = new FileEntry()
- dir.entries(f.name) = f
}
+ } finally {
+ if (ZipArchive.closeZipFile) zipFile.close()
}
(root, dirs)
}
@@ -238,7 +278,7 @@ final class ManifestResources(val url: URL) extends ZipArchive(null) {
val root = new DirEntry("/")
val dirs = mutable.HashMap[String, DirEntry]("/" -> root)
val manifest = new Manifest(input)
- val iter = manifest.getEntries().keySet().iterator().filter(_.endsWith(".class")).map(new ZipEntry(_))
+ val iter = manifest.getEntries().keySet().iterator().asScala.filter(_.endsWith(".class")).map(new ZipEntry(_))
for (zipEntry <- iter) {
val dir = getDir(dirs, zipEntry)