/* ____ ____ ____ ____ ______ *\
** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
** /_____/\____/\___/\____/____/ **
** **
** $Id$
\* */
package scalac.util;
import java.io.*;
import java.util.*;
import java.util.zip.*;
import java.util.jar.*;
public abstract class AbstractFile {
/** separator
*/
protected char separator = File.separatorChar;
/** table of all opened jar-files
*/
protected static Hashtable opened = new Hashtable();
/** get name of the file
*/
public abstract String getName();
/** get path of the file
*/
public abstract String getPath();
/** does the file exist?
*/
public abstract boolean exists();
/** is the file a directory?
*/
public abstract boolean isDirectory();
/** date file was last modified
*/
public abstract long lastModified();
/** read content of the file into a byte[] buffer
*/
public abstract byte[] read() throws IOException;
/** list contents of a directory
*/
public abstract String[] list() throws IOException;
/** open a new file
*/
public abstract AbstractFile open(String name);
/** return an input stream for the file
*/
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(read());
}
/** open file 'name' in directory 'dirname'
*/
public static AbstractFile open(String dirname, String name) {
AbstractFile res;
if (dirname == null)
res = new PlainFile(new File(name));
else if (dirname.endsWith(".zip")) {
AbstractFile dir = (AbstractFile)opened.get(dirname);
if (dir == null) {
dir = new ZipDir(new File(dirname));
if (dir.isDirectory())
opened.put(dirname, dir);
}
res = (name == null) ? dir : dir.open(name);
} else if (dirname.endsWith(".jar")) {
AbstractFile dir = (AbstractFile)opened.get(dirname);
if (dir == null) {
dir = new JarArchive(new File(dirname));
if (dir.isDirectory())
opened.put(dirname, dir);
}
res = (name == null) ? dir : dir.open(name);
} else if (name == null)
res = new PlainFile(new File(dirname));
else
res = new PlainFile(new File(dirname, name));
if (!res.exists())
res = null;
return res;
}
/** create file given by a fully qualified name from root directory `outdir';
* create intermediate directories if they do not exist already
*/
public static File create(File outdir, String name,
String suffix) throws IOException {
int start = 0;
int end = name.indexOf('.');
while (end >= start) {
outdir = new File(outdir, name.substring(start, end));
if (!outdir.exists())
outdir.mkdir();
start = end + 1;
end = name.indexOf('.', start);
}
return new File(outdir, name.substring(start) + suffix);
}
}
class PlainFile extends AbstractFile {
File f;
PlainFile(File f) {
this.f = f;
}
public String getName() {
return f.getName();
}
public String getPath() {
return f.getPath();
}
public boolean exists() {
return f.exists();
}
public boolean isDirectory() {
return f.isDirectory();
}
public long lastModified() {
return f.lastModified();
}
public byte[] read() throws IOException {
FileInputStream in = new FileInputStream(f);
int rest = (int)f.length();
byte[] buf = new byte[rest];
do {
int res = in.read(buf, buf.length - rest, rest);
if (res == -1)
throw new IOException("read error");
rest -= res;
} while (rest > 0);
in.close();
return buf;
}
public String[] list() throws IOException {
File[] fs = f.listFiles();
if (fs == null)
return new String[0];
String[] res = new String[fs.length];
for (int i = 0; i < fs.length; i++) {
res[i] = fs[i].getName();
if (fs[i].isDirectory() &&
!res[i].endsWith("/"))
res[i] = res[i] + "/";
}
return res;
}
public AbstractFile open(String name) {
return new PlainFile(new File(f, name));
}
}
class ZippedFile extends AbstractFile {
ZipDir dir;
ZipEntry zipEntry;
{
separator = '/';
}
ZippedFile(ZipDir dir, String name) {
this.dir = dir;
if (dir.zipFile != null) {
name = name.replace(File.separatorChar, separator);
zipEntry = this.dir.zipFile.getEntry(name);
if (zipEntry == null)
zipEntry = this.dir.zipFile.getEntry(name + separator);
}
}
public String getName() {
return zipEntry.getName();
}
public String getPath() {
return dir.getPath() + "(" + zipEntry.getName() + ")";
}
public boolean exists() {
return (zipEntry != null);
}
public boolean isDirectory() {
return zipEntry.isDirectory();
}
public long lastModified() {
return zipEntry.getTime();
}
public byte[] read() throws IOException {
InputStream in = dir.zipFile.getInputStream(zipEntry);
int rest = (int)zipEntry.getSize();
byte[] buf = new byte[rest];
do {
int res = in.read(buf, buf.length - rest, rest);
if (res == -1)
throw new IOException("read error");
rest -= res;
} while (rest > 0);
in.close();
return buf;
}
public String[] list() throws IOException {
if (!isDirectory())
throw new IOException("not a directory");
return dir.list(zipEntry.getName());
}
public AbstractFile open(String name) {
String pathname = zipEntry.getName();
return new ZippedFile(dir, pathname + name);
}
}
class ZipDir extends AbstractFile {
File f;
ZipFile zipFile;
{
separator = '/';
}
ZipDir(File f) {
this.f = f;
try {
zipFile = new ZipFile(f);
} catch (ZipException e) {
} catch (IOException e) {}
}
public String getName() {
return f.getName();
}
public String getPath() {
return f.getPath();
}
public boolean exists() {
return (zipFile != null);
}
public boolean isDirectory() {
return (zipFile != null);
}
public long lastModified() {
return -1;
}
public byte[] read() throws IOException {
throw new IOException("cannot read directory");
}
public String[] list(String prefix) {
int n = 0;
for (Enumeration enum = zipFile.entries(); enum.hasMoreElements();) {
ZipEntry e = (ZipEntry)enum.nextElement();
if (e.getName().startsWith(prefix)) {
String candidate = e.getName().substring(prefix.length());
if (candidate.indexOf(separator) < 0)
n++;
}
}
String[] filenames = new String[n];
n = 0;
for (Enumeration enum = zipFile.entries(); enum.hasMoreElements();) {
ZipEntry e = (ZipEntry)enum.nextElement();
if (e.getName().startsWith(prefix)) {
String candidate = e.getName().substring(prefix.length());
if (candidate.indexOf(separator) < 0)
filenames[n++] = candidate;
}
}
return filenames;
}
public String[] list() throws IOException {
return list("");
}
public AbstractFile open(String name) {
return new ZippedFile(this, name);
}
}
final class JarArchive extends AbstractFile {
File f;
JarFile jarFile;
HashMap entries;
public final static String[] EMPTY = new String[0];
JarArchive(File f) {
try {
jarFile = new JarFile(this.f = f);
}
catch (ZipException e) {}
catch (IOException e) {}
}
public String getName() {
return f.getName();
}
public String getPath() {
return f.getPath();
}
public boolean exists() {
return jarFile != null;
}
public boolean isDirectory() {
return jarFile != null;
}
public long lastModified() {
return -1;
}
public byte[] read() throws IOException {
throw new IOException("cannot read archive");
}
private void load() {
entries = new HashMap();
if (jarFile == null)
return;
Enumeration enum = jarFile.entries();
while (enum.hasMoreElements()) {
String candidate = ((JarEntry)enum.nextElement()).getName();
int i = candidate.indexOf('/');
int j = 0;
HashMap files = entries;
while (i >= 0) {
String dirname = candidate.substring(j, j = (i + 1));
JarDirEntry dir = (JarDirEntry)files.get(dirname);
if (dir == null)
files.put(dirname, dir = new JarDirEntry(
candidate.substring(0, j)));
files = dir.entries;
i = candidate.indexOf('/', j);
}
if (j < (candidate.length() - 1)) {
String filename = candidate.substring(j);
JarFileEntry file = (JarFileEntry)files.get(filename);
if (file == null)
files.put(filename, new JarFileEntry(candidate));
}
}
}
public String[] list(String prefix) {
prefix = prefix.replace(File.separatorChar, '/');
if (entries == null)
load();
int i = prefix.indexOf('/');
int j = 0;
HashMap files = entries;
while (i >= 0) {
String dirname = prefix.substring(j, j = (i + 1));
JarDirEntry dir = (JarDirEntry)files.get(dirname);
if (dir == null)
return EMPTY;
files = dir.entries;
i = prefix.indexOf('/', j);
}
if (j < (prefix.length() - 1)) {
String filename = prefix.substring(j);
return (files.get(filename) != null) ? new String[]{prefix}
: EMPTY;
} else
return (String[])files.keySet().toArray(new String[files.size()]);
}
public String[] list() throws IOException {
return list("");
}
public AbstractFile open(String name) {
if (entries == null)
load();
name = name.replace(File.separatorChar, '/');
int i = name.indexOf('/');
int j = 0;
int namelen = name.length();
HashMap files = entries;
while (i >= 0) {
String dirname = name.substring(j, j = (i + 1));
if (files != null) {
JarDirEntry dir = (JarDirEntry)files.get(dirname);
if (dir == null)
files = null;
else if (j == namelen)
return dir;
else
files = dir.entries;
}
i = name.indexOf('/', j);
}
if (j < (namelen - 1)) {
String filename = name.substring(j);
if (files == null)
return new NoJarFileEntry(name);
JarFileEntry file = (JarFileEntry)files.get(filename);
if (file == null)
return new NoJarFileEntry(name);
else
return file;
} else
return new NoJarDirEntry(name);
}
static class NoJarDirEntry extends AbstractFile {
String name;
NoJarDirEntry(String name) {
this.name = name;
}
public String getName() {
return name.substring(
name.lastIndexOf('/', name.length() - 2) + 1);
}
public String getPath() {
return name;
}
public String getFullName() {
return name;
}
public boolean exists() {
return false;
}
public boolean isDirectory() {
return true;
}
public long lastModified() {
return -1;
}
public String[] list() throws IOException {
throw new IOException("not a directory");
}
public byte[] read() throws IOException {
throw new IOException("cannot read archive");
}
public AbstractFile open(String fname) {
throw new Error("cannot open archive entry");
}
}
final class JarDirEntry extends NoJarDirEntry {
HashMap entries;
JarDirEntry(String name) {
super(name);
this.entries = new HashMap();
}
public String getPath() {
return JarArchive.this.getPath() + "(" + name + ")";
}
public boolean exists() {
return true;
}
public String[] list() throws IOException {
return JarArchive.this.list(name);
}
public AbstractFile open(String fname) {
fname = fname.replace(File.separatorChar, '/');
return JarArchive.this.open(name + fname);
}
}
static class NoJarFileEntry extends AbstractFile {
String name;
NoJarFileEntry(String name) {
this.name = name;
}
public String getName() {
return name.substring(
name.lastIndexOf('/', name.length() - 1) + 1);
}
public String getFullName() {
return name;
}
public String getPath() {
return name;
}
public boolean exists() {
return false;
}
public boolean isDirectory() {
return false;
}
public long lastModified() {
return -1;
}
public String[] list() throws IOException {
throw new IOException("not a directory");
}
public byte[] read() throws IOException {
throw new IOException("cannot read archive");
}
public AbstractFile open(String fname) {
throw new Error("not a directory");
}
}
final class JarFileEntry extends NoJarFileEntry {
JarFileEntry(String name) {
super(name);
}
public String getPath() {
return JarArchive.this.getPath() + "(" + name + ")";
}
public boolean exists() {
return true;
}
public long lastModified() {
return jarFile.getJarEntry(name).getTime();
}
public byte[] read() throws IOException {
JarEntry jarEntry = jarFile.getJarEntry(name);
if (jarEntry == null)
throw new IOException("unable to read " + name);
InputStream in = jarFile.getInputStream(jarEntry);
int rest = (int)jarEntry.getSize();
byte[] buf = new byte[rest];
do {
int res = in.read(buf, buf.length - rest, rest);
if (res == -1)
throw new IOException("read error");
rest -= res;
} while (rest > 0);
in.close();
return buf;
}
}
}