/*
* This file is part of the X10 project (http://x10-lang.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* This file was originally derived from the Polyglot extensible compiler framework.
*
* (C) Copyright 2000-2007 Polyglot project group, Cornell University
* (C) Copyright IBM Corporation 2007-2012.
*/
package polyglot.frontend;
import java.io.*;
import java.util.*;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import polyglot.main.Reporter;
import polyglot.util.FileUtil;
import polyglot.util.InternalCompilerError;
import polyglot.util.CollectionUtil;
import x10.util.CollectionFactory;
/**
* We implement our own class loader. All this pain is so
* we can define the classpath on the command line.
*/
public class ResourceLoader
{
/**
* Keep a cache of the zips and jars so we don't have to keep
* opening them from the file system.
*/
protected Map<File, Object> zipCache;
/**
* A cache of directories found in zip files.
*/
protected Set<String> dirCache;
/**
* Directory contents cache. Cache the first level of the directory
* so that we get less FileNotFoundExceptions
*/
protected Map<File, Set<String>> dirContentsCache;
protected static final Set<String> DOES_NOT_EXIST = new AbstractSet<String>() {
public Iterator<String> iterator() {
return new Iterator<String>() {
public boolean hasNext() { return false; }
public String next() { throw new NoSuchElementException(); }
public void remove() { throw new UnsupportedOperationException(); }
};
}
public int size() { return 0; }
public boolean contains(String s) { return false; }
};
/**
* Cache File.canRead()
*/
protected Map<File, Boolean> dirCanRead = CollectionFactory.newHashMap();
protected final static Object not_found = new Object();
protected Reporter reporter;
public ResourceLoader(Reporter reporter) {
this.zipCache = CollectionFactory.newHashMap();
this.dirContentsCache = CollectionFactory.newHashMap();
this.dirCache = CollectionFactory.newHashSet();
this.reporter = reporter;
}
private boolean canRead(File dir) {
Boolean res = dirCanRead.get(dir);
if (res==null) {
res = dir.canRead();
dirCanRead.put(dir,res);
}
return res;
}
/**
* Return true if the package name exists under the directory or file
* <code>dir</code>.
*/
public boolean dirExists(File dir, String name) {
if (reporter.should_report(Reporter.loader, 3)) {
reporter.report(3, "looking in " + dir + " for " +
name.replace('.', File.separatorChar));
}
if (!canRead(dir))
return false;
try {
if (dir.getName().endsWith(".jar") ||
dir.getName().endsWith(".zip")) {
if (dirCache.contains(name)) {
return true;
}
// load the zip file, forcing the package cache to be initialized
// with its contents.
loadZip(dir);
return dirCache.contains(name);
}
else {
File f = new File(dir, name);
return f.isDirectory() && FileUtil.checkNameFromRoot(dir, f);
}
}
catch (FileNotFoundException e) {
// ignore the exception.
}
catch (IOException e) {
throw new InternalCompilerError(e);
}
return false;
}
/**
* Try to find the file <code>name</code> in the directory or jar or zip
* file <code>dir</code>.
* If the file does not exist in the specified file/directory, then
* <code>null</code> is returned.
*/
public Resource loadResource(File dir, String name)
{
if (reporter.should_report(Reporter.loader, 3)) {
reporter.report(3, "looking in " + dir + " for " + name);
}
if (!canRead(dir))
return null;
try {
if (dir.getName().endsWith(".jar") ||
dir.getName().endsWith(".zip")) {
ZipFile zip = loadZip(dir);
return loadFromZip(dir, zip, name);
}
else {
return loadFromFile(name, dir);
}
}
catch (FileNotFoundException e) {
// ignore the exception.
}
catch (IOException e) {
throw new InternalCompilerError("Error while processing "+dir, e);
}
return null;
}
ZipFile loadZip(File dir) throws IOException {
Object o = zipCache.get(dir);
if (o != not_found) {
ZipFile zip = (ZipFile) o;
if (zip != null) {
return zip;
}
else {
// the zip is not in the cache.
// try to get it.
if (!dir.exists()) {
// record that the file does not exist,
zipCache.put(dir, not_found);
}
else {
// get the zip and put it in the cache.
if (reporter.should_report(Reporter.loader, 2))
reporter.report(2, "Opening zip " + dir);
if (dir.getName().endsWith(".jar")) {
zip = new JarFile(dir);
}
else {
zip = new ZipFile(dir);
}
zipCache.put(dir, zip);
// Load the package cache.
for (Enumeration<? extends ZipEntry> i = zip.entries(); i.hasMoreElements(); ) {
ZipEntry ei = i.nextElement();
String n = ei.getName();
int index = n.indexOf('/');
while (index >= 0) {
dirCache.add(n.substring(0, index));
index = n.indexOf('/', index+1);
}
}
return zip;
}
}
}
throw new FileNotFoundException(dir.getAbsolutePath());
}
Resource loadFromZip(File source, ZipFile zip, String fileName) throws IOException {
String entryName = fileName.replace(File.separatorChar, '/');
if (reporter.should_report(Reporter.loader, 2))
reporter.report(2, "Looking for " + entryName + " in " + zip.getName());
if (zip != null) {
ZipEntry entry = zip.getEntry(entryName);
if (entry != null) {
if (reporter.should_report(Reporter.loader, 3))
reporter.report(3, "found zip entry " + entry);
Resource c = new ZipResource(source, zip, entryName);
return c;
}
}
return null;
}
Resource loadFromFile(String name, File dir) throws IOException {
int sepIndex = name.indexOf('/');
if (sepIndex < 0) {
sepIndex = name.indexOf(File.separatorChar);
}
if (sepIndex > 0) {
String firstPart = name.substring(0, sepIndex);
Set<String> contents = dirContentsCache.get(dir);
if (contents != null && !contents.contains(firstPart)) {
return null;
}
File newDir = new File(dir, firstPart);
String newName = name.substring(sepIndex+1);
return loadFromFile(newName, newDir);
}
Set<String> dirContents = dirContentsCache.get(dir);
if (dirContents == null) {
if (dir.exists() && dir.isDirectory()) {
dirContents = CollectionFactory.newHashSet();
String[] contents = dir.list();
if (contents != null) {
for (int j = 0; j < contents.length; j++) {
dirContents.add(contents[j]);
}
}
} else {
dirContents = DOES_NOT_EXIST;
}
dirContentsCache.put(dir, dirContents);
}
if (dirContents == DOES_NOT_EXIST) {
return null;
}
String firstPart = name;
// check to see if the directory has the first part of the filename,
// to avoid trying to open the file if it doesn't
if (!dirContents.contains(firstPart)) {
return null;
}
// otherwise, try and open the thing.
File file = new File(dir, name);
if (! file.exists())
return null;
if (reporter.should_report(Reporter.loader, 3)) {
reporter.report(3, "found " + file);
reporter.report(3, "defining class " + name);
}
Resource c = new FileResource(file);
return c;
}
}