net.enilink.composition.asm.ExtendedClassNode.java Source code

Java tutorial

Introduction

Here is the source code for net.enilink.composition.asm.ExtendedClassNode.java

Source

/*******************************************************************************
 * Copyright (c) 2010 Fraunhofer IWU and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Fraunhofer IWU - initial API and implementation
 *******************************************************************************/
package net.enilink.composition.asm;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;
import net.enilink.composition.ClassDefiner;
import net.enilink.composition.asm.meta.ClassInfo;
import net.enilink.composition.asm.util.MethodNodeGenerator;

import com.google.inject.Inject;
import com.google.inject.Injector;

/**
 * Represents the mutable structure of a Java class.
 */
public abstract class ExtendedClassNode extends ClassNode {
    public static final String INJECTOR_FIELD = "_$injector";

    private MethodNodeGenerator clinitGen;
    private final List<MethodNode> constructors = new ArrayList<MethodNode>();
    private final Map<Object, ExtendedMethod> extendedMethods = new HashMap<Object, ExtendedMethod>();
    private Map<String, FieldNode> fieldIndex;
    private final Class<?> parentClass;

    private final Type parentType;

    private Type type;

    /**
     * Creates an {@link ExtendedClassNode}.
     * 
     * @param type
     *            Java type that gets defined by this class node.
     * @param parentClass
     *            The direct super class or the primary interface for this class
     *            node.
     * @param parentClassInfo
     *            Optional meta information for <code>parentClass</code>.
     */
    public ExtendedClassNode(Type type, Class<?> parentClass, ClassInfo parentClassInfo) {
        super(Opcodes.ASM5);
        String[] interfaces = new String[parentClass.getInterfaces().length];
        int i = 0;
        for (Class<?> face : parentClass.getInterfaces()) {
            interfaces[i++] = Type.getInternalName(face);
        }

        Class<?> superClass = parentClass.isInterface() ? Object.class : parentClass;
        visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, type.getInternalName(), null, Type.getInternalName(superClass),
                interfaces);

        if (parentClassInfo != null) {
            parentClassInfo.copyAnnotations(this);
        }

        this.type = type;
        this.parentClass = parentClass;
        parentType = Type.getType(parentClass);
    }

    @SuppressWarnings("unchecked")
    public ExtendedMethod addExtendedMethod(Method method, ClassDefiner definer) throws Exception {
        Object key = getKey(method);

        ExtendedMethod extendedMethod = extendedMethods.get(key);
        if (extendedMethod == null) {
            extendedMethod = new ExtendedMethod(this, method);

            MethodNode faceNode = AsmUtils.getClassInfo(method.getDeclaringClass().getName(), definer)
                    .getMethod(org.objectweb.asm.commons.Method.getMethod(method));
            faceNode.accept(extendedMethod);

            methods.add(extendedMethod);
            extendedMethods.put(key, extendedMethod);

            extendedMethod.access &= ~Opcodes.ACC_ABSTRACT;
        }

        return extendedMethod;
    }

    @SuppressWarnings("unchecked")
    public void addField(FieldNode field) {
        if (fieldIndex == null) {
            fieldIndex = new HashMap<String, FieldNode>();
        }
        fieldIndex.put(field.name, field);
        fields.add(field);
    }

    @SuppressWarnings("unchecked")
    public void addInjectorField() {
        FieldNode injectorField = new FieldNode(Opcodes.ACC_PRIVATE, INJECTOR_FIELD,
                Type.getDescriptor(Injector.class), null, null);
        injectorField.visitAnnotation(Type.getDescriptor(Inject.class), true);
        fields.add(injectorField);
    }

    @SuppressWarnings("unchecked")
    public void addInterface(String internalName) {
        if (!interfaces.contains(internalName)) {
            interfaces.add(internalName);
        }
    }

    public void endClass() {
        if (clinitGen != null) {
            clinitGen.returnValue();
            clinitGen.endMethod();
        }
    }

    @SuppressWarnings("unchecked")
    public MethodNodeGenerator getClassInitGen() {
        if (clinitGen == null) {
            MethodNode clinit = new MethodNode(Opcodes.ACC_STATIC, "<clinit>", "()V", null, null);
            methods.add(clinit);
            clinitGen = new MethodNodeGenerator(clinit);
        }
        return clinitGen;
    }

    public List<MethodNode> getConstructors() {
        return constructors;
    }

    public ExtendedMethod getExtendedMethod(Method method) {
        return extendedMethods.get(getKey(method));
    }

    public ExtendedMethod getExtendedMethod(Method method, ClassDefiner definer) throws Exception {
        return extendedMethods.get(getKey(method));
    }

    public Collection<ExtendedMethod> getExtendedMethods() {
        return extendedMethods.values();
    }

    public FieldNode getField(String name) {
        if (fieldIndex == null) {
            return null;
        }
        return fieldIndex.get(name);
    }

    private Object getKey(Method method) {
        return Arrays.asList(method.getReturnType(), method.getName(), Arrays.asList(method.getParameterTypes()));
    }

    public Class<?> getParentClass() {
        return parentClass;
    }

    public Type getParentType() {
        return parentType;
    }

    public Type getType() {
        return type;
    }
}