From d227d486fdb94ef3bffcfe048a3069db3d9c14e1 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Mon, 31 Aug 2009 15:46:46 +0000 Subject: 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. --- src/compiler/scala/tools/nsc/io/ZipArchive.scala | 295 +++++++++++------------ 1 file changed, 135 insertions(+), 160 deletions(-) (limited to 'src/compiler/scala/tools/nsc/io/ZipArchive.scala') 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 file 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 null, 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, "", "/") - //######################################################################## - // 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, "", "/") - // 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("", "/") - 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("", "/") // 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 = _ -- cgit v1.2.3