summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/io/SourceReader.scala6
-rw-r--r--src/compiler/scala/tools/nsc/io/ZipArchive.scala193
-rw-r--r--src/compiler/scala/tools/nsc/util/ClassPath.scala45
3 files changed, 134 insertions, 110 deletions
diff --git a/src/compiler/scala/tools/nsc/io/SourceReader.scala b/src/compiler/scala/tools/nsc/io/SourceReader.scala
index 37da2de83d..324c5e4111 100644
--- a/src/compiler/scala/tools/nsc/io/SourceReader.scala
+++ b/src/compiler/scala/tools/nsc/io/SourceReader.scala
@@ -49,9 +49,9 @@ class SourceReader(decoder: CharsetDecoder, reporter: Reporter) {
*/
def read(file: AbstractFile): Array[Char] = {
try file match {
- case p: PlainFile => read(p.file)
- case z: ZipArchive#FileEntry => read(Channels.newChannel(z.input))
- case _ => read(ByteBuffer.wrap(file.toByteArray))
+ case p: PlainFile => read(p.file)
+ case z: ZipArchive#Entry => read(Channels.newChannel(z.input))
+ case _ => read(ByteBuffer.wrap(file.toByteArray))
}
catch {
case e: Exception => reportEncodingError("" + file) ; Array()
diff --git a/src/compiler/scala/tools/nsc/io/ZipArchive.scala b/src/compiler/scala/tools/nsc/io/ZipArchive.scala
index 4fd72ba006..6e4998738e 100644
--- a/src/compiler/scala/tools/nsc/io/ZipArchive.scala
+++ b/src/compiler/scala/tools/nsc/io/ZipArchive.scala
@@ -10,6 +10,7 @@ import java.net.URL
import java.io.{ IOException, InputStream, ByteArrayInputStream }
import java.util.zip.{ ZipEntry, ZipFile, ZipInputStream }
import scala.collection.{ immutable, mutable }
+import annotation.tailrec
/** An abstraction for zip files and streams. Everything is written the way
* it is for performance: we come through here a lot on every run. Be careful
@@ -20,15 +21,15 @@ import scala.collection.{ immutable, mutable }
* @version 2.0,
*/
object ZipArchive {
- def fromPath(path: String): ZipArchive = fromFile(new JFile(path))
- def fromPath(path: Path): ZipArchive = fromFile(path.toFile)
+ def fromPath(path: String): FileZipArchive = fromFile(new JFile(path))
+ def fromPath(path: Path): FileZipArchive = fromFile(path.toFile)
/**
* @param file a File
* @return A ZipArchive if `file` is a readable zip file, otherwise null.
*/
- def fromFile(file: File): ZipArchive = fromFile(file.jfile)
- def fromFile(file: JFile): ZipArchive =
+ def fromFile(file: File): FileZipArchive = fromFile(file.jfile)
+ def fromFile(file: JFile): FileZipArchive =
try { new FileZipArchive(file) }
catch { case _: IOException => null }
@@ -36,45 +37,54 @@ object ZipArchive {
* @param url the url of a zip file
* @return A ZipArchive backed by the given url.
*/
- def fromURL(url: URL): ZipArchive = new URLZipArchive(url)
+ def fromURL(url: URL): URLZipArchive = new URLZipArchive(url)
+ def fromURL(url: String): URLZipArchive = fromURL(new URL(url))
+
+ private def dirName(path: String) = splitPath(path, true)
+ private def baseName(path: String) = splitPath(path, false)
+ private def splitPath(path0: String, front: Boolean): String = {
+ val isDir = path0.charAt(path0.length - 1) == '/'
+ val path = if (isDir) path0.substring(0, path0.length - 1) else path0
+ val idx = path.lastIndexOf('/')
+
+ if (idx < 0)
+ if (front) "/"
+ else path
+ else
+ if (front) path.substring(0, idx + 1)
+ else path.substring(idx + 1)
+ }
}
import ZipArchive._
abstract class ZipArchive(override val file: JFile) extends AbstractFile with Equals {
self =>
- // The root of this archive, populated lazily. Implemented by File and URL.
- def root: DirEntry
- // The underlying zip file, or null if that's not what underlies it.
- def zipFile: ZipFile
-
override def underlyingSource = Some(this)
- def lookupName(name: String, directory: Boolean) = root.lookupName(name, directory)
- def lookupNameUnchecked(name: String, directory: Boolean) = unsupported
- def iterator = root.iterator
def isDirectory = true
+ def lookupName(name: String, directory: Boolean) = unsupported
+ def lookupNameUnchecked(name: String, directory: Boolean) = unsupported
def create() = unsupported
def delete() = unsupported
def output = unsupported
+ def container = unsupported
+ def absolute = unsupported
- // Accumulated directories in this zip.
- // Violating my usual rule about vars holding mutable structures because
- // I want to make abundantly certain it can be collected.
- private var dirs: mutable.HashMap[String, DirEntry] = _
- protected def traverseAndClear(body: => Unit): DirEntry = {
- dirs = mutable.HashMap[String, DirEntry]("/" -> new DirEntry("/"))
- try { body ; dirs("/") }
- finally { dirs.clear() ; dirs = null }
+ private def walkIterator(its: Iterator[AbstractFile]): Iterator[AbstractFile] = {
+ its flatMap { f =>
+ if (f.isDirectory) walkIterator(f.iterator)
+ else Iterator(f)
+ }
}
+ def deepIterator = walkIterator(iterator)
- sealed abstract class Entry(override val container: DirEntry, path: String) extends VirtualFile(baseName(path), path) {
- // have to keep this apparently for compat with sbt's compiler-interface
- final def getArchive: ZipFile = self.zipFile
- final def addToParent() = container.entries(name) = this
+ sealed abstract class Entry(path: String) extends VirtualFile(baseName(path), path) {
+ // have to keep this name for compat with sbt's compiler-interface
+ def getArchive: ZipFile = null
override def underlyingSource = Some(self)
- override def toString = self + "(" + path + ")"
+ override def toString = self.path + "(" + path + ")"
}
- class DirEntry(override val path: String) extends Entry(getParent(path), path) {
+ class DirEntry(path: String) extends Entry(path) {
val entries = mutable.HashMap[String, Entry]()
override def isDirectory = true
@@ -84,45 +94,21 @@ abstract class ZipArchive(override val file: JFile) extends AbstractFile with Eq
else entries(name)
}
}
- class FileEntry(zipEntry: ZipEntry) extends Entry(getDir(zipEntry), zipEntry.getName) {
- lastModified = zipEntry.getTime()
-
- override def input = getArchive getInputStream zipEntry
- override def sizeOption = Some(zipEntry.getSize().toInt)
- }
-
- private def dirName(path: String) = splitPath(path, true)
- private def baseName(path: String) = splitPath(path, false)
- private def splitPath(path0: String, front: Boolean): String = {
- val isDir = path0.charAt(path0.length - 1) == '/'
- val path = if (isDir) path0.substring(0, path0.length - 1) else path0
- val idx = path.lastIndexOf('/')
-
- if (idx < 0)
- if (front) "/"
- else path
- else
- if (front) path.substring(0, idx + 1)
- else path.substring(idx + 1)
- }
- private def newDir(path: String, zipEntry: ZipEntry): DirEntry = {
+ private def newDir(dirs: mutable.Map[String, DirEntry], path: String, zipEntry: ZipEntry): DirEntry = {
+ val parent = {
+ val parentPath = dirName(path)
+ if (dirs contains parentPath) dirs(parentPath)
+ else newDir(dirs, parentPath, null)
+ }
val dir = new DirEntry(path)
if (zipEntry != null)
dir.lastModified = zipEntry.getTime()
- dir.addToParent()
+ parent.entries(baseName(path)) = dir
dirs(path) = dir
dir
}
- private def getParent(path: String): DirEntry = {
- if (path == "/") null
- else {
- val parentPath = dirName(path)
- if (dirs contains parentPath) dirs(parentPath)
- else newDir(parentPath, null)
- }
- }
- protected def getDir(entry: ZipEntry): DirEntry = {
+ protected def getDir(dirs: mutable.Map[String, DirEntry], entry: ZipEntry): DirEntry = {
val name = entry.getName
if (entry.isDirectory) {
if (dirs contains name) {
@@ -131,63 +117,104 @@ abstract class ZipArchive(override val file: JFile) extends AbstractFile with Eq
existing.lastModified = entry.getTime()
existing
}
- else newDir(name, entry)
+ else newDir(dirs, name, entry)
}
else {
val path = dirName(name)
if (dirs contains path) dirs(path)
- else newDir(path, null)
+ else newDir(dirs, path, null)
}
}
}
final class FileZipArchive(file: JFile) extends ZipArchive(file) {
- lazy val root = traverseAndClear(traverseZipFile(zipFile))
- def zipFile = new ZipFile(file)
+ def iterator = {
+ val zipFile = new ZipFile(file)
+ val root = new DirEntry("/")
+ val dirs = mutable.HashMap[String, DirEntry]("/" -> root)
+ val enum = zipFile.entries()
- def traverseZipFile(z: ZipFile) {
- val enum = z.entries()
while (enum.hasMoreElements) {
val zipEntry = enum.nextElement
- if (zipEntry.isDirectory) getDir(zipEntry)
- else new FileEntry(zipEntry).addToParent()
+ val dir = getDir(dirs, zipEntry)
+ if (zipEntry.isDirectory) dir
+ else {
+ class FileEntry() extends Entry(zipEntry.getName) {
+ override def getArchive = zipFile
+ override def input = getArchive getInputStream zipEntry
+ override def sizeOption = Some(zipEntry.getSize().toInt)
+ }
+ val f = new FileEntry()
+ f.lastModified = zipEntry.getTime()
+ dir.entries(f.name) = f
+ }
}
+
+ try root.iterator
+ finally dirs.clear()
}
- override def sizeOption = Some(file.length.toInt)
def name = file.getName
def path = file.getPath
def input = File(file).inputStream()
def lastModified = file.lastModified
- def absolute = if (file.isAbsolute) this else new FileZipArchive(file.getAbsoluteFile)
- def container = new PlainFile(file.getParent)
+ override def sizeOption = Some(file.length.toInt)
override def canEqual(other: Any) = other.isInstanceOf[FileZipArchive]
override def hashCode() = file.hashCode
override def equals(that: Any) = that match {
- case x: ZipArchive => file.getAbsoluteFile == x.file.getAbsoluteFile
- case _ => false
+ case x: FileZipArchive => file.getAbsoluteFile == x.file.getAbsoluteFile
+ case _ => false
}
}
final class URLZipArchive(val url: URL) extends ZipArchive(null) {
- lazy val root = traverseAndClear(traverseStream(input))
- def zipFile = null
-
- def traverseStream(input: InputStream) {
- val in = new ZipInputStream(new ByteArrayInputStream(Streamable.bytes(input)))
+ def iterator = {
+ val root = new DirEntry("/")
+ val dirs = mutable.HashMap[String, DirEntry]("/" -> root)
+ val in = new ZipInputStream(new ByteArrayInputStream(Streamable.bytes(input)))
- @annotation.tailrec def loop() {
+ @tailrec def loop() {
val zipEntry = in.getNextEntry()
+ class FileEntry() extends Entry(zipEntry.getName) {
+ override val toByteArray: Array[Byte] = {
+ val len = zipEntry.getSize().toInt
+ val arr = new Array[Byte](len)
+ var offset = 0
+
+ def loop() {
+ if (offset < len) {
+ val read = in.read(arr, offset, len - offset)
+ if (read >= 0) {
+ offset += read
+ loop()
+ }
+ }
+ }
+ loop()
+
+ if (offset == arr.length) arr
+ else throw new IOException("Input stream truncated: read %d of %d bytes".format(offset, len))
+ }
+ override def sizeOption = Some(zipEntry.getSize().toInt)
+ }
+
if (zipEntry != null) {
- if (zipEntry.isDirectory) getDir(zipEntry)
- else new FileEntry(zipEntry).addToParent()
+ val dir = getDir(dirs, zipEntry)
+ if (zipEntry.isDirectory)
+ dir
+ else {
+ val f = new FileEntry()
+ dir.entries(f.name) = f
+ }
in.closeEntry()
loop()
}
}
- try loop()
- finally in.close()
+
+ loop()
+ try root.iterator
+ finally dirs.clear()
}
def name = url.getFile()
@@ -196,8 +223,6 @@ final class URLZipArchive(val url: URL) extends ZipArchive(null) {
def lastModified =
try url.openConnection().getLastModified()
catch { case _: IOException => 0 }
- def absolute = this
- def container = unsupported
override def canEqual(other: Any) = other.isInstanceOf[URLZipArchive]
override def hashCode() = url.hashCode
diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala
index 25c09b6c15..2b3aa8696c 100644
--- a/src/compiler/scala/tools/nsc/util/ClassPath.scala
+++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala
@@ -307,16 +307,19 @@ class SourcePath[T](dir: AbstractFile, val context: ClassPathContext[T]) extends
def asClasspathString = dir.path
val sourcepaths: IndexedSeq[AbstractFile] = IndexedSeq(dir)
- lazy val classes: IndexedSeq[ClassRep] = dir flatMap { f =>
- if (f.isDirectory || !validSourceFile(f.name)) Nil
- else List(ClassRep(None, Some(f)))
- } toIndexedSeq
-
- lazy val packages: IndexedSeq[SourcePath[T]] = dir flatMap { f =>
- if (f.isDirectory && validPackage(f.name)) List(new SourcePath[T](f, context))
- else Nil
- } toIndexedSeq
+ private def traverse() = {
+ val classBuf = immutable.Vector.newBuilder[ClassRep]
+ val packageBuf = immutable.Vector.newBuilder[SourcePath[T]]
+ dir foreach { f =>
+ if (!f.isDirectory && validSourceFile(f.name))
+ classBuf += ClassRep(None, Some(f))
+ else if (f.isDirectory && validPackage(f.name))
+ packageBuf += new SourcePath[T](f, context)
+ }
+ (packageBuf.result, classBuf.result)
+ }
+ lazy val (packages, classes) = traverse()
override def toString() = "sourcepath: "+ dir.toString()
}
@@ -333,25 +336,21 @@ class DirectoryClassPath(val dir: AbstractFile, val context: ClassPathContext[Ab
def asClasspathString = dir.path
val sourcepaths: IndexedSeq[AbstractFile] = IndexedSeq()
- lazy val classes: IndexedSeq[ClassRep] = {
- val buf = immutable.Vector.newBuilder[ClassRep]
+ // calculates (packages, classes) in one traversal.
+ private def traverse() = {
+ val classBuf = immutable.Vector.newBuilder[ClassRep]
+ val packageBuf = immutable.Vector.newBuilder[DirectoryClassPath]
dir foreach { f =>
if (!f.isDirectory && validClassFile(f.name))
- buf += ClassRep(Some(f), None)
- }
- buf.result
- }
-
- lazy val packages: IndexedSeq[DirectoryClassPath] = {
- val buf = immutable.Vector.newBuilder[DirectoryClassPath]
- dir foreach { f =>
- if (f.isDirectory && validPackage(f.name))
- buf += new DirectoryClassPath(f, context)
+ classBuf += ClassRep(Some(f), None)
+ else if (f.isDirectory && validPackage(f.name))
+ packageBuf += new DirectoryClassPath(f, context)
}
- buf.result
+ (packageBuf.result, classBuf.result)
}
- override def toString() = "directory classpath: "+ dir
+ lazy val (packages, classes) = traverse()
+ override def toString() = "directory classpath: "+ origin.getOrElse("?")
}
/**