BeanCopier.java revision 674060f01e9090cd21b3c5656cc3204912ad17a6
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.beans;
17
18import java.beans.PropertyDescriptor;
19import java.lang.reflect.*;
20
21import org.mockito.asm.ClassVisitor;
22import org.mockito.asm.Type;
23import org.mockito.cglib.core.*;
24
25import java.util.*;
26
27/**
28 * @author Chris Nokleberg
29 */
30abstract public class BeanCopier
31{
32    private static final BeanCopierKey KEY_FACTORY =
33      (BeanCopierKey)KeyFactory.create(BeanCopierKey.class);
34    private static final Type CONVERTER =
35      TypeUtils.parseType("org.mockito.cglib.core.Converter");
36    private static final Type BEAN_COPIER =
37      TypeUtils.parseType("org.mockito.cglib.beans.BeanCopier");
38    private static final Signature COPY =
39      new Signature("copy", Type.VOID_TYPE, new Type[]{ Constants.TYPE_OBJECT, Constants.TYPE_OBJECT, CONVERTER });
40    private static final Signature CONVERT =
41      TypeUtils.parseSignature("Object convert(Object, Class, Object)");
42
43    interface BeanCopierKey {
44        public Object newInstance(String source, String target, boolean useConverter);
45    }
46
47    public static BeanCopier create(Class source, Class target, boolean useConverter) {
48        Generator gen = new Generator();
49        gen.setSource(source);
50        gen.setTarget(target);
51        gen.setUseConverter(useConverter);
52        return gen.create();
53    }
54
55    abstract public void copy(Object from, Object to, Converter converter);
56
57    public static class Generator extends AbstractClassGenerator {
58        private static final Source SOURCE = new Source(BeanCopier.class.getName());
59        private Class source;
60        private Class target;
61        private boolean useConverter;
62
63        public Generator() {
64            super(SOURCE);
65        }
66
67        public void setSource(Class source) {
68            if(!Modifier.isPublic(source.getModifiers())){
69               setNamePrefix(source.getName());
70            }
71            this.source = source;
72        }
73
74        public void setTarget(Class target) {
75            if(!Modifier.isPublic(target.getModifiers())){
76               setNamePrefix(target.getName());
77            }
78
79            this.target = target;
80        }
81
82        public void setUseConverter(boolean useConverter) {
83            this.useConverter = useConverter;
84        }
85
86        protected ClassLoader getDefaultClassLoader() {
87            return source.getClassLoader();
88        }
89
90        public BeanCopier create() {
91            Object key = KEY_FACTORY.newInstance(source.getName(), target.getName(), useConverter);
92            return (BeanCopier)super.create(key);
93        }
94
95        public void generateClass(ClassVisitor v) {
96            Type sourceType = Type.getType(source);
97            Type targetType = Type.getType(target);
98            ClassEmitter ce = new ClassEmitter(v);
99            ce.begin_class(Constants.V1_2,
100                           Constants.ACC_PUBLIC,
101                           getClassName(),
102                           BEAN_COPIER,
103                           null,
104                           Constants.SOURCE_FILE);
105
106            EmitUtils.null_constructor(ce);
107            CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, COPY, null);
108            PropertyDescriptor[] getters = ReflectUtils.getBeanGetters(source);
109            PropertyDescriptor[] setters = ReflectUtils.getBeanGetters(target);
110
111            Map names = new HashMap();
112            for (int i = 0; i < getters.length; i++) {
113                names.put(getters[i].getName(), getters[i]);
114            }
115            Local targetLocal = e.make_local();
116            Local sourceLocal = e.make_local();
117            if (useConverter) {
118                e.load_arg(1);
119                e.checkcast(targetType);
120                e.store_local(targetLocal);
121                e.load_arg(0);
122                e.checkcast(sourceType);
123                e.store_local(sourceLocal);
124            } else {
125                e.load_arg(1);
126                e.checkcast(targetType);
127                e.load_arg(0);
128                e.checkcast(sourceType);
129            }
130            for (int i = 0; i < setters.length; i++) {
131                PropertyDescriptor setter = setters[i];
132                PropertyDescriptor getter = (PropertyDescriptor)names.get(setter.getName());
133                if (getter != null) {
134                    MethodInfo read = ReflectUtils.getMethodInfo(getter.getReadMethod());
135                    MethodInfo write = ReflectUtils.getMethodInfo(setter.getWriteMethod());
136                    if (useConverter) {
137                        Type setterType = write.getSignature().getArgumentTypes()[0];
138                        e.load_local(targetLocal);
139                        e.load_arg(2);
140                        e.load_local(sourceLocal);
141                        e.invoke(read);
142                        e.box(read.getSignature().getReturnType());
143                        EmitUtils.load_class(e, setterType);
144                        e.push(write.getSignature().getName());
145                        e.invoke_interface(CONVERTER, CONVERT);
146                        e.unbox_or_zero(setterType);
147                        e.invoke(write);
148                    } else if (compatible(getter, setter)) {
149                        e.dup2();
150                        e.invoke(read);
151                        e.invoke(write);
152                    }
153                }
154            }
155            e.return_value();
156            e.end_method();
157            ce.end_class();
158        }
159
160        private static boolean compatible(PropertyDescriptor getter, PropertyDescriptor setter) {
161            // TODO: allow automatic widening conversions?
162            return setter.getPropertyType().isAssignableFrom(getter.getPropertyType());
163        }
164
165        protected Object firstInstance(Class type) {
166            return ReflectUtils.newInstance(type);
167        }
168
169        protected Object nextInstance(Object instance) {
170            return instance;
171        }
172    }
173}
174