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.*; 19import java.util.*; 20 21import org.mockito.asm.ClassVisitor; 22import org.mockito.asm.Label; 23import org.mockito.asm.Type; 24import org.mockito.cglib.core.*; 25 26class BeanMapEmitter extends ClassEmitter { 27 private static final Type BEAN_MAP = 28 TypeUtils.parseType("org.mockito.cglib.beans.BeanMap"); 29 private static final Type FIXED_KEY_SET = 30 TypeUtils.parseType("org.mockito.cglib.beans.FixedKeySet"); 31 private static final Signature CSTRUCT_OBJECT = 32 TypeUtils.parseConstructor("Object"); 33 private static final Signature CSTRUCT_STRING_ARRAY = 34 TypeUtils.parseConstructor("String[]"); 35 private static final Signature BEAN_MAP_GET = 36 TypeUtils.parseSignature("Object get(Object, Object)"); 37 private static final Signature BEAN_MAP_PUT = 38 TypeUtils.parseSignature("Object put(Object, Object, Object)"); 39 private static final Signature KEY_SET = 40 TypeUtils.parseSignature("java.util.Set keySet()"); 41 private static final Signature NEW_INSTANCE = 42 new Signature("newInstance", BEAN_MAP, new Type[]{ Constants.TYPE_OBJECT }); 43 private static final Signature GET_PROPERTY_TYPE = 44 TypeUtils.parseSignature("Class getPropertyType(String)"); 45 46 public BeanMapEmitter(ClassVisitor v, String className, Class type, int require) { 47 super(v); 48 49 begin_class(Constants.V1_2, Constants.ACC_PUBLIC, className, BEAN_MAP, null, Constants.SOURCE_FILE); 50 EmitUtils.null_constructor(this); 51 EmitUtils.factory_method(this, NEW_INSTANCE); 52 generateConstructor(); 53 54 Map getters = makePropertyMap(ReflectUtils.getBeanGetters(type)); 55 Map setters = makePropertyMap(ReflectUtils.getBeanSetters(type)); 56 Map allProps = new HashMap(); 57 allProps.putAll(getters); 58 allProps.putAll(setters); 59 60 if (require != 0) { 61 for (Iterator it = allProps.keySet().iterator(); it.hasNext();) { 62 String name = (String)it.next(); 63 if ((((require & BeanMap.REQUIRE_GETTER) != 0) && !getters.containsKey(name)) || 64 (((require & BeanMap.REQUIRE_SETTER) != 0) && !setters.containsKey(name))) { 65 it.remove(); 66 getters.remove(name); 67 setters.remove(name); 68 } 69 } 70 } 71 generateGet(type, getters); 72 generatePut(type, setters); 73 74 String[] allNames = getNames(allProps); 75 generateKeySet(allNames); 76 generateGetPropertyType(allProps, allNames); 77 end_class(); 78 } 79 80 private Map makePropertyMap(PropertyDescriptor[] props) { 81 Map names = new HashMap(); 82 for (int i = 0; i < props.length; i++) { 83 names.put(((PropertyDescriptor)props[i]).getName(), props[i]); 84 } 85 return names; 86 } 87 88 private String[] getNames(Map propertyMap) { 89 return (String[])propertyMap.keySet().toArray(new String[propertyMap.size()]); 90 } 91 92 private void generateConstructor() { 93 CodeEmitter e = begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT, null); 94 e.load_this(); 95 e.load_arg(0); 96 e.super_invoke_constructor(CSTRUCT_OBJECT); 97 e.return_value(); 98 e.end_method(); 99 } 100 101 private void generateGet(Class type, final Map getters) { 102 final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, BEAN_MAP_GET, null); 103 e.load_arg(0); 104 e.checkcast(Type.getType(type)); 105 e.load_arg(1); 106 e.checkcast(Constants.TYPE_STRING); 107 EmitUtils.string_switch(e, getNames(getters), Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() { 108 public void processCase(Object key, Label end) { 109 PropertyDescriptor pd = (PropertyDescriptor)getters.get(key); 110 MethodInfo method = ReflectUtils.getMethodInfo(pd.getReadMethod()); 111 e.invoke(method); 112 e.box(method.getSignature().getReturnType()); 113 e.return_value(); 114 } 115 public void processDefault() { 116 e.aconst_null(); 117 e.return_value(); 118 } 119 }); 120 e.end_method(); 121 } 122 123 private void generatePut(Class type, final Map setters) { 124 final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, BEAN_MAP_PUT, null); 125 e.load_arg(0); 126 e.checkcast(Type.getType(type)); 127 e.load_arg(1); 128 e.checkcast(Constants.TYPE_STRING); 129 EmitUtils.string_switch(e, getNames(setters), Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() { 130 public void processCase(Object key, Label end) { 131 PropertyDescriptor pd = (PropertyDescriptor)setters.get(key); 132 if (pd.getReadMethod() == null) { 133 e.aconst_null(); 134 } else { 135 MethodInfo read = ReflectUtils.getMethodInfo(pd.getReadMethod()); 136 e.dup(); 137 e.invoke(read); 138 e.box(read.getSignature().getReturnType()); 139 } 140 e.swap(); // move old value behind bean 141 e.load_arg(2); // new value 142 MethodInfo write = ReflectUtils.getMethodInfo(pd.getWriteMethod()); 143 e.unbox(write.getSignature().getArgumentTypes()[0]); 144 e.invoke(write); 145 e.return_value(); 146 } 147 public void processDefault() { 148 // fall-through 149 } 150 }); 151 e.aconst_null(); 152 e.return_value(); 153 e.end_method(); 154 } 155 156 private void generateKeySet(String[] allNames) { 157 // static initializer 158 declare_field(Constants.ACC_STATIC | Constants.ACC_PRIVATE, "keys", FIXED_KEY_SET, null); 159 160 CodeEmitter e = begin_static(); 161 e.new_instance(FIXED_KEY_SET); 162 e.dup(); 163 EmitUtils.push_array(e, allNames); 164 e.invoke_constructor(FIXED_KEY_SET, CSTRUCT_STRING_ARRAY); 165 e.putfield("keys"); 166 e.return_value(); 167 e.end_method(); 168 169 // keySet 170 e = begin_method(Constants.ACC_PUBLIC, KEY_SET, null); 171 e.load_this(); 172 e.getfield("keys"); 173 e.return_value(); 174 e.end_method(); 175 } 176 177 private void generateGetPropertyType(final Map allProps, String[] allNames) { 178 final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, GET_PROPERTY_TYPE, null); 179 e.load_arg(0); 180 EmitUtils.string_switch(e, allNames, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() { 181 public void processCase(Object key, Label end) { 182 PropertyDescriptor pd = (PropertyDescriptor)allProps.get(key); 183 EmitUtils.load_class(e, Type.getType(pd.getPropertyType())); 184 e.return_value(); 185 } 186 public void processDefault() { 187 e.aconst_null(); 188 e.return_value(); 189 } 190 }); 191 e.end_method(); 192 } 193} 194