1674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen/*
2674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen * Copyright 2003,2004 The Apache Software Foundation
3674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen *
4674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen *  Licensed under the Apache License, Version 2.0 (the "License");
5674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen * you may not use this file except in compliance with the License.
6674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen * You may obtain a copy of the License at
7674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen *
8674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen *      http://www.apache.org/licenses/LICENSE-2.0
9674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen *
10674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen *  Unless required by applicable law or agreed to in writing, software
11674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen * distributed under the License is distributed on an "AS IS" BASIS,
12674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen * See the License for the specific language governing permissions and
14674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen * limitations under the License.
15674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen */
16674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogenpackage org.mockito.cglib.beans;
17674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
18674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogenimport java.beans.PropertyDescriptor;
19674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogenimport java.lang.reflect.*;
20674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
21674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogenimport org.mockito.asm.ClassVisitor;
22674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogenimport org.mockito.asm.Type;
23674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogenimport org.mockito.cglib.core.*;
24674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
25674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogenimport java.util.*;
26674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
27674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen/**
28674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen * @author Chris Nokleberg
29674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen */
30674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogenabstract public class BeanCopier
31674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen{
32674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    private static final BeanCopierKey KEY_FACTORY =
33674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen      (BeanCopierKey)KeyFactory.create(BeanCopierKey.class);
34674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    private static final Type CONVERTER =
35674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen      TypeUtils.parseType("org.mockito.cglib.core.Converter");
36674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    private static final Type BEAN_COPIER =
37674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen      TypeUtils.parseType("org.mockito.cglib.beans.BeanCopier");
38674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    private static final Signature COPY =
39674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen      new Signature("copy", Type.VOID_TYPE, new Type[]{ Constants.TYPE_OBJECT, Constants.TYPE_OBJECT, CONVERTER });
40674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    private static final Signature CONVERT =
41674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen      TypeUtils.parseSignature("Object convert(Object, Class, Object)");
42674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
43674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    interface BeanCopierKey {
44674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        public Object newInstance(String source, String target, boolean useConverter);
45674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
46674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
47674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public static BeanCopier create(Class source, Class target, boolean useConverter) {
48674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        Generator gen = new Generator();
49674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        gen.setSource(source);
50674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        gen.setTarget(target);
51674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        gen.setUseConverter(useConverter);
52674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        return gen.create();
53674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
54674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
55674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    abstract public void copy(Object from, Object to, Converter converter);
56674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
57674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public static class Generator extends AbstractClassGenerator {
58674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        private static final Source SOURCE = new Source(BeanCopier.class.getName());
59674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        private Class source;
60674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        private Class target;
61674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        private boolean useConverter;
62674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
63674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        public Generator() {
64674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            super(SOURCE);
65674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
66674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
67674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        public void setSource(Class source) {
68674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            if(!Modifier.isPublic(source.getModifiers())){
69674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen               setNamePrefix(source.getName());
70674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            }
71674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            this.source = source;
72674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
73674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
74674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        public void setTarget(Class target) {
75674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            if(!Modifier.isPublic(target.getModifiers())){
76674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen               setNamePrefix(target.getName());
77674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            }
78674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
79674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            this.target = target;
80674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
81674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
82674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        public void setUseConverter(boolean useConverter) {
83674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            this.useConverter = useConverter;
84674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
85674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
86674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        protected ClassLoader getDefaultClassLoader() {
87674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            return source.getClassLoader();
88674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
89674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
90674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        public BeanCopier create() {
91674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            Object key = KEY_FACTORY.newInstance(source.getName(), target.getName(), useConverter);
92674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            return (BeanCopier)super.create(key);
93674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
94674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
95674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        public void generateClass(ClassVisitor v) {
96674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            Type sourceType = Type.getType(source);
97674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            Type targetType = Type.getType(target);
98674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            ClassEmitter ce = new ClassEmitter(v);
99674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            ce.begin_class(Constants.V1_2,
100674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                           Constants.ACC_PUBLIC,
101674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                           getClassName(),
102674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                           BEAN_COPIER,
103674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                           null,
104674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                           Constants.SOURCE_FILE);
105674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
106674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            EmitUtils.null_constructor(ce);
107674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, COPY, null);
108674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            PropertyDescriptor[] getters = ReflectUtils.getBeanGetters(source);
109674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            PropertyDescriptor[] setters = ReflectUtils.getBeanGetters(target);
110674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
111674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            Map names = new HashMap();
112674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            for (int i = 0; i < getters.length; i++) {
113674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                names.put(getters[i].getName(), getters[i]);
114674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            }
115674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            Local targetLocal = e.make_local();
116674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            Local sourceLocal = e.make_local();
117674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            if (useConverter) {
118674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.load_arg(1);
119674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.checkcast(targetType);
120674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.store_local(targetLocal);
121674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.load_arg(0);
122674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.checkcast(sourceType);
123674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.store_local(sourceLocal);
124674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            } else {
125674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.load_arg(1);
126674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.checkcast(targetType);
127674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.load_arg(0);
128674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.checkcast(sourceType);
129674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            }
130674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            for (int i = 0; i < setters.length; i++) {
131674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                PropertyDescriptor setter = setters[i];
132674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                PropertyDescriptor getter = (PropertyDescriptor)names.get(setter.getName());
133674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                if (getter != null) {
134674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                    MethodInfo read = ReflectUtils.getMethodInfo(getter.getReadMethod());
135674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                    MethodInfo write = ReflectUtils.getMethodInfo(setter.getWriteMethod());
136674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                    if (useConverter) {
137674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                        Type setterType = write.getSignature().getArgumentTypes()[0];
138674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                        e.load_local(targetLocal);
139674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                        e.load_arg(2);
140674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                        e.load_local(sourceLocal);
141674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                        e.invoke(read);
142674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                        e.box(read.getSignature().getReturnType());
143674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                        EmitUtils.load_class(e, setterType);
144674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                        e.push(write.getSignature().getName());
145674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                        e.invoke_interface(CONVERTER, CONVERT);
146674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                        e.unbox_or_zero(setterType);
147674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                        e.invoke(write);
148674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                    } else if (compatible(getter, setter)) {
149674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                        e.dup2();
150674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                        e.invoke(read);
151674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                        e.invoke(write);
152674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                    }
153674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                }
154674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            }
155674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            e.return_value();
156674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            e.end_method();
157674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            ce.end_class();
158674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
159674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
160674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        private static boolean compatible(PropertyDescriptor getter, PropertyDescriptor setter) {
161674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            // TODO: allow automatic widening conversions?
162674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            return setter.getPropertyType().isAssignableFrom(getter.getPropertyType());
163674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
164674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
165674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        protected Object firstInstance(Class type) {
166674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            return ReflectUtils.newInstance(type);
167674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
168674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
169674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        protected Object nextInstance(Object instance) {
170674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            return instance;
171674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
172674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
173674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen}
174