/* * Forge Mod Loader * Copyright (c) 2012-2013 cpw. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser Public License v2.1 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * * Contributors: * cpw - implementation */ package net.minecraftforge.fml.common.discovery.asm; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.LinkedList; import java.util.List; import net.minecraftforge.fml.common.FMLLog; import net.minecraftforge.fml.common.LoaderException; import net.minecraftforge.fml.common.discovery.ASMDataTable; import net.minecraftforge.fml.common.discovery.ModCandidate; import org.apache.logging.log4j.Level; import org.objectweb.asm.ClassReader; import org.objectweb.asm.Type; import com.google.common.base.Objects; import com.google.common.base.Strings; import com.google.common.collect.Lists; public class ASMModParser { private Type asmType; private int classVersion; private Type asmSuperType; private LinkedList<ModAnnotation> annotations = Lists.newLinkedList(); private String baseModProperties; static enum AnnotationType { CLASS, FIELD, METHOD, SUBTYPE; } public ASMModParser(InputStream stream) throws IOException { try { ClassReader reader = new ClassReader(stream); reader.accept(new ModClassVisitor(this), 0); } catch (Exception ex) { FMLLog.log(Level.ERROR, ex, "Unable to read a class file correctly"); throw new LoaderException(ex); } } public void beginNewTypeName(String typeQName, int classVersion, String superClassQName) { this.asmType = Type.getObjectType(typeQName); this.classVersion = classVersion; this.asmSuperType = !Strings.isNullOrEmpty(superClassQName) ? Type.getObjectType(superClassQName) : null; } public void startClassAnnotation(String annotationName) { ModAnnotation ann = new ModAnnotation(AnnotationType.CLASS, Type.getType(annotationName), this.asmType.getClassName()); annotations.addFirst(ann); } public void addAnnotationProperty(String key, Object value) { annotations.getFirst().addProperty(key, value); } public void startFieldAnnotation(String fieldName, String annotationName) { ModAnnotation ann = new ModAnnotation(AnnotationType.FIELD, Type.getType(annotationName), fieldName); annotations.addFirst(ann); } @Override public String toString() { return Objects.toStringHelper("ASMAnnotationDiscoverer") .add("className", asmType.getClassName()) .add("classVersion", classVersion) .add("superName", asmSuperType.getClassName()) .add("annotations", annotations) .add("isBaseMod", isBaseMod(Collections.<String>emptyList())) .add("baseModProperties", baseModProperties) .toString(); } public Type getASMType() { return asmType; } public int getClassVersion() { return classVersion; } public Type getASMSuperType() { return asmSuperType; } public LinkedList<ModAnnotation> getAnnotations() { return annotations; } public void validate() { // if (classVersion > 50.0) // { // // throw new LoaderException(new RuntimeException("Mod compiled for Java 7 detected")); // } } public boolean isBaseMod(List<String> rememberedTypes) { return getASMSuperType().equals(Type.getType("LBaseMod;")) || getASMSuperType().equals(Type.getType("Lnet/minecraft/src/BaseMod;"))|| rememberedTypes.contains(getASMSuperType().getClassName()); } public void setBaseModProperties(String foundProperties) { this.baseModProperties = foundProperties; } public String getBaseModProperties() { return this.baseModProperties; } public void sendToTable(ASMDataTable table, ModCandidate candidate) { for (ModAnnotation ma : annotations) { table.addASMData(candidate, ma.asmType.getClassName(), this.asmType.getClassName(), ma.member, ma.values); } } public void addAnnotationArray(String name) { annotations.getFirst().addArray(name); } public void addAnnotationEnumProperty(String name, String desc, String value) { annotations.getFirst().addEnumProperty(name, desc, value); } public void endArray() { annotations.getFirst().endArray(); } public void addSubAnnotation(String name, String desc) { ModAnnotation ma = annotations.getFirst(); annotations.addFirst(ma.addChildAnnotation(name, desc)); } public void endSubAnnotation() { // take the child and stick it at the end ModAnnotation child = annotations.removeFirst(); annotations.addLast(child); } public void startMethodAnnotation(String methodName, String methodDescriptor, String annotationName) { ModAnnotation ann = new ModAnnotation(AnnotationType.METHOD, Type.getType(annotationName), methodName+methodDescriptor); annotations.addFirst(ann); } }