Home | History | Annotate | Download | only in proxy
      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 {
     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