1/*
2 * Copyright 2003 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.reflect;
17
18import java.lang.reflect.*;
19
20import org.mockito.asm.ClassVisitor;
21import org.mockito.asm.Type;
22import org.mockito.cglib.core.*;
23
24/**
25 * @author Chris Nokleberg
26 * @version $Id: ConstructorDelegate.java,v 1.20 2006/03/05 02:43:19 herbyderby Exp $
27 */
28abstract public class ConstructorDelegate {
29    private static final ConstructorKey KEY_FACTORY =
30      (ConstructorKey)KeyFactory.create(ConstructorKey.class, KeyFactory.CLASS_BY_NAME);
31
32    interface ConstructorKey {
33        public Object newInstance(String declaring, String iface);
34    }
35
36    protected ConstructorDelegate() {
37    }
38
39    public static ConstructorDelegate create(Class targetClass, Class iface) {
40        Generator gen = new Generator();
41        gen.setTargetClass(targetClass);
42        gen.setInterface(iface);
43        return gen.create();
44    }
45
46    public static class Generator extends AbstractClassGenerator {
47        private static final Source SOURCE = new Source(ConstructorDelegate.class.getName());
48        private static final Type CONSTRUCTOR_DELEGATE =
49          TypeUtils.parseType("org.mockito.cglib.reflect.ConstructorDelegate");
50
51        private Class iface;
52        private Class targetClass;
53
54        public Generator() {
55            super(SOURCE);
56        }
57
58        public void setInterface(Class iface) {
59            this.iface = iface;
60        }
61
62        public void setTargetClass(Class targetClass) {
63            this.targetClass = targetClass;
64        }
65
66        public ConstructorDelegate create() {
67            setNamePrefix(targetClass.getName());
68            Object key = KEY_FACTORY.newInstance(iface.getName(), targetClass.getName());
69            return (ConstructorDelegate)super.create(key);
70        }
71
72        protected ClassLoader getDefaultClassLoader() {
73            return targetClass.getClassLoader();
74        }
75
76        public void generateClass(ClassVisitor v) {
77            setNamePrefix(targetClass.getName());
78
79            final Method newInstance = ReflectUtils.findNewInstance(iface);
80            if (!newInstance.getReturnType().isAssignableFrom(targetClass)) {
81                throw new IllegalArgumentException("incompatible return type");
82            }
83            final Constructor constructor;
84            try {
85                constructor = targetClass.getDeclaredConstructor(newInstance.getParameterTypes());
86            } catch (NoSuchMethodException e) {
87                throw new IllegalArgumentException("interface does not match any known constructor");
88            }
89
90            ClassEmitter ce = new ClassEmitter(v);
91            ce.begin_class(Constants.V1_2,
92                           Constants.ACC_PUBLIC,
93                           getClassName(),
94                           CONSTRUCTOR_DELEGATE,
95                           new Type[]{ Type.getType(iface) },
96                           Constants.SOURCE_FILE);
97            Type declaring = Type.getType(constructor.getDeclaringClass());
98            EmitUtils.null_constructor(ce);
99            CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC,
100                                            ReflectUtils.getSignature(newInstance),
101                                            ReflectUtils.getExceptionTypes(newInstance));
102            e.new_instance(declaring);
103            e.dup();
104            e.load_args();
105            e.invoke_constructor(declaring, ReflectUtils.getSignature(constructor));
106            e.return_value();
107            e.end_method();
108            ce.end_class();
109        }
110
111        protected Object firstInstance(Class type) {
112            return ReflectUtils.newInstance(type);
113        }
114
115        protected Object nextInstance(Object instance) {
116            return instance;
117        }
118    }
119}
120