1/* 2 * Copyright 2003,2004 The Apache Software Foundation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package org.mockito.cglib.proxy; 17 18import java.lang.reflect.Constructor; 19import java.lang.reflect.Modifier; 20import java.util.*; 21 22import org.mockito.asm.ClassVisitor; 23import org.mockito.cglib.core.*; 24 25 26 27/** 28 * <code>Mixin</code> allows 29 * multiple objects to be combined into a single larger object. The 30 * methods in the generated object simply call the original methods in the 31 * underlying "delegate" objects. 32 * @author Chris Nokleberg 33 * @version $Id: Mixin.java,v 1.7 2005/09/27 11:42:27 baliuka Exp $ 34 */ 35abstract public class Mixin { 36 private static final MixinKey KEY_FACTORY = 37 (MixinKey)KeyFactory.create(MixinKey.class, KeyFactory.CLASS_BY_NAME); 38 private static final Map ROUTE_CACHE = Collections.synchronizedMap(new HashMap()); 39 40 public static final int STYLE_INTERFACES = 0; 41 public static final int STYLE_BEANS = 1; 42 public static final int STYLE_EVERYTHING = 2; 43 44 interface MixinKey { 45 public Object newInstance(int style, String[] classes, int[] route); 46 } 47 48 abstract public Mixin newInstance(Object[] delegates); 49 50 /** 51 * Helper method to create an interface mixin. For finer control over the 52 * generated instance, use a new instance of <code>Mixin</code> 53 * instead of this static method. 54 * TODO 55 */ 56 public static Mixin create(Object[] delegates) { 57 Generator gen = new Generator(); 58 gen.setDelegates(delegates); 59 return gen.create(); 60 } 61 62 /** 63 * Helper method to create an interface mixin. For finer control over the 64 * generated instance, use a new instance of <code>Mixin</code> 65 * instead of this static method. 66 * TODO 67 */ 68 public static Mixin create(Class[] interfaces, Object[] delegates) { 69 Generator gen = new Generator(); 70 gen.setClasses(interfaces); 71 gen.setDelegates(delegates); 72 return gen.create(); 73 } 74 75 76 public static Mixin createBean(Object[] beans) { 77 78 return createBean(null, beans); 79 80 } 81 /** 82 * Helper method to create a bean mixin. For finer control over the 83 * generated instance, use a new instance of <code>Mixin</code> 84 * instead of this static method. 85 * TODO 86 */ 87 public static Mixin createBean(ClassLoader loader,Object[] beans) { 88 Generator gen = new Generator(); 89 gen.setStyle(STYLE_BEANS); 90 gen.setDelegates(beans); 91 gen.setClassLoader(loader); 92 return gen.create(); 93 } 94 95 public static class Generator extends AbstractClassGenerator { 96 private static final Source SOURCE = new Source(Mixin.class.getName()); 97 98 private Class[] classes; 99 private Object[] delegates; 100 private int style = STYLE_INTERFACES; 101 102 private int[] route; 103 104 public Generator() { 105 super(SOURCE); 106 } 107 108 protected ClassLoader getDefaultClassLoader() { 109 return classes[0].getClassLoader(); // is this right? 110 } 111 112 public void setStyle(int style) { 113 switch (style) { 114 case STYLE_INTERFACES: 115 case STYLE_BEANS: 116 case STYLE_EVERYTHING: 117 this.style = style; 118 break; 119 default: 120 throw new IllegalArgumentException("Unknown mixin style: " + style); 121 } 122 } 123 124 public void setClasses(Class[] classes) { 125 this.classes = classes; 126 } 127 128 public void setDelegates(Object[] delegates) { 129 this.delegates = delegates; 130 } 131 132 public Mixin create() { 133 if (classes == null && delegates == null) { 134 throw new IllegalStateException("Either classes or delegates must be set"); 135 } 136 switch (style) { 137 case STYLE_INTERFACES: 138 if (classes == null) { 139 Route r = route(delegates); 140 classes = r.classes; 141 route = r.route; 142 } 143 break; 144 case STYLE_BEANS: 145 // fall-through 146 case STYLE_EVERYTHING: 147 if (classes == null) { 148 classes = ReflectUtils.getClasses(delegates); 149 } else { 150 if (delegates != null) { 151 Class[] temp = ReflectUtils.getClasses(delegates); 152 if (classes.length != temp.length) { 153 throw new IllegalStateException("Specified classes are incompatible with delegates"); 154 } 155 for (int i = 0; i < classes.length; i++) { 156 if (!classes[i].isAssignableFrom(temp[i])) { 157 throw new IllegalStateException("Specified class " + classes[i] + " is incompatible with delegate class " + temp[i] + " (index " + i + ")"); 158 } 159 } 160 } 161 } 162 } 163 setNamePrefix(classes[ReflectUtils.findPackageProtected(classes)].getName()); 164 165 return (Mixin)super.create(KEY_FACTORY.newInstance(style, ReflectUtils.getNames( classes ), route)); 166 } 167 168 public void generateClass(ClassVisitor v) { 169 switch (style) { 170 case STYLE_INTERFACES: 171 new MixinEmitter(v, getClassName(), classes, route); 172 break; 173 case STYLE_BEANS: 174 new MixinBeanEmitter(v, getClassName(), classes); 175 break; 176 case STYLE_EVERYTHING: 177 new MixinEverythingEmitter(v, getClassName(), classes); 178 break; 179 } 180 } 181 182 protected Object firstInstance(Class type) { 183 return ((Mixin)ReflectUtils.newInstance(type)).newInstance(delegates); 184 } 185 186 protected Object nextInstance(Object instance) { 187 return ((Mixin)instance).newInstance(delegates); 188 } 189 } 190 191 public static Class[] getClasses(Object[] delegates) { 192 return (Class[])route(delegates).classes.clone(); 193 } 194 195// public static int[] getRoute(Object[] delegates) { 196// return (int[])route(delegates).route.clone(); 197// } 198 199 private static Route route(Object[] delegates) { 200 Object key = ClassesKey.create(delegates); 201 Route route = (Route)ROUTE_CACHE.get(key); 202 if (route == null) { 203 ROUTE_CACHE.put(key, route = new Route(delegates)); 204 } 205 return route; 206 } 207 208 private static class Route 209 { 210 private Class[] classes; 211 private int[] route; 212 213 Route(Object[] delegates) { 214 Map map = new HashMap(); 215 ArrayList collect = new ArrayList(); 216 for (int i = 0; i < delegates.length; i++) { 217 Class delegate = delegates[i].getClass(); 218 collect.clear(); 219 ReflectUtils.addAllInterfaces(delegate, collect); 220 for (Iterator it = collect.iterator(); it.hasNext();) { 221 Class iface = (Class)it.next(); 222 if (!map.containsKey(iface)) { 223 map.put(iface, new Integer(i)); 224 } 225 } 226 } 227 classes = new Class[map.size()]; 228 route = new int[map.size()]; 229 int index = 0; 230 for (Iterator it = map.keySet().iterator(); it.hasNext();) { 231 Class key = (Class)it.next(); 232 classes[index] = key; 233 route[index] = ((Integer)map.get(key)).intValue(); 234 index++; 235 } 236 } 237 } 238} 239