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.*;
19674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogenimport java.util.*;
20674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
21674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogenimport org.mockito.asm.ClassVisitor;
22674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogenimport org.mockito.asm.Label;
23674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogenimport org.mockito.asm.Type;
24674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogenimport org.mockito.cglib.core.*;
25674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
26674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogenclass BeanMapEmitter extends ClassEmitter {
27674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    private static final Type BEAN_MAP =
28674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen      TypeUtils.parseType("org.mockito.cglib.beans.BeanMap");
29674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    private static final Type FIXED_KEY_SET =
30674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen      TypeUtils.parseType("org.mockito.cglib.beans.FixedKeySet");
31674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    private static final Signature CSTRUCT_OBJECT =
32674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen      TypeUtils.parseConstructor("Object");
33674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    private static final Signature CSTRUCT_STRING_ARRAY =
34674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen      TypeUtils.parseConstructor("String[]");
35674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    private static final Signature BEAN_MAP_GET =
36674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen      TypeUtils.parseSignature("Object get(Object, Object)");
37674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    private static final Signature BEAN_MAP_PUT =
38674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen      TypeUtils.parseSignature("Object put(Object, Object, Object)");
39674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    private static final Signature KEY_SET =
40674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen      TypeUtils.parseSignature("java.util.Set keySet()");
41674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    private static final Signature NEW_INSTANCE =
42674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen      new Signature("newInstance", BEAN_MAP, new Type[]{ Constants.TYPE_OBJECT });
43674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    private static final Signature GET_PROPERTY_TYPE =
44674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen      TypeUtils.parseSignature("Class getPropertyType(String)");
45674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
46674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public BeanMapEmitter(ClassVisitor v, String className, Class type, int require) {
47674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        super(v);
48674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
49674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        begin_class(Constants.V1_2, Constants.ACC_PUBLIC, className, BEAN_MAP, null, Constants.SOURCE_FILE);
50674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        EmitUtils.null_constructor(this);
51674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        EmitUtils.factory_method(this, NEW_INSTANCE);
52674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        generateConstructor();
53674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
54674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        Map getters = makePropertyMap(ReflectUtils.getBeanGetters(type));
55674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        Map setters = makePropertyMap(ReflectUtils.getBeanSetters(type));
56674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        Map allProps = new HashMap();
57674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        allProps.putAll(getters);
58674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        allProps.putAll(setters);
59674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
60674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        if (require != 0) {
61674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            for (Iterator it = allProps.keySet().iterator(); it.hasNext();) {
62674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                String name = (String)it.next();
63674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                if ((((require & BeanMap.REQUIRE_GETTER) != 0) && !getters.containsKey(name)) ||
64674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                    (((require & BeanMap.REQUIRE_SETTER) != 0) && !setters.containsKey(name))) {
65674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                    it.remove();
66674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                    getters.remove(name);
67674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                    setters.remove(name);
68674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                }
69674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            }
70674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
71674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        generateGet(type, getters);
72674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        generatePut(type, setters);
73674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
74674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        String[] allNames = getNames(allProps);
75674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        generateKeySet(allNames);
76674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        generateGetPropertyType(allProps, allNames);
77674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        end_class();
78674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
79674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
80674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    private Map makePropertyMap(PropertyDescriptor[] props) {
81674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        Map names = new HashMap();
82674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        for (int i = 0; i < props.length; i++) {
83674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            names.put(((PropertyDescriptor)props[i]).getName(), props[i]);
84674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
85674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        return names;
86674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
87674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
88674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    private String[] getNames(Map propertyMap) {
89674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        return (String[])propertyMap.keySet().toArray(new String[propertyMap.size()]);
90674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
91674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
92674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    private void generateConstructor() {
93674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        CodeEmitter e = begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT, null);
94674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.load_this();
95674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.load_arg(0);
96674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.super_invoke_constructor(CSTRUCT_OBJECT);
97674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.return_value();
98674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.end_method();
99674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
100674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
101674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    private void generateGet(Class type, final Map getters) {
102674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, BEAN_MAP_GET, null);
103674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.load_arg(0);
104674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.checkcast(Type.getType(type));
105674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.load_arg(1);
106674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.checkcast(Constants.TYPE_STRING);
107674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        EmitUtils.string_switch(e, getNames(getters), Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
108674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            public void processCase(Object key, Label end) {
109674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                PropertyDescriptor pd = (PropertyDescriptor)getters.get(key);
110674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                MethodInfo method = ReflectUtils.getMethodInfo(pd.getReadMethod());
111674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.invoke(method);
112674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.box(method.getSignature().getReturnType());
113674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.return_value();
114674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            }
115674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            public void processDefault() {
116674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.aconst_null();
117674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.return_value();
118674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            }
119674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        });
120674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.end_method();
121674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
122674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
123674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    private void generatePut(Class type, final Map setters) {
124674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, BEAN_MAP_PUT, null);
125674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.load_arg(0);
126674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.checkcast(Type.getType(type));
127674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.load_arg(1);
128674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.checkcast(Constants.TYPE_STRING);
129674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        EmitUtils.string_switch(e, getNames(setters), Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
130674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            public void processCase(Object key, Label end) {
131674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                PropertyDescriptor pd = (PropertyDescriptor)setters.get(key);
132674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                if (pd.getReadMethod() == null) {
133674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                    e.aconst_null();
134674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                } else {
135674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                    MethodInfo read = ReflectUtils.getMethodInfo(pd.getReadMethod());
136674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                    e.dup();
137674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                    e.invoke(read);
138674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                    e.box(read.getSignature().getReturnType());
139674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                }
140674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.swap(); // move old value behind bean
141674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.load_arg(2); // new value
142674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                MethodInfo write = ReflectUtils.getMethodInfo(pd.getWriteMethod());
143674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.unbox(write.getSignature().getArgumentTypes()[0]);
144674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.invoke(write);
145674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.return_value();
146674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            }
147674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            public void processDefault() {
148674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                // fall-through
149674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            }
150674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        });
151674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.aconst_null();
152674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.return_value();
153674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.end_method();
154674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
155674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
156674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    private void generateKeySet(String[] allNames) {
157674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        // static initializer
158674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        declare_field(Constants.ACC_STATIC | Constants.ACC_PRIVATE, "keys", FIXED_KEY_SET, null);
159674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
160674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        CodeEmitter e = begin_static();
161674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.new_instance(FIXED_KEY_SET);
162674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.dup();
163674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        EmitUtils.push_array(e, allNames);
164674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.invoke_constructor(FIXED_KEY_SET, CSTRUCT_STRING_ARRAY);
165674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.putfield("keys");
166674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.return_value();
167674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.end_method();
168674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
169674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        // keySet
170674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e = begin_method(Constants.ACC_PUBLIC, KEY_SET, null);
171674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.load_this();
172674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.getfield("keys");
173674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.return_value();
174674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.end_method();
175674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
176674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
177674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    private void generateGetPropertyType(final Map allProps, String[] allNames) {
178674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, GET_PROPERTY_TYPE, null);
179674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.load_arg(0);
180674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        EmitUtils.string_switch(e, allNames, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
181674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            public void processCase(Object key, Label end) {
182674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                PropertyDescriptor pd = (PropertyDescriptor)allProps.get(key);
183674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                EmitUtils.load_class(e, Type.getType(pd.getPropertyType()));
184674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.return_value();
185674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            }
186674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            public void processDefault() {
187674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.aconst_null();
188674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                e.return_value();
189674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            }
190674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        });
191674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        e.end_method();
192674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
193674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen}
194