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