|
|
|
|
@@ -0,0 +1,330 @@
|
|
|
|
|
/*
|
|
|
|
|
* Copyright 2003,2004 The Apache Software Foundation
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
package org.springframework.cglib.beans;
|
|
|
|
|
|
|
|
|
|
import java.security.ProtectionDomain;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Collection;
|
|
|
|
|
import java.util.Collections;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.Iterator;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.Set;
|
|
|
|
|
|
|
|
|
|
import org.springframework.asm.ClassVisitor;
|
|
|
|
|
import org.springframework.cglib.core.AbstractClassGenerator;
|
|
|
|
|
import org.springframework.cglib.core.KeyFactory;
|
|
|
|
|
import org.springframework.cglib.core.ReflectUtils;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A <code>Map</code>-based view of a JavaBean. The default set of keys is the
|
|
|
|
|
* union of all property names (getters or setters). An attempt to set
|
|
|
|
|
* a read-only property will be ignored, and write-only properties will
|
|
|
|
|
* be returned as <code>null</code>. Removal of objects is not a
|
|
|
|
|
* supported (the key set is fixed).
|
|
|
|
|
* @author Chris Nokleberg
|
|
|
|
|
*/
|
|
|
|
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
|
|
|
|
abstract public class BeanMap implements Map {
|
|
|
|
|
/**
|
|
|
|
|
* Limit the properties reflected in the key set of the map
|
|
|
|
|
* to readable properties.
|
|
|
|
|
* @see BeanMap.Generator#setRequire
|
|
|
|
|
*/
|
|
|
|
|
public static final int REQUIRE_GETTER = 1;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Limit the properties reflected in the key set of the map
|
|
|
|
|
* to writable properties.
|
|
|
|
|
* @see BeanMap.Generator#setRequire
|
|
|
|
|
*/
|
|
|
|
|
public static final int REQUIRE_SETTER = 2;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Helper method to create a new <code>BeanMap</code>. For finer
|
|
|
|
|
* control over the generated instance, use a new instance of
|
|
|
|
|
* <code>BeanMap.Generator</code> instead of this static method.
|
|
|
|
|
* @param bean the JavaBean underlying the map
|
|
|
|
|
* @return a new <code>BeanMap</code> instance
|
|
|
|
|
*/
|
|
|
|
|
public static BeanMap create(Object bean) {
|
|
|
|
|
Generator gen = new Generator();
|
|
|
|
|
gen.setBean(bean);
|
|
|
|
|
return gen.create();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static class Generator extends AbstractClassGenerator {
|
|
|
|
|
private static final Source SOURCE = new Source(BeanMap.class.getName());
|
|
|
|
|
|
|
|
|
|
private static final BeanMapKey KEY_FACTORY =
|
|
|
|
|
(BeanMapKey)KeyFactory.create(BeanMapKey.class, KeyFactory.CLASS_BY_NAME);
|
|
|
|
|
|
|
|
|
|
interface BeanMapKey {
|
|
|
|
|
public Object newInstance(Class type, int require);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Object bean;
|
|
|
|
|
private Class beanClass;
|
|
|
|
|
private int require;
|
|
|
|
|
|
|
|
|
|
public Generator() {
|
|
|
|
|
super(SOURCE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the bean that the generated map should reflect. The bean may be swapped
|
|
|
|
|
* out for another bean of the same type using {@link #setBean}.
|
|
|
|
|
* Calling this method overrides any value previously set using {@link #setBeanClass}.
|
|
|
|
|
* You must call either this method or {@link #setBeanClass} before {@link #create}.
|
|
|
|
|
* @param bean the initial bean
|
|
|
|
|
*/
|
|
|
|
|
public void setBean(Object bean) {
|
|
|
|
|
this.bean = bean;
|
|
|
|
|
if (bean != null) {
|
|
|
|
|
beanClass = bean.getClass();
|
|
|
|
|
setContextClass(beanClass);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the class of the bean that the generated map should support.
|
|
|
|
|
* You must call either this method or {@link #setBeanClass} before {@link #create}.
|
|
|
|
|
* @param beanClass the class of the bean
|
|
|
|
|
*/
|
|
|
|
|
public void setBeanClass(Class beanClass) {
|
|
|
|
|
this.beanClass = beanClass;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Limit the properties reflected by the generated map.
|
|
|
|
|
* @param require any combination of {@link #REQUIRE_GETTER} and
|
|
|
|
|
* {@link #REQUIRE_SETTER}; default is zero (any property allowed)
|
|
|
|
|
*/
|
|
|
|
|
public void setRequire(int require) {
|
|
|
|
|
this.require = require;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected ClassLoader getDefaultClassLoader() {
|
|
|
|
|
return beanClass.getClassLoader();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected ProtectionDomain getProtectionDomain() {
|
|
|
|
|
return ReflectUtils.getProtectionDomain(beanClass);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a new instance of the <code>BeanMap</code>. An existing
|
|
|
|
|
* generated class will be reused if possible.
|
|
|
|
|
*/
|
|
|
|
|
public BeanMap create() {
|
|
|
|
|
if (beanClass == null)
|
|
|
|
|
throw new IllegalArgumentException("Class of bean unknown");
|
|
|
|
|
setNamePrefix(beanClass.getName());
|
|
|
|
|
return (BeanMap)super.create(KEY_FACTORY.newInstance(beanClass, require));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void generateClass(ClassVisitor v) throws Exception {
|
|
|
|
|
new BeanMapEmitter(v, getClassName(), beanClass, require);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected Object firstInstance(Class type) {
|
|
|
|
|
return ((BeanMap)ReflectUtils.newInstance(type)).newInstance(bean);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected Object nextInstance(Object instance) {
|
|
|
|
|
return ((BeanMap)instance).newInstance(bean);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a new <code>BeanMap</code> instance using the specified bean.
|
|
|
|
|
* This is faster than using the {@link #create} static method.
|
|
|
|
|
* @param bean the JavaBean underlying the map
|
|
|
|
|
* @return a new <code>BeanMap</code> instance
|
|
|
|
|
*/
|
|
|
|
|
abstract public BeanMap newInstance(Object bean);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the type of a property.
|
|
|
|
|
* @param name the name of the JavaBean property
|
|
|
|
|
* @return the type of the property, or null if the property does not exist
|
|
|
|
|
*/
|
|
|
|
|
abstract public Class getPropertyType(String name);
|
|
|
|
|
|
|
|
|
|
protected Object bean;
|
|
|
|
|
|
|
|
|
|
protected BeanMap() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected BeanMap(Object bean) {
|
|
|
|
|
setBean(bean);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Object get(Object key) {
|
|
|
|
|
return get(bean, key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Object put(Object key, Object value) {
|
|
|
|
|
return put(bean, key, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the property of a bean. This allows a <code>BeanMap</code>
|
|
|
|
|
* to be used statically for multiple beans--the bean instance tied to the
|
|
|
|
|
* map is ignored and the bean passed to this method is used instead.
|
|
|
|
|
* @param bean the bean to query; must be compatible with the type of
|
|
|
|
|
* this <code>BeanMap</code>
|
|
|
|
|
* @param key must be a String
|
|
|
|
|
* @return the current value, or null if there is no matching property
|
|
|
|
|
*/
|
|
|
|
|
abstract public Object get(Object bean, Object key);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the property of a bean. This allows a <code>BeanMap</code>
|
|
|
|
|
* to be used statically for multiple beans--the bean instance tied to the
|
|
|
|
|
* map is ignored and the bean passed to this method is used instead.
|
|
|
|
|
* @param key must be a String
|
|
|
|
|
* @return the old value, if there was one, or null
|
|
|
|
|
*/
|
|
|
|
|
abstract public Object put(Object bean, Object key, Object value);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Change the underlying bean this map should use.
|
|
|
|
|
* @param bean the new JavaBean
|
|
|
|
|
* @see #getBean
|
|
|
|
|
*/
|
|
|
|
|
public void setBean(Object bean) {
|
|
|
|
|
this.bean = bean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return the bean currently in use by this map.
|
|
|
|
|
* @return the current JavaBean
|
|
|
|
|
* @see #setBean
|
|
|
|
|
*/
|
|
|
|
|
public Object getBean() {
|
|
|
|
|
return bean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void clear() {
|
|
|
|
|
throw new UnsupportedOperationException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean containsKey(Object key) {
|
|
|
|
|
return keySet().contains(key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean containsValue(Object value) {
|
|
|
|
|
for (Iterator it = keySet().iterator(); it.hasNext();) {
|
|
|
|
|
Object v = get(it.next());
|
|
|
|
|
if (((value == null) && (v == null)) || (value != null && value.equals(v)))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int size() {
|
|
|
|
|
return keySet().size();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean isEmpty() {
|
|
|
|
|
return size() == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Object remove(Object key) {
|
|
|
|
|
throw new UnsupportedOperationException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void putAll(Map t) {
|
|
|
|
|
for (Iterator it = t.keySet().iterator(); it.hasNext();) {
|
|
|
|
|
Object key = it.next();
|
|
|
|
|
put(key, t.get(key));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean equals(Object o) {
|
|
|
|
|
if (o == null || !(o instanceof Map)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
Map other = (Map)o;
|
|
|
|
|
if (size() != other.size()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
for (Iterator it = keySet().iterator(); it.hasNext();) {
|
|
|
|
|
Object key = it.next();
|
|
|
|
|
if (!other.containsKey(key)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
Object v1 = get(key);
|
|
|
|
|
Object v2 = other.get(key);
|
|
|
|
|
if (!((v1 == null) ? v2 == null : v1.equals(v2))) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int hashCode() {
|
|
|
|
|
int code = 0;
|
|
|
|
|
for (Iterator it = keySet().iterator(); it.hasNext();) {
|
|
|
|
|
Object key = it.next();
|
|
|
|
|
Object value = get(key);
|
|
|
|
|
code += ((key == null) ? 0 : key.hashCode()) ^
|
|
|
|
|
((value == null) ? 0 : value.hashCode());
|
|
|
|
|
}
|
|
|
|
|
return code;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: optimize
|
|
|
|
|
public Set entrySet() {
|
|
|
|
|
HashMap copy = new HashMap();
|
|
|
|
|
for (Iterator it = keySet().iterator(); it.hasNext();) {
|
|
|
|
|
Object key = it.next();
|
|
|
|
|
copy.put(key, get(key));
|
|
|
|
|
}
|
|
|
|
|
return Collections.unmodifiableMap(copy).entrySet();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Collection values() {
|
|
|
|
|
Set keys = keySet();
|
|
|
|
|
List values = new ArrayList(keys.size());
|
|
|
|
|
for (Iterator it = keys.iterator(); it.hasNext();) {
|
|
|
|
|
values.add(get(it.next()));
|
|
|
|
|
}
|
|
|
|
|
return Collections.unmodifiableCollection(values);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* @see java.util.AbstractMap#toString
|
|
|
|
|
*/
|
|
|
|
|
public String toString()
|
|
|
|
|
{
|
|
|
|
|
StringBuffer sb = new StringBuffer();
|
|
|
|
|
sb.append('{');
|
|
|
|
|
for (Iterator it = keySet().iterator(); it.hasNext();) {
|
|
|
|
|
Object key = it.next();
|
|
|
|
|
sb.append(key);
|
|
|
|
|
sb.append('=');
|
|
|
|
|
sb.append(get(key));
|
|
|
|
|
if (it.hasNext()) {
|
|
|
|
|
sb.append(", ");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
sb.append('}');
|
|
|
|
|
return sb.toString();
|
|
|
|
|
}
|
|
|
|
|
}
|