1 /*
2 * The Apache Software License, Version 1.1
3 *
4 * Copyright (c) 2002 The Apache Software Foundation. All rights
5 * reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * 3. The end-user documentation included with the redistribution,
20 * if any, must include the following acknowledgment:
21 * "This product includes software developed by the
22 * Apache Software Foundation (http://www.apache.org/)."
23 * Alternately, this acknowledgment may appear in the software itself,
24 * if and wherever such third-party acknowledgments normally appear.
25 *
26 * 4. The names "Apache" and "Apache Software Foundation" must
27 * not be used to endorse or promote products derived from this
28 * software without prior written permission. For written
29 * permission, please contact apache@apache.org.
30 *
31 * 5. Products derived from this software may not be called "Apache",
32 * nor may "Apache" appear in their name, without prior written
33 * permission of the Apache Software Foundation.
34 *
35 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46 * SUCH DAMAGE.
47 * ====================================================================
48 *
49 * This software consists of voluntary contributions made by many
50 * individuals on behalf of the Apache Software Foundation. For more
51 * information on the Apache Software Foundation, please see
52 * <http://www.apache.org/>.
53 */
54 package net.sf.cglib.beans;
55
56 import java.beans.*;
57 import java.lang.reflect.Constructor;
58 import java.lang.reflect.Method;
59 import java.util.*;
60 import net.sf.cglib.core.*;
61 import org.objectweb.asm.ClassVisitor;
62
63 /***
64 * A <code>Map</code>-based view of a JavaBean. The default set of keys is the
65 * union of all property names (getters or setters). An attempt to set
66 * a read-only property will be ignored, and write-only properties will
67 * be returned as <code>null</code>. Removal of objects is not a
68 * supported (the key set is fixed).
69 * @author Chris Nokleberg
70 */
71 abstract public class BeanMap implements Map {
72 /***
73 * Limit the properties reflected in the key set of the map
74 * to readable properties.
75 * @see BeanMap.Generator#setRequire
76 */
77 public static final int REQUIRE_GETTER = 1;
78
79 /***
80 * Limit the properties reflected in the key set of the map
81 * to writable properties.
82 * @see BeanMap.Generator#setRequire
83 */
84 public static final int REQUIRE_SETTER = 2;
85
86 /***
87 * Helper method to create a new <code>BeanMap</code>. For finer
88 * control over the generated instance, use a new instance of
89 * <code>BeanMap.Generator</code> instead of this static method.
90 * @param bean the JavaBean underlying the map
91 * @return a new <code>BeanMap</code> instance
92 */
93 public static BeanMap create(Object bean) {
94 Generator gen = new Generator();
95 gen.setBean(bean);
96 return gen.create();
97 }
98
99 public static class Generator extends AbstractClassGenerator {
100 private static final Source SOURCE = new Source(BeanMap.class.getName());
101
102 private static final BeanMapKey KEY_FACTORY =
103 (BeanMapKey)KeyFactory.create(BeanMapKey.class, KeyFactory.CLASS_BY_NAME);
104
105 interface BeanMapKey {
106 public Object newInstance(Class type, int require);
107 }
108
109 private Object bean;
110 private Class beanClass;
111 private int require;
112
113 public Generator() {
114 super(SOURCE);
115 }
116
117 /***
118 * Set the bean that the generated map should reflect. The bean may be swapped
119 * out for another bean of the same type using {@link #setBean}.
120 * Calling this method overrides any value previously set using {@link #setBeanClass}.
121 * You must call either this method or {@link #setBeanClass} before {@link #create}.
122 * @param bean the initial bean
123 */
124 public void setBean(Object bean) {
125 this.bean = bean;
126 if (bean != null)
127 beanClass = bean.getClass();
128 }
129
130 /***
131 * Set the class of the bean that the generated map should support.
132 * You must call either this method or {@link #setBeanClass} before {@link #create}.
133 * @param beanClass the class of the bean
134 */
135 public void setBeanClass(Class beanClass) {
136 this.beanClass = beanClass;
137 }
138
139 /***
140 * Limit the properties reflected by the generated map.
141 * @param require any combination of {@link #REQUIRE_GETTER} and
142 * {@link #REQUIRE_SETTER}; default is zero (any property allowed)
143 */
144 public void setRequire(int require) {
145 this.require = require;
146 }
147
148 protected ClassLoader getDefaultClassLoader() {
149 return beanClass.getClassLoader();
150 }
151
152 /***
153 * Create a new instance of the <code>BeanMap</code>. An existing
154 * generated class will be reused if possible.
155 */
156 public BeanMap create() {
157 if (beanClass == null)
158 throw new IllegalArgumentException("Class of bean unknown");
159 setNamePrefix(beanClass.getName());
160 return (BeanMap)super.create(KEY_FACTORY.newInstance(beanClass, require));
161 }
162
163 public void generateClass(ClassVisitor v) throws Exception {
164 new BeanMapEmitter(v, getClassName(), beanClass, require);
165 }
166
167 protected Object firstInstance(Class type) {
168 return ((BeanMap)ReflectUtils.newInstance(type)).newInstance(bean);
169 }
170
171 protected Object nextInstance(Object instance) {
172 return ((BeanMap)instance).newInstance(bean);
173 }
174 }
175
176 /***
177 * Create a new <code>BeanMap</code> instance using the specified bean.
178 * This is faster than using the {@link #create} static method.
179 * @param bean the JavaBean underlying the map
180 * @return a new <code>BeanMap</code> instance
181 */
182 abstract public BeanMap newInstance(Object bean);
183
184 /***
185 * Get the type of a property.
186 * @param name the name of the JavaBean property
187 * @return the type of the property, or null if the property does not exist
188 */
189 abstract public Class getPropertyType(String name);
190
191 protected Object bean;
192
193 protected BeanMap() {
194 }
195
196 protected BeanMap(Object bean) {
197 setBean(bean);
198 }
199
200 public Object get(Object key) {
201 return get(bean, key);
202 }
203
204 public Object put(Object key, Object value) {
205 return put(bean, key, value);
206 }
207
208 /***
209 * Get the property of a bean. This allows a <code>BeanMap</code>
210 * to be used statically for multiple beans--the bean instance tied to the
211 * map is ignored and the bean passed to this method is used instead.
212 * @param bean the bean to query; must be compatible with the type of
213 * this <code>BeanMap</code>
214 * @param key must be a String
215 * @return the current value, or null if there is no matching property
216 */
217 abstract public Object get(Object bean, Object key);
218
219 /***
220 * Set the property of a bean. This allows a <code>BeanMap</code>
221 * to be used statically for multiple beans--the bean instance tied to the
222 * map is ignored and the bean passed to this method is used instead.
223 * @param key must be a String
224 * @return the old value, if there was one, or null
225 */
226 abstract public Object put(Object bean, Object key, Object value);
227
228 /***
229 * Change the underlying bean this map should use.
230 * @param bean the new JavaBean
231 * @see #getBean
232 */
233 public void setBean(Object bean) {
234 this.bean = bean;
235 }
236
237 /***
238 * Return the bean currently in use by this map.
239 * @return the current JavaBean
240 * @see #setBean
241 */
242 public Object getBean() {
243 return bean;
244 }
245
246 public void clear() {
247 throw new UnsupportedOperationException();
248 }
249
250 public boolean containsKey(Object key) {
251 return keySet().contains(key);
252 }
253
254 public boolean containsValue(Object value) {
255 for (Iterator it = keySet().iterator(); it.hasNext();) {
256 Object v = get(it.next());
257 if (((value == null) && (v == null)) || value.equals(v))
258 return true;
259 }
260 return false;
261 }
262
263 public int size() {
264 return keySet().size();
265 }
266
267 public boolean isEmpty() {
268 return size() == 0;
269 }
270
271 public Object remove(Object key) {
272 throw new UnsupportedOperationException();
273 }
274
275 public void putAll(Map t) {
276 for (Iterator it = t.keySet().iterator(); it.hasNext();) {
277 Object key = it.next();
278 put(key, t.get(key));
279 }
280 }
281
282 public boolean equals(Object o) {
283 if (o == null || !(o instanceof Map)) {
284 return false;
285 }
286 Map other = (Map)o;
287 if (size() != other.size()) {
288 return false;
289 }
290 for (Iterator it = keySet().iterator(); it.hasNext();) {
291 Object key = it.next();
292 if (!other.containsKey(key)) {
293 return false;
294 }
295 Object v1 = get(key);
296 Object v2 = other.get(key);
297 if (!((v1 == null) ? v2 == null : v1.equals(v2))) {
298 return false;
299 }
300 }
301 return true;
302 }
303
304 public int hashCode() {
305 int code = 0;
306 for (Iterator it = keySet().iterator(); it.hasNext();) {
307 Object key = it.next();
308 Object value = get(key);
309 code += ((key == null) ? 0 : key.hashCode()) ^
310 ((value == null) ? 0 : value.hashCode());
311 }
312 return code;
313 }
314
315 // TODO: optimize
316 public Set entrySet() {
317 return Collections.unmodifiableMap(new HashMap(this)).entrySet();
318 }
319
320 public Collection values() {
321 Set keys = keySet();
322 List values = new ArrayList(keys.size());
323 for (Iterator it = keys.iterator(); it.hasNext();) {
324 values.add(get(it.next()));
325 }
326 return Collections.unmodifiableCollection(values);
327 }
328
329 /*
330 * @see java.util.AbstractMap#toString
331 */
332 public String toString()
333 {
334 StringBuffer sb = new StringBuffer();
335 sb.append('{');
336 for (Iterator it = keySet().iterator(); it.hasNext();) {
337 Object key = it.next();
338 sb.append(key);
339 sb.append('=');
340 sb.append(get(key));
341 if (it.hasNext()) {
342 sb.append(", ");
343 }
344 }
345 sb.append('}');
346 return sb.toString();
347 }
348 }
This page was automatically generated by Maven