• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
16 package org.mockito.cglib.proxy;
17 
18 import java.lang.reflect.Constructor;
19 import java.lang.reflect.Modifier;
20 import java.util.*;
21 
22 import org.mockito.asm.ClassVisitor;
23 import 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  */
35 abstract 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 {
newInstance(int style, String[] classes, int[] route)45         public Object newInstance(int style, String[] classes, int[] route);
46     }
47 
newInstance(Object[] delegates)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      */
create(Object[] delegates)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      */
create(Class[] interfaces, Object[] delegates)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 
createBean(Object[] beans)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      */
createBean(ClassLoader loader,Object[] beans)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 
Generator()104         public Generator() {
105             super(SOURCE);
106         }
107 
getDefaultClassLoader()108         protected ClassLoader getDefaultClassLoader() {
109             return classes[0].getClassLoader(); // is this right?
110         }
111 
setStyle(int style)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 
setClasses(Class[] classes)124         public void setClasses(Class[] classes) {
125             this.classes = classes;
126         }
127 
setDelegates(Object[] delegates)128         public void setDelegates(Object[] delegates) {
129             this.delegates = delegates;
130         }
131 
create()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 
generateClass(ClassVisitor v)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 
firstInstance(Class type)182         protected Object firstInstance(Class type) {
183             return ((Mixin)ReflectUtils.newInstance(type)).newInstance(delegates);
184         }
185 
nextInstance(Object instance)186         protected Object nextInstance(Object instance) {
187             return ((Mixin)instance).newInstance(delegates);
188         }
189     }
190 
getClasses(Object[] delegates)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 
route(Object[] delegates)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 
Route(Object[] delegates)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