/******************************************************************************* * 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.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.objectweb.asm.Type; import net.enilink.composition.BehaviourFactory; import net.enilink.composition.ClassDefiner; import net.enilink.composition.exceptions.CompositionException; import net.enilink.composition.helpers.IPartialOrder; import net.enilink.composition.helpers.LinearExtension; import com.google.inject.Inject; /** * Abstract implementation for {@link BehaviourFactory}s that use * {@link BehaviourClassProcessor}s and {@link BehaviourMethodProcessor}s to * create concrete behaviour classes. */ public abstract class BehaviourFactoryBase implements BehaviourFactory { class MethodProcessorRunner { Class<?> behaviourClass; Collection<Method> methods; public MethodProcessorRunner(Class<?> behaviourClass) { this.behaviourClass = behaviourClass; this.methods = getMethods(); } private Collection<Method> getMethods() { List<Method> methods = new ArrayList<Method>(); methods.addAll(Arrays.asList(behaviourClass.getMethods())); HashMap<Object, Method> map = new HashMap<Object, Method>(); Map<Object, Method> pms = getProtectedMethods(behaviourClass, map); methods.addAll(pms.values()); return methods; } private Map<Object, Method> getProtectedMethods(Class<?> c, Map<Object, Method> methods) { if (c == null || c.isInterface()) { return methods; } for (Method m : c.getDeclaredMethods()) { if (Modifier.isProtected(m.getModifiers())) { Object key = Arrays.asList(m.getName(), Arrays.asList(m.getParameterTypes())); if (!methods.containsKey(key)) { methods.put(key, m); } } } return getProtectedMethods(c.getSuperclass(), methods); } public boolean implementsClass() { for (Method method : methods) { for (BehaviourMethodProcessor methodProcessor : methodProcessors) { if (methodProcessor .implementsMethod(behaviourClass, method)) { return true; } } } return false; } public void process(BehaviourClassNode classNode) throws Exception { Set<BehaviourMethodProcessor> initialized = new HashSet<BehaviourMethodProcessor>(); for (Method method : methods) { ExtendedMethod behaviourMethod = classNode .getExtendedMethod(method); for (BehaviourMethodProcessor methodProcessor : methodProcessors) { boolean implementsMethod = false; if (behaviourMethod == null && methodProcessor.implementsMethod( classNode.getParentClass(), method)) { behaviourMethod = classNode.addExtendedMethod(method, definer); implementsMethod = true; } if (behaviourMethod != null && methodProcessor.appliesTo(classNode, behaviourMethod)) { if (initialized.add(methodProcessor)) { methodProcessor.initialize(classNode); } methodProcessor.process(classNode, behaviourMethod); } else if (implementsMethod) { throw new CompositionException("Processor " + methodProcessor.getClass() + " pretended to implement method " + behaviourMethod.getOverriddenMethod() + " of class " + classNode.getType().getClassName() + " but was not applied."); } } } } } @Inject protected ClassDefiner definer; protected List<BehaviourClassProcessor> classProcessors; private List<BehaviourMethodProcessor> methodProcessors; private <T> boolean dependsOn(T element, T other) { DependsOn dependsOn = element.getClass().getAnnotation(DependsOn.class); if (dependsOn != null) { for (Class<?> type : dependsOn.value()) { if (type.isAssignableFrom(other.getClass())) { return true; } } } return false; } private <K, T> List<T> ensureList(Map<K, List<T>> map, K key) { List<T> list = map.get(key); if (list == null) { list = new ArrayList<T>(); map.put(key, list); } return list; } Class<?> extendBehaviourClass(String extendedClassName, Class<?> behaviourClass) throws Exception { boolean createBehaviour = !behaviourClass.isInterface(); if (!createBehaviour) { for (BehaviourClassProcessor classProcessor : classProcessors) { if (classProcessor.implementsClass(behaviourClass)) { createBehaviour = true; } } } MethodProcessorRunner runner = new MethodProcessorRunner(behaviourClass); if (createBehaviour | runner.implementsClass()) { BehaviourClassNode classNode = new BehaviourClassNode( Type.getObjectType(extendedClassName.replace('.', '/')), behaviourClass, AsmUtils.getClassInfo( behaviourClass.getName(), definer)); for (BehaviourClassProcessor classProcessor : classProcessors) { classProcessor.process(classNode); } runner.process(classNode); return AsmUtils.defineExtendedClass(definer, classNode); } return null; } private <T> List<T> filterAndSort(final Set<T> elements) { final Set<T> filteredElements = new HashSet<T>(elements); for (Iterator<T> it = filteredElements.iterator(); it.hasNext();) { UseWith useWith = it.next().getClass().getAnnotation(UseWith.class); if (useWith != null) { for (Class<?> type : useWith.value()) { if (!type.isAssignableFrom(getClass())) { it.remove(); } } } } final Map<T, List<T>> successors = new HashMap<T, List<T>>(); for (T element : filteredElements) { DependsOn dependsOn = element.getClass().getAnnotation( DependsOn.class); if (dependsOn != null) { for (Class<?> type : dependsOn.value()) { for (T other : filteredElements) { if (other != element && type.isAssignableFrom(other.getClass()) // remove circular dependencies && !dependsOn(other, element)) { ensureList(successors, other).add(element); } } } } } return LinearExtension.createLinearExtension(new IPartialOrder<T>() { @Override public Collection<T> getElements() { return filteredElements; } public Collection<T> getSuccessors(T element) { return successors.get(element); } }); } protected abstract String getExtendedClassName(Class<?> behaviourClass); public Collection<Class<?>> implement(Class<?> behaviourClass) throws Exception { // first check whether we did not already create and load the // extension of the given behaviour class String extendedClassName = getExtendedClassName(behaviourClass); Class<?> extendedClass = AsmUtils.findClass(extendedClassName, definer); if (extendedClass == null) { extendedClass = extendBehaviourClass(extendedClassName, behaviourClass); } return extendedClass != null ? Collections .<Class<?>> singleton(extendedClass) : Collections .<Class<?>> emptySet(); } @Inject public void setClassProcessors(Set<BehaviourClassProcessor> classProcessors) { this.classProcessors = filterAndSort(classProcessors); } @Inject public void setMethodProcessors( Set<BehaviourMethodProcessor> methodProcessors) { this.methodProcessors = filterAndSort(methodProcessors); } }