/******************************************************************************* * 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; } }