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.lang.reflect.Constructor;
20674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogenimport java.lang.reflect.Method;
21674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogenimport java.util.*;
22674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
23674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogenimport org.mockito.asm.ClassVisitor;
24674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogenimport org.mockito.cglib.core.*;
25674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
26674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen/**
27674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen * A <code>Map</code>-based view of a JavaBean.  The default set of keys is the
28674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen * union of all property names (getters or setters). An attempt to set
29674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen * a read-only property will be ignored, and write-only properties will
30674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen * be returned as <code>null</code>. Removal of objects is not a
31674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen * supported (the key set is fixed).
32674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen * @author Chris Nokleberg
33674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen */
34674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogenabstract public class BeanMap implements Map {
35674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    /**
36674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * Limit the properties reflected in the key set of the map
37674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * to readable properties.
38674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * @see BeanMap.Generator#setRequire
39674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     */
40674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public static final int REQUIRE_GETTER = 1;
41674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
42674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    /**
43674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * Limit the properties reflected in the key set of the map
44674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * to writable properties.
45674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * @see BeanMap.Generator#setRequire
46674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     */
47674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public static final int REQUIRE_SETTER = 2;
48674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
49674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    /**
50674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * Helper method to create a new <code>BeanMap</code>.  For finer
51674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * control over the generated instance, use a new instance of
52674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * <code>BeanMap.Generator</code> instead of this static method.
53674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * @param bean the JavaBean underlying the map
54674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * @return a new <code>BeanMap</code> instance
55674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     */
56674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public static BeanMap create(Object bean) {
57674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        Generator gen = new Generator();
58674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        gen.setBean(bean);
59674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        return gen.create();
60674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
61674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
62674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public static class Generator extends AbstractClassGenerator {
63674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        private static final Source SOURCE = new Source(BeanMap.class.getName());
64674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
65674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        private static final BeanMapKey KEY_FACTORY =
66674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen          (BeanMapKey)KeyFactory.create(BeanMapKey.class, KeyFactory.CLASS_BY_NAME);
67674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
68674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        interface BeanMapKey {
69674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            public Object newInstance(Class type, int require);
70674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
71674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
72674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        private Object bean;
73674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        private Class beanClass;
74674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        private int require;
75674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
76674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        public Generator() {
77674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            super(SOURCE);
78674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
79674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
80674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        /**
81674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen         * Set the bean that the generated map should reflect. The bean may be swapped
82674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen         * out for another bean of the same type using {@link #setBean}.
83674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen         * Calling this method overrides any value previously set using {@link #setBeanClass}.
84674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen         * You must call either this method or {@link #setBeanClass} before {@link #create}.
85674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen         * @param bean the initial bean
86674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen         */
87674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        public void setBean(Object bean) {
88674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            this.bean = bean;
89674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            if (bean != null)
90674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                beanClass = bean.getClass();
91674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
92674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
93674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        /**
94674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen         * Set the class of the bean that the generated map should support.
95674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen         * You must call either this method or {@link #setBeanClass} before {@link #create}.
96674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen         * @param beanClass the class of the bean
97674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen         */
98674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        public void setBeanClass(Class beanClass) {
99674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            this.beanClass = beanClass;
100674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
101674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
102674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        /**
103674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen         * Limit the properties reflected by the generated map.
104674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen         * @param require any combination of {@link #REQUIRE_GETTER} and
105674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen         * {@link #REQUIRE_SETTER}; default is zero (any property allowed)
106674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen         */
107674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        public void setRequire(int require) {
108674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            this.require = require;
109674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
110674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
111674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        protected ClassLoader getDefaultClassLoader() {
112674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            return beanClass.getClassLoader();
113674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
114674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
115674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        /**
116674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen         * Create a new instance of the <code>BeanMap</code>. An existing
117674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen         * generated class will be reused if possible.
118674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen         */
119674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        public BeanMap create() {
120674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            if (beanClass == null)
121674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                throw new IllegalArgumentException("Class of bean unknown");
122674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            setNamePrefix(beanClass.getName());
123674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            return (BeanMap)super.create(KEY_FACTORY.newInstance(beanClass, require));
124674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
125674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
126674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        public void generateClass(ClassVisitor v) throws Exception {
127674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            new BeanMapEmitter(v, getClassName(), beanClass, require);
128674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
129674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
130674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        protected Object firstInstance(Class type) {
131674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            return ((BeanMap)ReflectUtils.newInstance(type)).newInstance(bean);
132674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
133674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
134674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        protected Object nextInstance(Object instance) {
135674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            return ((BeanMap)instance).newInstance(bean);
136674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
137674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
138674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
139674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    /**
140674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * Create a new <code>BeanMap</code> instance using the specified bean.
141674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * This is faster than using the {@link #create} static method.
142674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * @param bean the JavaBean underlying the map
143674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * @return a new <code>BeanMap</code> instance
144674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     */
145674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    abstract public BeanMap newInstance(Object bean);
146674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
147674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    /**
148674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * Get the type of a property.
149674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * @param name the name of the JavaBean property
150674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * @return the type of the property, or null if the property does not exist
151674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     */
152674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    abstract public Class getPropertyType(String name);
153674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
154674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    protected Object bean;
155674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
156674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    protected BeanMap() {
157674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
158674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
159674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    protected BeanMap(Object bean) {
160674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        setBean(bean);
161674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
162674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
163674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public Object get(Object key) {
164674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        return get(bean, key);
165674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
166674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
167674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public Object put(Object key, Object value) {
168674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        return put(bean, key, value);
169674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
170674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
171674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    /**
172674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * Get the property of a bean. This allows a <code>BeanMap</code>
173674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * to be used statically for multiple beans--the bean instance tied to the
174674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * map is ignored and the bean passed to this method is used instead.
175674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * @param bean the bean to query; must be compatible with the type of
176674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * this <code>BeanMap</code>
177674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * @param key must be a String
178674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * @return the current value, or null if there is no matching property
179674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     */
180674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    abstract public Object get(Object bean, Object key);
181674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
182674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    /**
183674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * Set the property of a bean. This allows a <code>BeanMap</code>
184674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * to be used statically for multiple beans--the bean instance tied to the
185674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * map is ignored and the bean passed to this method is used instead.
186674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * @param key must be a String
187674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * @return the old value, if there was one, or null
188674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     */
189674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    abstract public Object put(Object bean, Object key, Object value);
190674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
191674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    /**
192674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * Change the underlying bean this map should use.
193674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * @param bean the new JavaBean
194674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * @see #getBean
195674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     */
196674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public void setBean(Object bean) {
197674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        this.bean = bean;
198674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
199674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
200674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    /**
201674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * Return the bean currently in use by this map.
202674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * @return the current JavaBean
203674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * @see #setBean
204674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     */
205674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public Object getBean() {
206674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        return bean;
207674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
208674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
209674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public void clear() {
210674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        throw new UnsupportedOperationException();
211674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
212674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
213674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public boolean containsKey(Object key) {
214674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        return keySet().contains(key);
215674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
216674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
217674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public boolean containsValue(Object value) {
218674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        for (Iterator it = keySet().iterator(); it.hasNext();) {
219674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            Object v = get(it.next());
220674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            if (((value == null) && (v == null)) || value.equals(v))
221674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                return true;
222674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
223674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        return false;
224674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
225674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
226674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public int size() {
227674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        return keySet().size();
228674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
229674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
230674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public boolean isEmpty() {
231674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        return size() == 0;
232674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
233674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
234674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public Object remove(Object key) {
235674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        throw new UnsupportedOperationException();
236674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
237674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
238674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public void putAll(Map t) {
239674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        for (Iterator it = t.keySet().iterator(); it.hasNext();) {
240674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            Object key = it.next();
241674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            put(key, t.get(key));
242674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
243674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
244674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
245674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public boolean equals(Object o) {
246674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        if (o == null || !(o instanceof Map)) {
247674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            return false;
248674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
249674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        Map other = (Map)o;
250674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        if (size() != other.size()) {
251674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            return false;
252674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
253674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        for (Iterator it = keySet().iterator(); it.hasNext();) {
254674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            Object key = it.next();
255674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            if (!other.containsKey(key)) {
256674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                return false;
257674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            }
258674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            Object v1 = get(key);
259674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            Object v2 = other.get(key);
260674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            if (!((v1 == null) ? v2 == null : v1.equals(v2))) {
261674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                return false;
262674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            }
263674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
264674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        return true;
265674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
266674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
267674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public int hashCode() {
268674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        int code = 0;
269674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        for (Iterator it = keySet().iterator(); it.hasNext();) {
270674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            Object key = it.next();
271674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            Object value = get(key);
272674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            code += ((key == null) ? 0 : key.hashCode()) ^
273674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                ((value == null) ? 0 : value.hashCode());
274674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
275674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        return code;
276674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
277674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
278674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    // TODO: optimize
279674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public Set entrySet() {
280674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        HashMap copy = new HashMap();
281674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        for (Iterator it = keySet().iterator(); it.hasNext();) {
282674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            Object key = it.next();
283674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            copy.put(key, get(key));
284674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
285674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        return Collections.unmodifiableMap(copy).entrySet();
286674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
287674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
288674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public Collection values() {
289674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        Set keys = keySet();
290674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        List values = new ArrayList(keys.size());
291674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        for (Iterator it = keys.iterator(); it.hasNext();) {
292674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            values.add(get(it.next()));
293674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
294674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        return Collections.unmodifiableCollection(values);
295674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
296674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen
297674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    /*
298674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     * @see java.util.AbstractMap#toString
299674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen     */
300674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    public String toString()
301674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    {
302674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        StringBuffer sb = new StringBuffer();
303674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        sb.append('{');
304674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        for (Iterator it = keySet().iterator(); it.hasNext();) {
305674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            Object key = it.next();
306674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            sb.append(key);
307674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            sb.append('=');
308674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            sb.append(get(key));
309674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            if (it.hasNext()) {
310674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen                sb.append(", ");
311674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen            }
312674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        }
313674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        sb.append('}');
314674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen        return sb.toString();
315674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen    }
316674060f01e9090cd21b3c5656cc3204912ad17a6Jon Boekenoogen}
317