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.Method; 20 21import org.mockito.asm.ClassVisitor; 22import org.mockito.asm.Type; 23import org.mockito.cglib.core.*; 24 25/** 26 * @author Chris Nokleberg 27 */ 28public class ImmutableBean 29{ 30 private static final Type ILLEGAL_STATE_EXCEPTION = 31 TypeUtils.parseType("IllegalStateException"); 32 private static final Signature CSTRUCT_OBJECT = 33 TypeUtils.parseConstructor("Object"); 34 private static final Class[] OBJECT_CLASSES = { Object.class }; 35 private static final String FIELD_NAME = "CGLIB$RWBean"; 36 37 private ImmutableBean() { 38 } 39 40 public static Object create(Object bean) { 41 Generator gen = new Generator(); 42 gen.setBean(bean); 43 return gen.create(); 44 } 45 46 public static class Generator extends AbstractClassGenerator { 47 private static final Source SOURCE = new Source(ImmutableBean.class.getName()); 48 private Object bean; 49 private Class target; 50 51 public Generator() { 52 super(SOURCE); 53 } 54 55 public void setBean(Object bean) { 56 this.bean = bean; 57 target = bean.getClass(); 58 } 59 60 protected ClassLoader getDefaultClassLoader() { 61 return target.getClassLoader(); 62 } 63 64 public Object create() { 65 String name = target.getName(); 66 setNamePrefix(name); 67 return super.create(name); 68 } 69 70 public void generateClass(ClassVisitor v) { 71 Type targetType = Type.getType(target); 72 ClassEmitter ce = new ClassEmitter(v); 73 ce.begin_class(Constants.V1_2, 74 Constants.ACC_PUBLIC, 75 getClassName(), 76 targetType, 77 null, 78 Constants.SOURCE_FILE); 79 80 ce.declare_field(Constants.ACC_FINAL | Constants.ACC_PRIVATE, FIELD_NAME, targetType, null); 81 82 CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT, null); 83 e.load_this(); 84 e.super_invoke_constructor(); 85 e.load_this(); 86 e.load_arg(0); 87 e.checkcast(targetType); 88 e.putfield(FIELD_NAME); 89 e.return_value(); 90 e.end_method(); 91 92 PropertyDescriptor[] descriptors = ReflectUtils.getBeanProperties(target); 93 Method[] getters = ReflectUtils.getPropertyMethods(descriptors, true, false); 94 Method[] setters = ReflectUtils.getPropertyMethods(descriptors, false, true); 95 96 for (int i = 0; i < getters.length; i++) { 97 MethodInfo getter = ReflectUtils.getMethodInfo(getters[i]); 98 e = EmitUtils.begin_method(ce, getter, Constants.ACC_PUBLIC); 99 e.load_this(); 100 e.getfield(FIELD_NAME); 101 e.invoke(getter); 102 e.return_value(); 103 e.end_method(); 104 } 105 106 for (int i = 0; i < setters.length; i++) { 107 MethodInfo setter = ReflectUtils.getMethodInfo(setters[i]); 108 e = EmitUtils.begin_method(ce, setter, Constants.ACC_PUBLIC); 109 e.throw_exception(ILLEGAL_STATE_EXCEPTION, "Bean is immutable"); 110 e.end_method(); 111 } 112 113 ce.end_class(); 114 } 115 116 protected Object firstInstance(Class type) { 117 return ReflectUtils.newInstance(type, OBJECT_CLASSES, new Object[]{ bean }); 118 } 119 120 // TODO: optimize 121 protected Object nextInstance(Object instance) { 122 return firstInstance(instance.getClass()); 123 } 124 } 125} 126