// ex: se sts=4 sw=4 expandtab:
/*
* Yeti language compiler java class type reader.
*
* Copyright (c) 2007-2013 Madis Janson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package yeti.lang.compiler;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.util.Collections;
import java.util.Map;
import java.util.HashMap;
import java.util.Enumeration;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import yeti.renamed.asmx.ClassReader;
abstract class ClassPathItem {
abstract InputStream getStream(String name, long[] time) throws IOException;
abstract boolean exists(String name);
}
class ClassDir extends ClassPathItem {
String path;
ClassDir(String path) {
this.path = path;
}
InputStream getStream(String name, long[] time) throws IOException {
File f = new File(path, name);
InputStream r = new FileInputStream(f);
if (time != null)
time[0] = f.lastModified();
return r;
}
boolean exists(String name) {
return new File(path, name).isFile();
}
}
class ClassJar extends ClassPathItem {
JarFile jar;
Map entries = Collections.EMPTY_MAP;
ClassJar(String path) {
try {
jar = new JarFile(path);
Enumeration e = jar.entries();
entries = new HashMap();
while (e.hasMoreElements()) {
ZipEntry entry = (ZipEntry) e.nextElement();
String name = entry.getName();
if (name.endsWith(".class"))
entries.put(name, entry);
}
} catch (IOException ex) {
}
}
InputStream getStream(String name, long[] time) throws IOException {
ZipEntry entry = (ZipEntry) entries.get(name);
if (entry == null)
return null;
InputStream r = jar.getInputStream(entry);
if (time != null && (time[0] = entry.getTime()) < 0)
time[0] = 0; // unknown, should probably rebuild
return r;
}
boolean exists(String name) {
return entries.containsKey(name);
}
}
class ClassFinder {
private final ClassPathItem[] classPath;
private final ClassPathItem destDir;
private Map defined = new HashMap();
final Map parsed = new HashMap();
final Map existsCache = new HashMap();
final String pathStr;
ClassFinder(String cp) {
this(cp.split(File.pathSeparator), null);
}
ClassFinder(String[] cp, String depDestDir) {
classPath = new ClassPathItem[cp.length];
StringBuffer buf = new StringBuffer();
for (int i = 0; i < cp.length; ++i) {
classPath[i] = cp[i].endsWith(".jar")
? (ClassPathItem) new ClassJar(cp[i]) : new ClassDir(cp[i]);
if (i != 0)
buf.append(File.pathSeparator);
buf.append(cp[i]);
}
pathStr = buf.toString();
destDir = depDestDir == null ? null : new ClassDir(depDestDir);
}
public InputStream findClass(String name, long[] time) {
Object x = defined.get(name);
if (x != null && time != null) {
time[0] = 0; // unknown, should probably rebuild
return new ByteArrayInputStream((byte[]) x);
}
InputStream in;
for (int i = 0; i < classPath.length; ++i) {
try {
if ((in = classPath[i].getStream(name, time)) != null)
return in;
} catch (IOException ex) {
}
}
ClassLoader clc = Thread.currentThread().getContextClassLoader();
in = clc != null ? clc.getResourceAsStream(name) : null;
return in != null ? in :
getClass().getClassLoader().getResourceAsStream(name);
}
public void define(String name, byte[] content) {
defined.put(name, content);
}
boolean exists(String name) {
if (parsed.containsKey(name))
return true;
Boolean known = (Boolean) existsCache.get(name);
if (known != null)
return known.booleanValue();
String fn = name.concat(".class");
boolean found = false;
for (int i = 0; i < classPath.length; ++i)
if (classPath[i].exists(fn)) {
found = true;
break;
}
ClassLoader clc = null;
InputStream in;
if (!found) {
clc = Thread.currentThread().getContextClassLoader();
if (clc == null && name.startsWith("java"))
clc = ClassLoader.getSystemClassLoader();
}
if (clc != null && (in = clc.getResourceAsStream(fn)) != null) {
found = true;
try {
in.close();
} catch (Exception ex) {
}
}
existsCache.put(name, Boolean.valueOf(found));
return found;
}
JavaTypeReader readClass(String className) {
JavaTypeReader t = new JavaTypeReader();
t.className = className;
Object classNode = parsed.get(className);
if (classNode != null) {
JavaSource.loadClass(this, t, (JavaNode) classNode);
return t;
}
String classFile = className.concat(".class");
InputStream in = findClass(classFile, null);
if (in == null)
try {
if (destDir == null)
return null;
in = destDir.getStream(classFile, null);
} catch (IOException ex) {
return null;
}
try {
new ClassReader(in).accept(t, null,
ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES);
} catch (IOException ex) {
return null;
} catch (Exception ex) {
throw new RuntimeException("Internal error reading class " +
className + ": " + ex.getMessage(), ex);
}
return t;
}
}