From 99a85272928ab170351fa1f36b5684ae6a6b4755 Mon Sep 17 00:00:00 2001 From: mihaylov Date: Wed, 12 Apr 2006 06:27:51 +0000 Subject: Moved some files from scala/tools/util/ to scal... Moved some files from scala/tools/util/ to scala/tools/nsc/io/ --- .../scala/tools/nsc/CompilationUnits.scala | 3 +- src/compiler/scala/tools/nsc/Global.scala | 2 +- src/compiler/scala/tools/nsc/Interpreter.scala | 2 +- .../scala/tools/nsc/backend/jvm/GenJVM.scala | 2 +- src/compiler/scala/tools/nsc/io/AbstractFile.scala | 123 +++++++++++++ .../scala/tools/nsc/io/AbstractFileReader.scala | 83 +++++++++ .../scala/tools/nsc/io/CharArrayFile.scala | 29 ++++ src/compiler/scala/tools/nsc/io/PlainFile.scala | 105 ++++++++++++ src/compiler/scala/tools/nsc/io/SourceReader.scala | 137 +++++++++++++++ src/compiler/scala/tools/nsc/io/VirtualFile.scala | 67 ++++++++ src/compiler/scala/tools/nsc/io/ZipArchive.scala | 190 +++++++++++++++++++++ .../scala/tools/nsc/symtab/SymbolLoaders.scala | 9 +- src/compiler/scala/tools/nsc/symtab/Symbols.scala | 2 +- .../nsc/symtab/classfile/ClassfileParser.scala | 7 +- .../nsc/symtab/classfile/SymblfileParser.scala | 5 +- src/compiler/scala/tools/nsc/util/ClassPath.scala | 3 +- src/compiler/scala/tools/nsc/util/SourceFile.scala | 5 +- 17 files changed, 754 insertions(+), 20 deletions(-) create mode 100644 src/compiler/scala/tools/nsc/io/AbstractFile.scala create mode 100644 src/compiler/scala/tools/nsc/io/AbstractFileReader.scala create mode 100644 src/compiler/scala/tools/nsc/io/CharArrayFile.scala create mode 100644 src/compiler/scala/tools/nsc/io/PlainFile.scala create mode 100644 src/compiler/scala/tools/nsc/io/SourceReader.scala create mode 100644 src/compiler/scala/tools/nsc/io/VirtualFile.scala create mode 100644 src/compiler/scala/tools/nsc/io/ZipArchive.scala (limited to 'src/compiler/scala/tools/nsc') diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala index a593538852..60799c3c37 100644 --- a/src/compiler/scala/tools/nsc/CompilationUnits.scala +++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala @@ -3,11 +3,12 @@ * @author Martin Odersky */ // $Id$ + package scala.tools.nsc; import scala.tools.nsc.util.{SourceFile, Position}; import scala.tools.nsc.util.FreshNameCreator; -import scala.tools.util.AbstractFile; +import scala.tools.nsc.io.AbstractFile; import scala.collection.mutable.HashSet; trait CompilationUnits requires Global { diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 7e0e81357a..6c9f0affb0 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -7,7 +7,7 @@ package scala.tools.nsc; import java.io._; import java.nio.charset._; -import scala.tools.util.{SourceReader,AbstractFile}; +import scala.tools.nsc.io.{SourceReader,AbstractFile}; import scala.tools.nsc.util.ClassPath; import scala.tools.nsc.util.{Position,SourceFile}; import scala.tools.nsc.reporters._; diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/compiler/scala/tools/nsc/Interpreter.scala index fd77e89c0d..d805b8fc95 100644 --- a/src/compiler/scala/tools/nsc/Interpreter.scala +++ b/src/compiler/scala/tools/nsc/Interpreter.scala @@ -8,7 +8,7 @@ package scala.tools.nsc import reporters._ import nsc.util.SourceFile -import scala.tools.util.PlainFile +import scala.tools.nsc.io.PlainFile import java.io._ import nsc.ast.parser.SyntaxAnalyzer import scala.collection.mutable.{ListBuffer, HashSet, ArrayBuffer} diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index 1a4611d1fd..3bff265aff 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -98,7 +98,7 @@ abstract class GenJVM extends SubComponent { addScalaAttr(if (isTopLevelModule(sym)) sym.sourceModule else sym); val outfile = getFile(jclass, ".class"); jclass.writeTo(outfile); - val file = scala.tools.util.AbstractFile.getFile(outfile); + val file = scala.tools.nsc.io.AbstractFile.getFile(outfile); informProgress("wrote " + outfile + " " + (if (file != null) "" + file.file + " " + file.file.exists() else "no file")); diff --git a/src/compiler/scala/tools/nsc/io/AbstractFile.scala b/src/compiler/scala/tools/nsc/io/AbstractFile.scala new file mode 100644 index 0000000000..521596350b --- /dev/null +++ b/src/compiler/scala/tools/nsc/io/AbstractFile.scala @@ -0,0 +1,123 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002-2006, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + + +package scala.tools.nsc.io; + + +import java.io.File; + +object AbstractFile { + + /** Returns "getFile(new File(path))". */ + def getFile(path: String): AbstractFile = getFile(new File(path)); + + /** + * If the specified File exists and is a regular file, returns an + * abstract regular file backed by it. Otherwise, returns null. + */ + def getFile(file: File): AbstractFile = + if (file.isFile() && file.exists()) new PlainFile(file) else null; + + + /** Returns "getDirectory(new File(path))". */ + def getDirectory(path: String): AbstractFile = getDirectory(new File(path)); + + /** + * if the specified File exists and is either a directory or a + * readable zip or jar archive, returns an abstract directory + * backed by it. Otherwise, returns null. + */ + def getDirectory(file: File): AbstractFile = { + if (file.isDirectory() && file.exists()) return new PlainFile(file); + if (file.isFile() && file.exists()) { + val path = file.getPath(); + if (path.endsWith(".jar") || path.endsWith(".zip")) + return ZipArchive.fromFile(file); + } + null + } + +} + +/** + * This class implements an abstract representation of files and + * directories. These files and directories may have some real counter + * part within the file system but that is not necessarily true. For + * example, there exist abstract files that represent files within a + * zip archive or files that exist only in memory. + * + * Every abstract file has a path (i.e. a full name) and a name + * (i.e. a short name) and may be backed by some real File. There are + * two different kinds of abstract files: regular files and + * directories. Regular files may be read and have a last modification + * time. Directories may list their content and look for subfiles with + * a specified name or path and of a specified kind. + */ +abstract class AbstractFile extends Object with Iterable[AbstractFile] { + + //######################################################################## + // Public Methods + + /** Returns the name of this abstract file. */ + def name: String; + + /** Returns the path of this abstract file. */ + def path: String; + + /** Returns the underlying File if any and null otherwise. */ + def file: File; + + /** Is this abstract file a directory? */ + def isDirectory: Boolean; + + /** Returns the time that this abstract file was last modified. */ + def lastModified: Long; + + /** Reads the content of this abstract file into a byte array. */ + def read: Array[Byte]; + + /** Returns all abstract subfiles of this abstract directory. */ + def elements: Iterator[AbstractFile]; + + /** + * 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; + + /** + * Returns the abstract file in this abstract directory with the + * specified path relative to it, If there is no such file, + * returns null. The argument "directory" tells whether to look + * for a directory or a regular file. + */ + def lookupPath(path: String, directory: Boolean): AbstractFile = { + val length = path.length(); + val separator = File.separatorChar; + assert(0 < length && path.lastIndexOf(separator) < length - 1, path); + var file = this; + var start = 0; + while (true) { + val index = path.indexOf(separator, start); + assert(index < 0 || start < index, path+" - "+start+" - "+index); + val name = path.substring(start, if (index < 0) length else index); + file = file.lookupName(name, if (index < 0) directory else true); + if (file == null || index < 0) return file; + start = index + 1; + } + file + } + + /** Returns the path of this abstract file. */ + override def toString() = path; + + //######################################################################## +} diff --git a/src/compiler/scala/tools/nsc/io/AbstractFileReader.scala b/src/compiler/scala/tools/nsc/io/AbstractFileReader.scala new file mode 100644 index 0000000000..0f0a65f54b --- /dev/null +++ b/src/compiler/scala/tools/nsc/io/AbstractFileReader.scala @@ -0,0 +1,83 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002-2006, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + + +package scala.tools.nsc.io; + + +class AbstractFileReader(val file: AbstractFile) { + + /** the buffer containing the file + */ + val buf: Array[Byte] = file.read; + + /** the current input pointer + */ + var bp: Int = 0; + + /** return byte at offset 'pos' + */ + def byteAt(pos: Int): Byte = return buf(pos); + + /** read a byte + */ + def nextByte: Byte = { + val b = buf(bp); + bp = bp + 1; + b + } + + /** read some bytes + */ + def nextBytes(len: Int): Array[Byte] = { + bp = bp + len; + buf.subArray(bp - len, bp); + } + + /** read a character + */ + def nextChar: Char = { + (((nextByte & 0xff) << 8) + (nextByte & 0xff)).toChar; + } + + /** read an integer + */ + def nextInt: Int = + ((nextByte & 0xff) << 24) + ((nextByte & 0xff) << 16) + + ((nextByte & 0xff) << 8) + (nextByte & 0xff); + + + /** extract a character at position bp from buf + */ + def getChar(mybp: Int): Char = + (((buf(mybp) & 0xff) << 8) + (buf(mybp+1) & 0xff)).toChar; + + /** extract an integer at position bp from buf + */ + def getInt(mybp: Int): Int = + ((buf(mybp ) & 0xff) << 24) + ((buf(mybp+1) & 0xff) << 16) + + ((buf(mybp+2) & 0xff) << 8) + (buf(mybp+3) & 0xff); + + /** extract a long integer at position bp from buf + */ + def getLong(mybp: Int): Long = + (getInt(mybp).toLong << 32) + (getInt(mybp + 4) & 0xffffffffL); + + /** extract a float at position bp from buf + */ + def getFloat(mybp: Int): Float = Float.intBitsToFloat(getInt(mybp)); + + /** extract a double at position bp from buf + */ + def getDouble(mybp: Int): Double = Double.longBitsToDouble(getLong(mybp)); + + /** skip next 'n' bytes + */ + def skip(n: Int): Unit = bp = bp + n; + +} diff --git a/src/compiler/scala/tools/nsc/io/CharArrayFile.scala b/src/compiler/scala/tools/nsc/io/CharArrayFile.scala new file mode 100644 index 0000000000..9288a44451 --- /dev/null +++ b/src/compiler/scala/tools/nsc/io/CharArrayFile.scala @@ -0,0 +1,29 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002-2006, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + + +package scala.tools.nsc.io; + + +/** + * This class implements an abstract regular file backed by a + * character array. + */ +class CharArrayFile(name: String, path: String, chars: Array[Char]) + extends VirtualFile(name, path) +{ + + def this(name: String, chars: Array[Char]) = this(name, name, chars); + + /** Reads the content of this abstract file into a byte array. */ + override def read: Array[Byte] = { + Predef.error("!!! not yet implemented"); + new String(chars).getBytes(); // !!! + } + +} diff --git a/src/compiler/scala/tools/nsc/io/PlainFile.scala b/src/compiler/scala/tools/nsc/io/PlainFile.scala new file mode 100644 index 0000000000..5ab83c447f --- /dev/null +++ b/src/compiler/scala/tools/nsc/io/PlainFile.scala @@ -0,0 +1,105 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002-2006, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + + +package scala.tools.nsc.io; + + +import java.io.{File, FileInputStream, IOException}; + +object PlainFile { + + /** Returns "fromFile(new File(path))". */ + def fromPath(path: String): AbstractFile = fromFile(new File(path)); + + /** + * If the specified File exists, returns an abstract file backed + * by it. Otherwise, returns null. + */ + def fromFile(file: File): AbstractFile = + if (file.exists()) new PlainFile(file) else null; + +} + +/** This class implements an abstract file backed by a File. */ +class PlainFile(val file: File) extends AbstractFile { + + assert(file != null); + assert(file.exists(), "non-existent file: " + file); + + //######################################################################## + // Public Methods + + /** Returns the name of this abstract file. */ + def name = file.getName(); + + /** Returns the path of this abstract file. */ + def path = file.getPath(); + + + override def hashCode(): Int = + try { file.getCanonicalPath().hashCode() } + catch { case _: IOException => 0 } + + override def equals(that: Any): Boolean = + try { + that.isInstanceOf[PlainFile] && + file.getCanonicalPath().equals(that.asInstanceOf[PlainFile].file.getCanonicalPath()); + } catch { + case _: IOException => + that.isInstanceOf[PlainFile] && + file.getAbsolutePath().equals(that.asInstanceOf[PlainFile].file.getAbsolutePath()); + } + + /** Is this abstract file a directory? */ + def isDirectory: Boolean = file.isDirectory(); + + /** Returns the time that this abstract file was last modified. */ + def lastModified: Long = file.lastModified(); + + /** Reads the content of this abstract file into a byte array. */ + def read: Array[Byte] = { + assert(!isDirectory, "cannot read directory '" + this + "'"); + val in = new FileInputStream(file); + var rest: Int = file.length().toInt; + val buf: Array[Byte] = new Array[Byte](rest); + while (rest > 0) { + val res = in.read(buf, buf.length - rest, rest); + if (res == -1) + throw new IOException("read error"); + rest = rest - res; + } + in.close(); + return buf; + } + + /** Returns all abstract subfiles of this abstract directory. */ + def elements: Iterator[AbstractFile] = { + assert(isDirectory, "not a directory '" + this + "'"); + val names: Array[String] = file.list(); + if (names == null || names.length == 0) Iterator.empty; + else Iterator.fromArray(names).map(name: String => new File(file, name)) + .filter(.exists()).map(file => new PlainFile(file)) + } + + /** + * 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 = { + //assert(isDirectory, "not a directory '" + this + "'"); + val child = new File(file, name); + if (!child.exists() || (directory != child.isDirectory) || + directory == child.isFile()) null; + else new PlainFile(child); + } + + //######################################################################## +} diff --git a/src/compiler/scala/tools/nsc/io/SourceReader.scala b/src/compiler/scala/tools/nsc/io/SourceReader.scala new file mode 100644 index 0000000000..f5be1da58a --- /dev/null +++ b/src/compiler/scala/tools/nsc/io/SourceReader.scala @@ -0,0 +1,137 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002-2006, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + + +package scala.tools.nsc.io; + + +import java.io.{File, FileInputStream, IOException}; +import java.nio.{ByteBuffer, CharBuffer}; +import java.nio.channels.{FileChannel, ReadableByteChannel}; +import java.nio.charset.{CharsetDecoder, CoderResult}; + +/** This class implements methods to read and decode source files. */ +class SourceReader(decoder: CharsetDecoder) { + + import SourceReader.{decode, flush} + + //######################################################################## + // Private Fields + + /** The input byte buffer (small enough to fit in cache) */ + private val bytes: ByteBuffer = ByteBuffer.allocate(0x4000); + + /** The output character buffer */ + private var chars: CharBuffer = CharBuffer.allocate(0x4000); + + //######################################################################## + // Public Methods + + /** Reads the file with the specified name. */ + def read(filename: String): Array[Char]= read(new File(filename)); + + /** Reads the specified file. */ + def read(file: File): Array[Char] = { + val channel: FileChannel = new FileInputStream(file).getChannel(); + try { + read(channel); + } finally { + channel.close(); + } + } + + /** Reads the specified file. */ + def read(file: AbstractFile): Array[Char] = { + val decoder: CharsetDecoder = this.decoder.reset(); + val bytes: ByteBuffer = ByteBuffer.wrap(file.read); + val chars: CharBuffer = this.chars; chars.clear(); + terminate(flush(decoder, decode(decoder, bytes, chars, true))); + } + + /** Reads the specified byte channel. */ + def read(input: ReadableByteChannel): Array[Char] = { + val decoder: CharsetDecoder = this.decoder.reset(); + val bytes: ByteBuffer = this.bytes; bytes.clear(); + var chars: CharBuffer = this.chars; chars.clear(); + var endOfInput: Boolean = false + while (!endOfInput ) { + endOfInput = input.read(bytes) < 0; + bytes.flip(); + chars = decode(decoder, bytes, chars, endOfInput); + } + terminate(flush(decoder, chars)); + } + + //######################################################################## + // Private Methods + + /** + * Sets the specified char buffer as the new output buffer and + * reads and returns its content. + */ + private def terminate(chars: CharBuffer): Array[Char] = { + val result = new Array[Char](chars.length()); + chars.get(result); + this.chars = chars; + result; + } + +} + +object SourceReader { + + /** + * Decodes the content of the specified byte buffer with the + * specified decoder into the specified char buffer, allocating + * bigger ones if necessary, then compacts the byte buffer and + * returns the last allocated char buffer. The "endOfInput" + * argument indicates whether the byte buffer contains the last + * chunk of the input file. + */ + def decode(decoder: CharsetDecoder, bytes: ByteBuffer, chars: CharBuffer, + endOfInput: boolean): CharBuffer = + { + val result: CoderResult = decoder.decode(bytes, chars, endOfInput); + if (result.isUnderflow()) { + bytes.compact(); + chars; + } else { + if (result.isError()) throw new IOException(result.toString()); + assert(result.isOverflow()); + decode(decoder, bytes, increaseCapacity(chars), endOfInput); + } + } + + /** + * Flushes the specified decoder into the specified char buffer, + * allocating bigger ones if necessary and then flips and returns + * the last allocated char buffer. + */ + def flush(decoder: CharsetDecoder, chars: CharBuffer): CharBuffer = { + val result: CoderResult = decoder.flush(chars); + if (result.isUnderflow()) { + chars.flip(); + chars; + } else { + if (result.isError()) throw new IOException(result.toString()); + assert(result.isOverflow()); + flush(decoder, increaseCapacity(chars)); + } + } + + /** + * Flips the specified buffer and returns a new one with the same + * content but with an increased capacity. + */ + private def increaseCapacity(buffer: CharBuffer): CharBuffer = { + buffer.flip(); + val capacity = 2 * buffer.capacity(); + CharBuffer.allocate(capacity).put(buffer); + } + +} diff --git a/src/compiler/scala/tools/nsc/io/VirtualFile.scala b/src/compiler/scala/tools/nsc/io/VirtualFile.scala new file mode 100644 index 0000000000..02ce3b0f81 --- /dev/null +++ b/src/compiler/scala/tools/nsc/io/VirtualFile.scala @@ -0,0 +1,67 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002-2006, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + + +package scala.tools.nsc.io; + + +import java.io.{File,IOException}; + +/** This class implements an empty abstract regular file. */ +class VirtualFile(val name: String, _path: String) extends AbstractFile { + + assert(name != null && path != null, name + " - " + path); + + //######################################################################## + // Public Constructors + + /** + * Initializes this instance with the specified name and an + * identical path. + */ + def this(name: String) = this(name, name); + + //######################################################################## + // Public Methods + + def path = _path; + + /** Returns null. */ + final def file: File = null; + + /** Is this abstract file a directory? */ + def isDirectory: Boolean = false; + + /** Returns the time that this abstract file was last modified. */ + def lastModified: Long = Long.MIN_VALUE; + + /** Reads the content of this abstract file into a byte array. */ + def read: Array[Byte] = { + assert(!isDirectory, "cannot read directory '" + this + "'"); + new Array[Byte](0); + } + + /** Returns all abstract subfiles of this abstract directory. */ + def elements: Iterator[AbstractFile] = { + assert(isDirectory, "not a directory '" + this + "'"); + 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 = { + assert(isDirectory, "not a directory '" + this + "'"); + null; + } + + //######################################################################## +} diff --git a/src/compiler/scala/tools/nsc/io/ZipArchive.scala b/src/compiler/scala/tools/nsc/io/ZipArchive.scala new file mode 100644 index 0000000000..84bc7752f8 --- /dev/null +++ b/src/compiler/scala/tools/nsc/io/ZipArchive.scala @@ -0,0 +1,190 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + + +package scala.tools.nsc.io; + + +import java.io.{File, IOException, InputStream}; +import java.util.Enumeration; +import java.util.zip.{ZipEntry, ZipFile}; +import scala.collection.mutable.{Map, HashMap} + +object ZipArchive { + + //######################################################################## + + /** Returns "fromFile(new File(path))". */ + def fromPath(path: String): AbstractFile = fromFile(new File(path)); + + /** + * If the specified File exists and is a readable zip archive, + * returns an abstract file backed by it. Otherwise, returns null. + */ + def fromFile(file: File): AbstractFile = + try { new ZipArchive(file, new ZipFile(file)) } + catch { case _: IOException => null } + + /** + * Returns an abstract directory backed by the specified archive. + */ + def fromArchive(archive: ZipFile): AbstractFile = + new ZipArchive(new File(archive.getName()), archive); +} + +/** + * This class implements an abstract directory backed by a zip + * archive. + */ +final class ZipArchive(file: File, val archive: ZipFile) extends PlainFile(file) { + + assert(archive != null) + //######################################################################## + // Private Fields + + /** The root directory or null if not yet initialized */ + private var root: DirEntry = _; + + //######################################################################## + // Public Methods + + /** Returns true. */ + override def isDirectory = true; + + /** Returns all abstract subfiles of this abstract directory. */ + override def elements: Iterator[AbstractFile] = { + if (root == null) load(); + root.elements; + } + + /** + * 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. + */ + override def lookupName(name: String, directory: Boolean): AbstractFile = { + if (root == null) load(); + root.lookupName(name, directory); + } + + //######################################################################## + // Private Methods + + /** Loads the archive and creates the root directory. */ + private def load(): Unit = { + this.root = new DirEntry("", "/"); + // 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); + assert(dir.entry == null, this.toString() + " - " + path); + 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); + assert(!parent.entries.contains(path), this.toString() + " - " + path); + parent.entries.update(name, new FileEntry(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(name.substring(0, name.length() - 1), path); + parent.entries.update(name, dir); + dirs.update(path, dir); + dir + } + } + + //######################################################################## + // Private Class - Entry + + /** Superclass of archive entries */ + abstract class Entry(name: String, path: String) + extends VirtualFile(name, path) + { + + final override def path = ZipArchive.this.toString() + "(" + super.path + ")"; + + } + + //######################################################################## + // Private Class - DirEntry + + /** A directory archive entry */ + private final class DirEntry(name: String, path: String) + extends Entry(name, path) + { + + val entries: Map[String,Entry] = new HashMap(); + + var entry: ZipEntry = _; + + override def isDirectory = true; + + override def lastModified: Long = + if (entry != null) entry.getTime() else super.lastModified; + + override def elements: Iterator[AbstractFile] = entries.values; + + override def lookupName(name: String, directory: Boolean): AbstractFile = + entries.get(if (directory) name + "/" else name) match { + case Some(dir) => dir + case None => null + } + } + + //######################################################################## + // Private Class - FileEntry + + /** A regular file archive entry */ + final class FileEntry(name: String, path: String, val entry: ZipEntry) + extends Entry(name, path) + { + + override def lastModified: Long = entry.getTime(); + + override def read: Array[Byte] = { + val in: InputStream = archive.getInputStream(entry); + var rest: Int = entry.getSize().toInt; + val buf = new Array[Byte](rest); + while (rest > 0) { + val res = in.read(buf, buf.length - rest, rest); + if (res == -1) + throw new IOException("read error"); + rest = rest - res; + } + in.close(); + buf + } + } + +} diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index bce1e2e202..0df3271bb8 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -3,13 +3,12 @@ * @author Martin Odersky */ // $Id$ + package scala.tools.nsc.symtab; -import java.io.IOException; -import java.io.File; -import scala.tools.nsc.util.Position; -import scala.tools.nsc.util.ClassPath; -import scala.tools.util.{AbstractFile}; +import java.io.{File, IOException}; +import scala.tools.nsc.util.{Position, ClassPath}; +import scala.tools.nsc.io.AbstractFile; import scala.tools.nsc.util.NameTransformer; import scala.collection.mutable.HashMap; import classfile.{ClassfileParser, SymblfileParser}; diff --git a/src/compiler/scala/tools/nsc/symtab/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala index e21ac25ff1..08df424779 100644 --- a/src/compiler/scala/tools/nsc/symtab/Symbols.scala +++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala @@ -6,7 +6,7 @@ package scala.tools.nsc.symtab; -import scala.tools.util.AbstractFile; +import scala.tools.nsc.io.AbstractFile; import scala.tools.nsc.util.{Position, SourceFile}; import Flags._; diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 8e8cadd21d..489502910c 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -2,7 +2,9 @@ * Copyright 2005 LAMP/EPFL * @author Martin Odersky */ + // $Id$ + /* Ideas to extend this to an icode reader: 1. Parse classfile a second time, creating a hashmap `code' that associates method symbols with code. @@ -12,12 +14,11 @@ code(new) = code(meth) */ + package scala.tools.nsc.symtab.classfile; import scala.tools.nsc.util.Position; -import scala.tools.util.AbstractFile; -import scala.tools.util.AbstractFileReader; - +import scala.tools.nsc.io.{AbstractFile, AbstractFileReader}; import scala.collection.mutable.ListBuffer; import scala.collection.immutable.{Map, ListMap}; diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/SymblfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/SymblfileParser.scala index 587940e6b8..83f296125f 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/SymblfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/SymblfileParser.scala @@ -3,11 +3,10 @@ * @author Martin Odersky */ // $Id$ -package scala.tools.nsc.symtab.classfile; -import scala.tools.util.{AbstractFile, AbstractFileReader}; +package scala.tools.nsc.symtab.classfile; -import java.io.IOException; +import scala.tools.nsc.io.{AbstractFile, AbstractFileReader}; abstract class SymblfileParser { diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala index 88e5050cbb..5b140f2411 100644 --- a/src/compiler/scala/tools/nsc/util/ClassPath.scala +++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala @@ -11,10 +11,9 @@ package scala.tools.nsc.util import scala.collection.mutable.ArrayBuffer -import scala.tools.util.AbstractFile +import scala.tools.nsc.io.AbstractFile import java.io.File -import java.io.FileNotFoundException import java.util.StringTokenizer /** Richer classpath abstraction than files. diff --git a/src/compiler/scala/tools/nsc/util/SourceFile.scala b/src/compiler/scala/tools/nsc/util/SourceFile.scala index 0c1bd9758c..cf0e71adfe 100644 --- a/src/compiler/scala/tools/nsc/util/SourceFile.scala +++ b/src/compiler/scala/tools/nsc/util/SourceFile.scala @@ -9,8 +9,9 @@ package scala.tools.nsc.util; -import scala.tools.util.AbstractFile; -import scala.tools.util.CharArrayFile; + + +import scala.tools.nsc.io.{AbstractFile, CharArrayFile}; /** Uses positions that are offsets rather than line/column pairs. * -- cgit v1.2.3