001/*
002 * Forge Mod Loader
003 * Copyright (c) 2012-2013 cpw.
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser Public License v2.1
006 * which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
008 *
009 * Contributors:
010 *     cpw - implementation
011 */
012
013package cpw.mods.fml.common.discovery.asm;
014
015import java.io.IOException;
016import java.io.InputStream;
017import java.util.Collections;
018import java.util.LinkedList;
019import java.util.List;
020import java.util.logging.Level;
021
022import net.minecraft.src.BaseMod;
023
024import org.objectweb.asm.ClassReader;
025import org.objectweb.asm.Type;
026
027import com.google.common.base.Objects;
028import com.google.common.base.Strings;
029import com.google.common.collect.Lists;
030
031import cpw.mods.fml.common.FMLLog;
032import cpw.mods.fml.common.LoaderException;
033import cpw.mods.fml.common.discovery.ASMDataTable;
034import cpw.mods.fml.common.discovery.ModCandidate;
035
036public class ASMModParser
037{
038
039    private Type asmType;
040    private int classVersion;
041    private Type asmSuperType;
042    private LinkedList<ModAnnotation> annotations = Lists.newLinkedList();
043    private String baseModProperties;
044
045    static enum AnnotationType
046    {
047        CLASS, FIELD, METHOD, SUBTYPE;
048    }
049
050    public ASMModParser(InputStream stream) throws IOException
051    {
052        try
053        {
054            ClassReader reader = new ClassReader(stream);
055            reader.accept(new ModClassVisitor(this), 0);
056        }
057        catch (Exception ex)
058        {
059            FMLLog.log(Level.SEVERE, ex, "Unable to read a class file correctly");
060            throw new LoaderException(ex);
061        }
062    }
063
064    public void beginNewTypeName(String typeQName, int classVersion, String superClassQName)
065    {
066        this.asmType = Type.getObjectType(typeQName);
067        this.classVersion = classVersion;
068        this.asmSuperType = !Strings.isNullOrEmpty(superClassQName) ? Type.getObjectType(superClassQName) : null;
069    }
070
071    public void startClassAnnotation(String annotationName)
072    {
073        ModAnnotation ann = new ModAnnotation(AnnotationType.CLASS, Type.getType(annotationName), this.asmType.getClassName());
074        annotations.addFirst(ann);
075    }
076
077    public void addAnnotationProperty(String key, Object value)
078    {
079        annotations.getFirst().addProperty(key, value);
080    }
081
082    public void startFieldAnnotation(String fieldName, String annotationName)
083    {
084        ModAnnotation ann = new ModAnnotation(AnnotationType.FIELD, Type.getType(annotationName), fieldName);
085        annotations.addFirst(ann);
086    }
087
088    @Override
089    public String toString()
090    {
091        return Objects.toStringHelper("ASMAnnotationDiscoverer")
092                .add("className", asmType.getClassName())
093                .add("classVersion", classVersion)
094                .add("superName", asmSuperType.getClassName())
095                .add("annotations", annotations)
096                .add("isBaseMod", isBaseMod(Collections.<String>emptyList()))
097                .add("baseModProperties", baseModProperties)
098                .toString();
099    }
100
101    public Type getASMType()
102    {
103        return asmType;
104    }
105
106    public int getClassVersion()
107    {
108        return classVersion;
109    }
110
111    public Type getASMSuperType()
112    {
113        return asmSuperType;
114    }
115
116    public LinkedList<ModAnnotation> getAnnotations()
117    {
118        return annotations;
119    }
120
121    public void validate()
122    {
123//        if (classVersion > 50.0)
124//        {
125//
126//            throw new LoaderException(new RuntimeException("Mod compiled for Java 7 detected"));
127//        }
128    }
129
130    public boolean isBaseMod(List<String> rememberedTypes)
131    {
132        return getASMSuperType().equals(Type.getType("LBaseMod;")) || getASMSuperType().equals(Type.getType("Lnet/minecraft/src/BaseMod;"))|| rememberedTypes.contains(getASMSuperType().getClassName());
133    }
134
135    public void setBaseModProperties(String foundProperties)
136    {
137        this.baseModProperties = foundProperties;
138    }
139
140    public String getBaseModProperties()
141    {
142        return this.baseModProperties;
143    }
144
145    public void sendToTable(ASMDataTable table, ModCandidate candidate)
146    {
147        for (ModAnnotation ma : annotations)
148        {
149            table.addASMData(candidate, ma.asmType.getClassName(), this.asmType.getClassName(), ma.member, ma.values);
150        }
151    }
152
153    public void addAnnotationArray(String name)
154    {
155        annotations.getFirst().addArray(name);
156    }
157
158    public void addAnnotationEnumProperty(String name, String desc, String value)
159    {
160        annotations.getFirst().addEnumProperty(name, desc, value);
161
162    }
163
164    public void endArray()
165    {
166        annotations.getFirst().endArray();
167
168    }
169
170    public void addSubAnnotation(String name, String desc)
171    {
172        ModAnnotation ma = annotations.getFirst();
173        annotations.addFirst(ma.addChildAnnotation(name, desc));
174    }
175
176    public void endSubAnnotation()
177    {
178        // take the child and stick it at the end
179        ModAnnotation child = annotations.removeFirst();
180        annotations.addLast(child);
181    }
182}