    001package cpw.mods.fml.common.asm.transformers;
    002
    003import java.util.Iterator;
    004import java.util.List;
    005
    006import org.objectweb.asm.ClassReader;
    007import org.objectweb.asm.ClassWriter;
    008import org.objectweb.asm.Type;
    009import org.objectweb.asm.tree.AnnotationNode;
    010import org.objectweb.asm.tree.ClassNode;
    011import org.objectweb.asm.tree.FieldNode;
    012import org.objectweb.asm.tree.MethodNode;
    013
    014import cpw.mods.fml.relauncher.FMLRelauncher;
    015import cpw.mods.fml.relauncher.IClassTransformer;
    016import cpw.mods.fml.relauncher.SideOnly;
    017
    018public class SideTransformer implements IClassTransformer
    019{
    020    private static String SIDE = FMLRelauncher.side();
    021    private static final boolean DEBUG = false;
    022    @SuppressWarnings("unchecked")
    023    @Override
    024    public byte[] transform(String name, byte[] bytes)
    025    {
    026        if (bytes == null) { return null; }
    027
    028        ClassNode classNode = new ClassNode();
    029        ClassReader classReader = new ClassReader(bytes);
    030        classReader.accept(classNode, 0);
    031
    032        if (remove((List<AnnotationNode>)classNode.visibleAnnotations, SIDE))
    033        {
    034            if (DEBUG)
    035            {
    036                System.out.println(String.format("Attempted to load class %s for invalid side %s", classNode.name, SIDE));
    037            }
    038            throw new RuntimeException(String.format("Attempted to load class %s for invalid side %s", classNode.name, SIDE));
    039        }
    040
    041        Iterator<FieldNode> fields = classNode.fields.iterator();
    042        while(fields.hasNext())
    043        {
    044            FieldNode field = fields.next();
    045            if (remove((List<AnnotationNode>)field.visibleAnnotations, SIDE))
    046            {
    047                if (DEBUG)
    048                {
    049                    System.out.println(String.format("Removing Field: %s.%s", classNode.name, field.name));
    050                }
    051                fields.remove();
    052            }
    053        }
    054        Iterator<MethodNode> methods = classNode.methods.iterator();
    055        while(methods.hasNext())
    056        {
    057            MethodNode method = methods.next();
    058            if (remove((List<AnnotationNode>)method.visibleAnnotations, SIDE))
    059            {
    060                if (DEBUG)
    061                {
    062                    System.out.println(String.format("Removing Method: %s.%s%s", classNode.name, method.name, method.desc));
    063                }
    064                methods.remove();
    065            }
    066        }
    067
    068        ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
    069        classNode.accept(writer);
    070        return writer.toByteArray();
    071    }
    072
    073    private boolean remove(List<AnnotationNode> anns, String side)
    074    {
    075        if (anns == null)
    076        {
    077            return false;
    078        }
    079        for (AnnotationNode ann : anns)
    080        {
    081            if (ann.desc.equals(Type.getDescriptor(SideOnly.class)))
    082            {
    083                if (ann.values != null)
    084                {
    085                    for (int x = 0; x < ann.values.size() - 1; x += 2)
    086                    {
    087                        Object key = ann.values.get(x);
    088                        Object value = ann.values.get(x+1);
    089                        if (key instanceof String && key.equals("value"))
    090                        {
    091                            if (value instanceof String[] )
    092                            {
    093                                if (!((String[])value)[1].equals(side))
    094                                {
    095                                    return true;
    096                                }
    097                            }
    098                        }
    099                    }
    100                }
    101            }
    102        }
    103        return false;
    104    }
    105}