summaryrefslogtreecommitdiff
path: root/src/reflect/scala/tools/nsc/io/AbstractFile.scala
diff options
context:
space:
mode:
authorEugene Burmako <xeno.by@gmail.com>2012-06-08 02:36:10 +0200
committerEugene Burmako <xeno.by@gmail.com>2012-06-08 15:32:28 +0200
commit0b2f1bcf75d31c59b25e19eebcb80f39c155365b (patch)
tree8d9dfc50ef01ca48c068b232af7e67a723325388 /src/reflect/scala/tools/nsc/io/AbstractFile.scala
parent13213e3df0384b1fd815c0798758a22284572cdb (diff)
downloadscala-0b2f1bcf75d31c59b25e19eebcb80f39c155365b.tar.gz
scala-0b2f1bcf75d31c59b25e19eebcb80f39c155365b.tar.bz2
scala-0b2f1bcf75d31c59b25e19eebcb80f39c155365b.zip
Introduces scala-reflect.jar
Diffstat (limited to 'src/reflect/scala/tools/nsc/io/AbstractFile.scala')
-rw-r--r--src/reflect/scala/tools/nsc/io/AbstractFile.scala258
1 files changed, 258 insertions, 0 deletions
diff --git a/src/reflect/scala/tools/nsc/io/AbstractFile.scala b/src/reflect/scala/tools/nsc/io/AbstractFile.scala
new file mode 100644
index 0000000000..fd56608fab
--- /dev/null
+++ b/src/reflect/scala/tools/nsc/io/AbstractFile.scala
@@ -0,0 +1,258 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2011 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+
+package scala.tools.nsc
+package io
+
+import java.io.{ FileOutputStream, IOException, InputStream, OutputStream, BufferedOutputStream }
+import java.io.{ File => JFile }
+import java.net.URL
+import scala.collection.mutable.ArrayBuffer
+
+/**
+ * @author Philippe Altherr
+ * @version 1.0, 23/03/2004
+ */
+object AbstractFile {
+ /** Returns "getFile(new File(path))". */
+ def getFile(path: String): AbstractFile = getFile(File(path))
+ def getFile(path: Path): AbstractFile = getFile(path.toFile)
+
+ /**
+ * If the specified File exists and is a regular file, returns an
+ * abstract regular file backed by it. Otherwise, returns <code>null</code>.
+ */
+ def getFile(file: File): AbstractFile =
+ if (file.isFile) new PlainFile(file) else null
+
+ /** Returns "getDirectory(new File(path))". */
+ def getDirectory(path: Path): AbstractFile = getDirectory(path.toFile)
+
+ /**
+ * 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 <code>null</code>.
+ *
+ * @param file ...
+ * @return ...
+ */
+ def getDirectory(file: File): AbstractFile =
+ if (file.isDirectory) new PlainFile(file)
+ else if (file.isFile && Path.isExtensionJarOrZip(file.jfile)) ZipArchive fromFile file
+ else null
+
+ /**
+ * If the specified URL exists and is a readable zip or jar archive,
+ * returns an abstract directory backed by it. Otherwise, returns
+ * <code>null</code>.
+ *
+ * @param file ...
+ * @return ...
+ */
+ def getURL(url: URL): AbstractFile = {
+ if (url == null || !Path.isExtensionJarOrZip(url.getPath)) null
+ else ZipArchive fromURL url
+ }
+}
+
+/**
+ * <p>
+ * This class and its children serve to unify handling of files and
+ * directories. These files and directories may or may not have some
+ * real counter part within the file system. For example, some file
+ * handles reference files within a zip archive or virtual ones
+ * that exist only in memory.
+ * </p>
+ * <p>
+ * 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.
+ * </p>
+ * <p>
+ * The interface does <b>not</b> allow to access the content.
+ * The class <code>symtab.classfile.AbstractFileReader</code> accesses
+ * bytes, knowing that the character set of classfiles is UTF-8. For
+ * all other cases, the class <code>SourceFile</code> is used, which honors
+ * <code>global.settings.encoding.value</code>.
+ * </p>
+ */
+abstract class AbstractFile extends reflect.internal.AbstractFileApi with Iterable[AbstractFile] {
+
+ /** Returns the name of this abstract file. */
+ def name: String
+
+ /** Returns the path of this abstract file. */
+ def path: String
+
+ /** Returns the path of this abstract file in a canonical form. */
+ def canonicalPath: String = if (file == null) path else file.getCanonicalPath
+
+ /** Checks extension case insensitively. */
+ def hasExtension(other: String) = extension == other.toLowerCase
+ private lazy val extension: String = Path.extension(name)
+
+ /** The absolute file, if this is a relative file. */
+ def absolute: AbstractFile
+
+ /** Returns the containing directory of this abstract file */
+ def container : AbstractFile
+
+ /** Returns the underlying File if any and null otherwise. */
+ def file: JFile
+
+ /** An underlying source, if known. Mostly, a zip/jar file. */
+ def underlyingSource: Option[AbstractFile] = None
+
+ /** Does this abstract file denote an existing file? */
+ def exists: Boolean = (file eq null) || file.exists
+
+ /** Does this abstract file represent something which can contain classfiles? */
+ def isClassContainer = isDirectory || (file != null && (extension == "jar" || extension == "zip"))
+
+ /** Create a file on disk, if one does not exist already. */
+ def create(): Unit
+
+ /** Delete the underlying file or directory (recursively). */
+ def delete(): Unit
+
+ /** Is this abstract file a directory? */
+ def isDirectory: Boolean
+
+ /** Returns the time that this abstract file was last modified. */
+ def lastModified: Long
+
+ /** returns an input stream so the file can be read */
+ def input: InputStream
+
+ /** Returns an output stream for writing the file */
+ def output: OutputStream
+
+ /** Returns a buffered output stream for writing the file - defaults to out */
+ def bufferedOutput: BufferedOutputStream = new BufferedOutputStream(output)
+
+ /** size of this file if it is a concrete file. */
+ def sizeOption: Option[Int] = None
+
+ def toURL: URL = if (file == null) null else file.toURI.toURL
+
+ /** Returns contents of file (if applicable) in a Char array.
+ * warning: use <code>Global.getSourceFile()</code> to use the proper
+ * encoding when converting to the char array.
+ */
+ @throws(classOf[IOException])
+ def toCharArray = new String(toByteArray).toCharArray
+
+ /** Returns contents of file (if applicable) in a byte array.
+ */
+ @throws(classOf[IOException])
+ def toByteArray: Array[Byte] = {
+ val in = input
+ var rest = sizeOption.getOrElse(0)
+ val arr = new Array[Byte](rest)
+ while (rest > 0) {
+ val res = in.read(arr, arr.length - rest, rest)
+ if (res == -1)
+ throw new IOException("read error")
+ rest -= res
+ }
+ in.close()
+ arr
+ }
+
+ /** Returns all abstract subfiles of this abstract directory. */
+ def iterator: Iterator[AbstractFile]
+
+ /** Returns the abstract file in this abstract directory with the specified
+ * name. If there is no such file, returns <code>null</code>. The argument
+ * <code>directory</code> tells whether to look for a directory or
+ * a regular file.
+ */
+ def lookupName(name: String, directory: Boolean): AbstractFile
+
+ /** Returns an abstract file with the given name. It does not
+ * check that it exists.
+ */
+ def lookupNameUnchecked(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
+ * <code>directory</code> tells whether to look for a directory or a regular
+ * file.
+ *
+ * @param path ...
+ * @param directory ...
+ * @return ...
+ */
+ def lookupPath(path: String, directory: Boolean): AbstractFile = {
+ lookup((f, p, dir) => f.lookupName(p, dir), path, directory)
+ }
+
+ /** Return an abstract file that does not check that `path` denotes
+ * an existing file.
+ */
+ def lookupPathUnchecked(path: String, directory: Boolean): AbstractFile = {
+ lookup((f, p, dir) => f.lookupNameUnchecked(p, dir), path, directory)
+ }
+
+ private def lookup(getFile: (AbstractFile, String, Boolean) => AbstractFile,
+ path0: String,
+ directory: Boolean): AbstractFile = {
+ val separator = java.io.File.separatorChar
+ // trim trailing '/'s
+ val path: String = if (path0.last == separator) path0 dropRight 1 else path0
+ val length = path.length()
+ assert(length > 0 && !(path.last == separator), path)
+ var file = this
+ var start = 0
+ while (true) {
+ val index = path.indexOf(separator, start)
+ assert(index < 0 || start < index, ((path, directory, start, index)))
+ val name = path.substring(start, if (index < 0) length else index)
+ file = getFile(file, name, if (index < 0) directory else true)
+ if ((file eq null) || index < 0) return file
+ start = index + 1
+ }
+ file
+ }
+
+ private def fileOrSubdirectoryNamed(name: String, isDir: Boolean): AbstractFile = {
+ val lookup = lookupName(name, isDir)
+ if (lookup != null) lookup
+ else {
+ val jfile = new JFile(file, name)
+ if (isDir) jfile.mkdirs() else jfile.createNewFile()
+ new PlainFile(jfile)
+ }
+ }
+
+ /**
+ * Get the file in this directory with the given name,
+ * creating an empty file if it does not already existing.
+ */
+ def fileNamed(name: String): AbstractFile = {
+ assert(isDirectory, "Tried to find '%s' in '%s' but it is not a directory".format(name, path))
+ fileOrSubdirectoryNamed(name, false)
+ }
+
+ /**
+ * Get the subdirectory with a given name, creating it if it
+ * does not already exist.
+ */
+ def subdirectoryNamed(name: String): AbstractFile = {
+ assert (isDirectory, "Tried to find '%s' in '%s' but it is not a directory".format(name, path))
+ fileOrSubdirectoryNamed(name, true)
+ }
+
+ protected def unsupported(): Nothing = unsupported(null)
+ protected def unsupported(msg: String): Nothing = throw new UnsupportedOperationException(msg)
+
+ /** Returns the path of this abstract file. */
+ override def toString() = path
+
+}