diff options
Diffstat (limited to 'src/reflect/scala/reflect/io')
-rw-r--r-- | src/reflect/scala/reflect/io/AbstractFile.scala | 3 | ||||
-rw-r--r-- | src/reflect/scala/reflect/io/File.scala | 8 | ||||
-rw-r--r-- | src/reflect/scala/reflect/io/Path.scala | 20 | ||||
-rw-r--r-- | src/reflect/scala/reflect/io/PlainFile.scala | 82 | ||||
-rw-r--r-- | src/reflect/scala/reflect/io/Streamable.scala | 4 | ||||
-rw-r--r-- | src/reflect/scala/reflect/io/ZipArchive.scala | 82 |
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) |