/* * Copyright (c) 2007 Mockito contributors * This program is made available under the terms of the MIT License. */ package org.powermock.api.mockito.repackaged.cglib.reflect; import org.powermock.api.mockito.repackaged.asm.ClassVisitor; import org.powermock.api.mockito.repackaged.asm.Type; import org.powermock.api.mockito.repackaged.cglib.core.AbstractClassGenerator; import org.powermock.api.mockito.repackaged.cglib.core.ClassEmitter; import org.powermock.api.mockito.repackaged.cglib.core.CodeEmitter; import org.powermock.api.mockito.repackaged.cglib.core.Constants; import org.powermock.api.mockito.repackaged.cglib.core.EmitUtils; import org.powermock.api.mockito.repackaged.cglib.core.Local; import org.powermock.api.mockito.repackaged.cglib.core.MethodInfo; import org.powermock.api.mockito.repackaged.cglib.core.ProcessArrayCallback; import org.powermock.api.mockito.repackaged.cglib.core.ReflectUtils; import org.powermock.api.mockito.repackaged.cglib.core.Signature; import org.powermock.api.mockito.repackaged.cglib.core.TypeUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.List; abstract public class MulticastDelegate implements Cloneable { protected Object[] targets = {}; protected MulticastDelegate() { } public static MulticastDelegate create(Class iface) { Generator gen = new Generator(); gen.setInterface(iface); return gen.create(); } public List getTargets() { return new ArrayList(Arrays.asList(targets)); } abstract public MulticastDelegate add(Object target); protected MulticastDelegate addHelper(Object target) { MulticastDelegate copy = newInstance(); copy.targets = new Object[targets.length + 1]; System.arraycopy(targets, 0, copy.targets, 0, targets.length); copy.targets[targets.length] = target; return copy; } public MulticastDelegate remove(Object target) { for (int i = targets.length - 1; i >= 0; i--) { if (targets[i].equals(target)) { MulticastDelegate copy = newInstance(); copy.targets = new Object[targets.length - 1]; System.arraycopy(targets, 0, copy.targets, 0, i); System.arraycopy(targets, i + 1, copy.targets, i, targets.length - i - 1); return copy; } } return this; } abstract public MulticastDelegate newInstance(); public static class Generator extends AbstractClassGenerator { private static final Source SOURCE = new Source(MulticastDelegate.class.getName()); private static final Type MULTICAST_DELEGATE = TypeUtils.parseType("org.powermock.api.mockito.repackaged.cglib.reflect.MulticastDelegate"); private static final Signature NEW_INSTANCE = new Signature("newInstance", MULTICAST_DELEGATE, new Type[0]); private static final Signature ADD_DELEGATE = new Signature("add", MULTICAST_DELEGATE, new Type[]{ Constants.TYPE_OBJECT }); private static final Signature ADD_HELPER = new Signature("addHelper", MULTICAST_DELEGATE, new Type[]{ Constants.TYPE_OBJECT }); private Class iface; public Generator() { super(SOURCE); } protected ClassLoader getDefaultClassLoader() { return iface.getClassLoader(); } public void setInterface(Class iface) { this.iface = iface; } public MulticastDelegate create() { setNamePrefix(MulticastDelegate.class.getName()); return (MulticastDelegate)super.create(iface.getName()); } public void generateClass(ClassVisitor cv) { final MethodInfo method = ReflectUtils.getMethodInfo(ReflectUtils.findInterfaceMethod(iface)); ClassEmitter ce = new ClassEmitter(cv); ce.begin_class(Constants.V1_2, Constants.ACC_PUBLIC, getClassName(), MULTICAST_DELEGATE, new Type[]{ Type.getType(iface) }, Constants.SOURCE_FILE); EmitUtils.null_constructor(ce); // generate proxied method emitProxy(ce, method); // newInstance CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, null); e.new_instance_this(); e.dup(); e.invoke_constructor_this(); e.return_value(); e.end_method(); // add e = ce.begin_method(Constants.ACC_PUBLIC, ADD_DELEGATE, null); e.load_this(); e.load_arg(0); e.checkcast(Type.getType(iface)); e.invoke_virtual_this(ADD_HELPER); e.return_value(); e.end_method(); ce.end_class(); } private void emitProxy(ClassEmitter ce, final MethodInfo method) { final CodeEmitter e = EmitUtils.begin_method(ce, method, Constants.ACC_PUBLIC); Type returnType = method.getSignature().getReturnType(); final boolean returns = returnType != Type.VOID_TYPE; Local result = null; if (returns) { result = e.make_local(returnType); e.zero_or_null(returnType); e.store_local(result); } e.load_this(); e.super_getfield("targets", Constants.TYPE_OBJECT_ARRAY); final Local result2 = result; EmitUtils.process_array(e, Constants.TYPE_OBJECT_ARRAY, new ProcessArrayCallback() { public void processElement(Type type) { e.checkcast(Type.getType(iface)); e.load_args(); e.invoke(method); if (returns) { e.store_local(result2); } } }); if (returns) { e.load_local(result); } e.return_value(); e.end_method(); } protected Object firstInstance(Class type) { // make a new instance in case first object is used with a long list of targets return ((MulticastDelegate)ReflectUtils.newInstance(type)).newInstance(); } protected Object nextInstance(Object instance) { return ((MulticastDelegate)instance).newInstance(); } } }