summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/io/ZipArchive.scala
diff options
context:
space:
mode:
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 = _