summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/io/ZipArchive.scala
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2009-08-31 15:46:46 +0000
committerPaul Phillips <paulp@improving.org>2009-08-31 15:46:46 +0000
commitd227d486fdb94ef3bffcfe048a3069db3d9c14e1 (patch)
tree26968c7974e11cd24a527cf466c2a40275447e6e /src/compiler/scala/tools/nsc/io/ZipArchive.scala
parent09ba9ab65e4e10ce05f099e4bf28a16341dac70c (diff)
downloadscala-d227d486fdb94ef3bffcfe048a3069db3d9c14e1.tar.gz
scala-d227d486fdb94ef3bffcfe048a3069db3d9c14e1.tar.bz2
scala-d227d486fdb94ef3bffcfe048a3069db3d9c14e1.zip
Modernizing bits of scala.tools.nsc.io and incr...
Modernizing bits of scala.tools.nsc.io and incrementally introducing some of scala.io to further flesh out what the File interface will require.
Diffstat (limited to 'src/compiler/scala/tools/nsc/io/ZipArchive.scala')
-rw-r--r--src/compiler/scala/tools/nsc/io/ZipArchive.scala295
1 files changed, 135 insertions, 160 deletions
diff --git a/src/compiler/scala/tools/nsc/io/ZipArchive.scala b/src/compiler/scala/tools/nsc/io/ZipArchive.scala
index 9d322af889..7951f7802d 100644
--- a/src/compiler/scala/tools/nsc/io/ZipArchive.scala
+++ b/src/compiler/scala/tools/nsc/io/ZipArchive.scala
@@ -8,12 +8,16 @@
package scala.tools.nsc
package io
-import java.io.{File, IOException, InputStream}
+import scala.io.File
import java.net.URL
import java.util.Enumeration
-import java.util.zip.{ZipEntry, ZipFile, ZipInputStream}
+import java.io.{ File => JFile, IOException, InputStream, BufferedInputStream }
+import java.util.zip.{ ZipEntry, ZipFile, ZipInputStream }
+import PartialFunction._
-import scala.collection.mutable.{Map, HashMap}
+import scala.collection.mutable.{ Map, HashMap }
+import scala.collection.immutable.{ StringVector => SV }
+import scala.collection.JavaConversions.asIterator
/**
* @author Philippe Altherr
@@ -29,7 +33,7 @@ object ZipArchive {
* @param path ...
* @return ...
*/
- def fromPath(path: String): AbstractFile = fromFile(new File(path))
+ def fromPath(path: String): AbstractFile = fromFile(File(path))
/**
* If the specified file <code>file</code> exists and is a readable
@@ -40,7 +44,7 @@ object ZipArchive {
* @return ...
*/
def fromFile(file: File): AbstractFile =
- try { new ZipArchive(file, new ZipFile(file)) }
+ try { new ZipArchive(file, new ZipFile(file.jfile)) }
catch { case _: IOException => null }
/**
@@ -50,7 +54,7 @@ object ZipArchive {
* @return ...
*/
def fromArchive(archive: ZipFile): AbstractFile =
- new ZipArchive(new File(archive.getName()), archive)
+ new ZipArchive(File(archive.getName()), archive)
/**
* Returns an abstract directory backed by the specified archive.
@@ -62,6 +66,28 @@ object ZipArchive {
new URLZipArchive(url)
}
+private[io] trait ZipFileCommon
+{
+ import SV.{ splitAt }
+
+ protected def splitPath(path: String): (String, String) = {
+ (path lastIndexOf '/') match {
+ case -1 => ("/", path)
+ case idx => splitAt(path, idx + 1)
+ }
+ }
+
+ protected def byteInputStream(in: InputStream): InputStream = {
+ val buf = new BufferedInputStream(in)
+ val bytes = Iterator continually in.read().toByte takeWhile (_ != -1)
+ new java.io.ByteArrayInputStream(bytes.toSequence.toArray)
+ }
+
+ protected def zipEntryIterator(zis: ZipInputStream): Iterator[ZipEntry] = {
+ Iterator continually zis.getNextEntry() takeWhile (_ != null)
+ }
+}
+
/**
* This class implements an abstract directory backed by a zip
* archive. We let the encoding be <code>null</code>, because we behave like
@@ -70,26 +96,40 @@ object ZipArchive {
* @author Philippe Altherr
* @version 1.0, 23/03/2004
*/
-final class ZipArchive(file: File, val archive: ZipFile) extends PlainFile(file) {
-
+final class ZipArchive(file: File, val archive: ZipFile) extends PlainFile(file) with ZipFileCommon
+{
assert(archive ne null)
- //########################################################################
- // Private Fields
- /** The root directory or null if not yet initialized */
- private var root: DirEntry = _
+ /** The root directory */
+ private lazy val root = {
+ val root = new DirEntry(this, "<root>", "/")
- //########################################################################
- // Public Methods
+ // A path to DirEntry map
+ val dirs: Map[String, DirEntry] = new HashMap()
+ dirs("/") = root
+ val entries = asIterator(archive.entries())
+
+ entries foreach { case entry: ZipEntry =>
+ val path = entry.getName
+ if (entry.isDirectory) {
+ val dir: DirEntry = getDir(dirs, path)
+ if (dir.entry == null) dir.entry = entry
+ }
+ else {
+ val (home, name) = splitPath(path)
+ val parent: DirEntry = getDir(dirs, home)
+ parent.entries.update(name, new FileEntry(parent, name, path, entry))
+ }
+ }
+
+ root
+ }
/** Returns true. */
override def isDirectory = true
/** Returns all abstract subfiles of this abstract directory. */
- override def iterator: Iterator[AbstractFile] = {
- if (root eq null) load()
- root.iterator
- }
+ override def iterator: Iterator[AbstractFile] = root.iterator
/**
* Returns the abstract file in this abstract directory with the
@@ -97,10 +137,8 @@ final class ZipArchive(file: File, val archive: ZipFile) extends PlainFile(file)
* argument "directory" tells whether to look for a directory or
* or a regular file.
*/
- override def lookupName(name: String, directory: Boolean): AbstractFile = {
- if (root eq null) load()
+ override def lookupName(name: String, directory: Boolean): AbstractFile =
root.lookupName(name, directory)
- }
/** Returns an abstract file with the given name. It does not
* check that it exists.
@@ -108,86 +146,54 @@ final class ZipArchive(file: File, val archive: ZipFile) extends PlainFile(file)
override def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile =
throw new UnsupportedOperationException()
- //########################################################################
- // Private Methods
-
- /** Loads the archive and creates the root directory. */
- private def load() {
- this.root = new DirEntry(this, "<root>", "/")
- // A path to DirEntry map
- val dirs: Map[String, DirEntry] = new HashMap()
- dirs.update("/", root)
- val entries = archive.entries()
- while (entries.hasMoreElements()) {
- val entry = entries.nextElement().asInstanceOf[ZipEntry]
- val path = entry.getName()
- assert(entry.isDirectory() == path.endsWith("/"),
- this.toString() + " - " + path);
- if (entry.isDirectory()) {
- val dir: DirEntry = getDir(dirs, path)
- // this assertion causes an unnecessary bomb if a directory is twice listed in the jar
- // assert(dir.entry eq null, this.toString() + " - " + path)
- if (dir.entry eq null) dir.entry = entry
- } else {
- val index = path.lastIndexOf('/')
- val name = if (index < 0) path else path.substring(index + 1)
- val home = if (index < 0) "/" else path.substring(0, index + 1)
- val parent: DirEntry = getDir(dirs, home)
- // OLD: assert(!parent.entries.contains(path))
- // MAYBE: assert(!parent.entries.contains(name))
- //if (parent.entries.contains(name))
- // Console.println("XXX: " + this.toString() + " - " + home + "/" + name)
- parent.entries.update(name, new FileEntry(parent, name, path, entry))
- }
- }
- }
-
/**
* Lookups the specified table for a DirEntry with the specified
* path. If successful, returns the found DirEntry. Otherwise
* creates a new DirEntry, enters it into the table and in the
* table of its parent ZipDir and returns it.
*/
- private def getDir(dirs: Map[String,DirEntry], path: String): DirEntry =
- dirs.get(path) match {
- case Some(dir) => dir
- case None =>
- val index = path.lastIndexOf('/', path.length() - 2);
- val name = if (index < 0) path else path.substring(index + 1);
- val home = if (index < 0) "/" else path.substring(0, index + 1);
- val parent: DirEntry = getDir(dirs, home);
- val dir = new DirEntry(parent, name.substring(0, name.length() - 1), path);
- parent.entries.update(name, dir);
- dirs.update(path, dir);
- dir
- }
+ private def getDir(dirs: Map[String, DirEntry], path: String): DirEntry =
+ (dirs get path) getOrElse {
+ val (home, name) = splitPath(SV.dropRight(path, 1))
+ val parent: DirEntry = getDir(dirs, home)
+ val dir = new DirEntry(parent, name, path)
+ parent.entries.update(name + SV.last(path), dir)
+ dirs.update(path, dir)
+ dir
+ }
//########################################################################
// Private Class - Entry
/** Superclass of archive entries */
- abstract class Entry(override val container : AbstractFile, name: String, path: String)
- extends VirtualFile(name, path) {
- final override def path = ZipArchive.this.toString() + "(" + pathInArchive + ")"
+ abstract class Entry(
+ override val container: AbstractFile,
+ name: String,
+ path: String
+ ) extends VirtualFile(name, path)
+ {
+ final override def path = "%s(%s)".format(ZipArchive.this, pathInArchive)
final def getArchive = ZipArchive.this.archive
def pathInArchive = super.path
+
override def hashCode = super.hashCode + container.hashCode
- override def equals(that : Any) = super.equals(that) && (that match {
- case entry : Entry => container == entry.container
- case _ => false
- })
+ override def equals(that : Any) =
+ super.equals(that) && (cond(that) {
+ case e: Entry => container == e.container
+ })
}
//########################################################################
// Private Class - DirEntry
/** A directory archive entry */
- private final class DirEntry(container : AbstractFile, name: String, path: String)
- extends Entry(container, name, path)
+ private final class DirEntry(
+ container: AbstractFile,
+ name: String,
+ path: String
+ ) extends Entry(container, name, path)
{
-
val entries: Map[String, Entry] = new HashMap()
-
var entry: ZipEntry = _
override def isDirectory = true
@@ -198,19 +204,20 @@ final class ZipArchive(file: File, val archive: ZipFile) extends PlainFile(file)
override def iterator: Iterator[AbstractFile] = entries.valuesIterator
- override def lookupName(name: String, directory: Boolean): AbstractFile =
- entries.get(if (directory) name + "/" else name) match {
- case Some(dir) => dir
- case None => null
+ override def lookupName(name: String, directory: Boolean): AbstractFile = {
+ def slashName = if (directory) name + "/" else name
+ entries.getOrElse(slashName, null)
}
}
- //########################################################################
- // Private Class - FileEntry
-
/** A regular file archive entry */
- final class FileEntry(container : AbstractFile, name: String, path: String, val entry: ZipEntry)
- extends Entry(container, name, path) {
+ final class FileEntry(
+ container: AbstractFile,
+ name: String,
+ path: String,
+ val entry: ZipEntry
+ ) extends Entry(container, name, path)
+ {
def archive = ZipArchive.this.archive
override def lastModified: Long = entry.getTime()
override def input = archive.getInputStream(entry)
@@ -225,75 +232,18 @@ final class ZipArchive(file: File, val archive: ZipFile) extends PlainFile(file)
* @author Stephane Micheloud
* @version 1.0, 29/05/2007
*/
-final class URLZipArchive(url: URL) extends AbstractFile {
+final class URLZipArchive(url: URL) extends AbstractFile with ZipFileCommon {
assert(url ne null)
- private var root: DirEntry = _
-
- def container = throw new Error("unsupported")
-
- def name: String = url.getFile()
-
- def path: String = url.getPath()
-
- def file: File = null
-
- def absolute: AbstractFile = this
-
- def isDirectory: Boolean = true
-
- def lastModified: Long =
- try { url.openConnection().getLastModified() }
- catch { case _ => 0 }
-
- /** Does this abstract file denote an existing file? */
- def create {
- throw new UnsupportedOperationException
- }
-
- /** Delete the underlying file or directory (recursively). */
- def delete {
- throw new UnsupportedOperationException
- }
-
- def input: InputStream = url.openStream()
-
- def output = throw new Error("unsupported")
+ private lazy val root: DirEntry = {
+ val root = new DirEntry("<root>", "/")
- override def iterator: Iterator[AbstractFile] = {
- if (root eq null) load()
- root.iterator
- }
-
- override def lookupName(name: String, directory: Boolean): AbstractFile = {
- if (root eq null) load()
- root.lookupName(name, directory)
- }
-
- /** Returns an abstract file with the given name. It does not
- * check that it exists.
- */
- def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile =
- throw new UnsupportedOperationException()
-
- private def load() {
- def getEntryInputStream(in: InputStream): InputStream = {
- val buf = new scala.collection.mutable.ArrayBuffer[Byte]
- val data = new Array[Byte](1024)
- var n = in.read(data)
- while (n > 0) {
- buf.++=(data, 0, n)
- n = in.read(data)
- }
- new java.io.ByteArrayInputStream(buf.toArray)
- }
- this.root = new DirEntry("<root>", "/")
// A path to DirEntry map
val dirs: Map[String, DirEntry] = new HashMap()
- dirs.update("/", root)
+ dirs("/") = root
+
val zis = new ZipInputStream(input)
- var entry = zis.getNextEntry()
- while (entry ne null) {
+ zipEntryIterator(zis) foreach { case entry =>
val path = entry.getName()
assert(entry.isDirectory() == path.endsWith("/"),
this.toString() + " - " + path);
@@ -307,14 +257,43 @@ final class URLZipArchive(url: URL) extends AbstractFile {
val home = if (index < 0) "/" else path.substring(0, index + 1)
val parent: DirEntry = getDir(dirs, home)
assert(!parent.entries.contains(path), this.toString() + " - " + path)
- val in = getEntryInputStream(zis)
+ val in = byteInputStream(zis)
parent.entries.update(name, new FileEntry(name, path, entry, in))
}
zis.closeEntry()
- entry = zis.getNextEntry()
}
+ root
}
+ def container = throw new Error("unsupported")
+
+ def name: String = url.getFile()
+ def path: String = url.getPath()
+ def file: JFile = null
+ def absolute: AbstractFile = this
+ def isDirectory: Boolean = true
+
+ def lastModified: Long =
+ try url.openConnection().getLastModified()
+ catch { case _: IOException => 0 }
+
+ def create: Unit = throw new UnsupportedOperationException
+ def delete: Unit = throw new UnsupportedOperationException
+
+ def input: InputStream = url.openStream()
+ def output = throw new Error("unsupported")
+
+ override def iterator: Iterator[AbstractFile] = root.iterator
+
+ override def lookupName(name: String, directory: Boolean): AbstractFile =
+ root.lookupName(name, directory)
+
+ /** Returns an abstract file with the given name. It does not
+ * check that it exists.
+ */
+ def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile =
+ throw new UnsupportedOperationException()
+
private def getDir(dirs: Map[String, DirEntry], path: String): DirEntry =
dirs.get(path) match {
case Some(dir) => dir
@@ -330,16 +309,12 @@ final class URLZipArchive(url: URL) extends AbstractFile {
}
/** Superclass of archive entries */
- abstract class Entry(name: String, path: String)
- extends VirtualFile(name, path) {
- final override def path = URLZipArchive.this.toString() + "(" + super.path + ")"
- //final def getArchive = URLZipArchive.this.archive
+ abstract class Entry(name: String, path: String) extends VirtualFile(name, path) {
+ final override def path = "%s(%s)".format(URLZipArchive.this, super.path)
}
/** A directory archive entry */
- private final class DirEntry(name: String, path: String)
- extends Entry(name, path)
- {
+ private final class DirEntry(name: String, path: String) extends Entry(name, path) {
val entries: Map[String, Entry] = new HashMap()
var entry: ZipEntry = _