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