package codechicken.core;
import codechicken.core.launch.CodeChickenCorePlugin;
import codechicken.lib.asm.ASMHelper;
import net.minecraft.launchwrapper.Launch;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.ModClassLoader;
import net.minecraftforge.fml.common.ModContainer;
import org.objectweb.asm.tree.ClassNode;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class ClassDiscoverer {
public IStringMatcher matcher;
public String[] superclasses;
public ArrayList<Class<?>> classes;
public ModClassLoader modClassLoader;
public ClassDiscoverer(IStringMatcher matcher, Class<?>... superclasses) {
this.matcher = matcher;
this.superclasses = new String[superclasses.length];
for (int i = 0; i < superclasses.length; i++) {
this.superclasses[i] = superclasses[i].getName().replace('.', '/');
}
classes = new ArrayList<Class<?>>();
modClassLoader = (ModClassLoader) Loader.instance().getModClassLoader();
}
public ClassDiscoverer(Class<?>... superclasses) {
this(new IStringMatcher() {
public boolean matches(String test) {
return true;
}
}, superclasses);
}
public ArrayList<Class<?>> findClasses() {
try {
findClasspathMods();
} catch (Exception e) {
throw new RuntimeException(e);
}
return classes;
}
private void checkAddClass(String resource) {
try {
String classname = resource.replace(".class", "").replace("\\", ".").replace("/", ".");
byte[] bytes = Launch.classLoader.getClassBytes(classname);
if (bytes == null) {
return;
}
ClassNode cnode = ASMHelper.createClassNode(bytes);
for (String superclass : superclasses) {
if (!cnode.interfaces.contains(superclass) && !cnode.superName.equals(superclass)) {
return;
}
}
addClass(classname);
} catch (IOException e) {
CodeChickenCorePlugin.logger.error("Unable to load class: " + resource, e);
}
}
private void addClass(String classname) {
try {
Class<?> class1 = Class.forName(classname, true, modClassLoader);
classes.add(class1);
} catch (Throwable t) {
CodeChickenCorePlugin.logger.error("Unable to load class: " + classname, t);
}
}
private void findClasspathMods() {
List<ModContainer> mods = Loader.instance().getActiveModList();
HashSet<String> searched = new HashSet<String>();
for (ModContainer mod : mods) {
File source = mod.getSource();
if (source == null || searched.contains(source.getAbsolutePath())) {
continue;
}
searched.add(source.getAbsolutePath());
if (source.isFile()) {
CodeChickenCorePlugin.logger.debug("Found a mod container %s, examining for codechicken classes", source.getAbsolutePath());
try {
readFromZipFile(source);
} catch (Exception e) {
CodeChickenCorePlugin.logger.error("Failed to scan " + source.getAbsolutePath() + ", the zip file is invalid", e);
}
} else if (source.isDirectory()) {
CodeChickenCorePlugin.logger.debug("Found a minecraft related directory at %s, examining for codechicken classes", source.getAbsolutePath());
readFromDirectory(source, source);
}
}
}
private void readFromZipFile(File file) throws IOException {
FileInputStream fileinputstream = new FileInputStream(file);
ZipInputStream zipinputstream = new ZipInputStream(fileinputstream);
do {
ZipEntry zipentry = zipinputstream.getNextEntry();
if (zipentry == null) {
break;
}
String fullname = zipentry.getName().replace('\\', '/');
int pos = fullname.lastIndexOf('/');
String name = pos == -1 ? fullname : fullname.substring(pos + 1);
if (!zipentry.isDirectory() && matcher.matches(name)) {
checkAddClass(fullname);
}
} while (true);
fileinputstream.close();
}
private void readFromDirectory(File directory, File basedirectory) {
for (File child : directory.listFiles()) {
if (child.isDirectory()) {
readFromDirectory(child, basedirectory);
} else if (child.isFile() && matcher.matches(child.getName())) {
String fullname = CommonUtils.getRelativePath(basedirectory, child);
checkAddClass(fullname);
}
}
}
}