diff --git a/build.third_step6.gradle b/build.third_step6.gradle
index 0bc156155..d87976633 100644
--- a/build.third_step6.gradle
+++ b/build.third_step6.gradle
@@ -28,6 +28,8 @@ sourceSets{
"${srcDir}/fine-guava/src",
"${srcDir}/fine-lucene/src",
"${srcDir}/fine-lucene/resources",
+ "${srcDir}/fine-cglib/src",
+ "${srcDir}/fine-cglib/resources",
]
}
}
@@ -76,6 +78,8 @@ task copyFiles(type:Copy,dependsOn:'compileJava'){
with dataContent.call("${srcDir}/fine-guava/src")
with dataContent.call("${srcDir}/fine-lucene/src")
with dataContent.call("${srcDir}/fine-lucene/resources")
+ with dataContent.call("${srcDir}/fine-cglib/src")
+ with dataContent.call("${srcDir}/fine-cglib/resources")
into "${classesDir}"
}
}
diff --git a/fine-cglib/resources/com/fr/third/net/sf/cglib/util/words.txt b/fine-cglib/resources/com/fr/third/net/sf/cglib/util/words.txt
new file mode 100644
index 000000000..263a4396d
--- /dev/null
+++ b/fine-cglib/resources/com/fr/third/net/sf/cglib/util/words.txt
@@ -0,0 +1,50 @@
+Casuarinaceae
+hylomorphic
+granitize
+biddably
+repulsive
+amphimictical
+trio
+toxodont
+nonreigning
+dragbar
+Moronidae
+unlanguishing
+metabolizable
+Osmerus
+goran
+spiritfulness
+tetrachloromethane
+baobab
+caroline
+radioconductor
+imband
+crinoline
+circummundane
+incontractile
+forerank
+modernization
+meal
+fishman
+underbuy
+pertain
+equiped
+cockal
+unshrined
+Harb
+heterotaxis
+commensurableness
+baggy
+sarcophilous
+tankard
+acervuline
+unverifiably
+premidnight
+strangles
+vitellus
+Socratean
+flock
+scourage
+feverlike
+citharist
+harn
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanCopier.java b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanCopier.java
new file mode 100644
index 000000000..393ab2fc3
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanCopier.java
@@ -0,0 +1,176 @@
+/*
+ * 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 com.fr.third.net.sf.cglib.beans;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.*;
+import java.security.ProtectionDomain;
+import com.fr.third.net.sf.cglib.core.*;
+import com.fr.third.org.objectweb.asm.ClassVisitor;
+import com.fr.third.org.objectweb.asm.Type;
+import java.util.*;
+
+/**
+ * @author Chris Nokleberg
+ */
+abstract public class BeanCopier
+{
+ private static final BeanCopierKey KEY_FACTORY =
+ (BeanCopierKey)KeyFactory.create(BeanCopierKey.class);
+ private static final Type CONVERTER =
+ TypeUtils.parseType("com.fr.third.net.sf.cglib.core.Converter");
+ private static final Type BEAN_COPIER =
+ TypeUtils.parseType("com.fr.third.net.sf.cglib.beans.BeanCopier");
+ private static final Signature COPY =
+ new Signature("copy", Type.VOID_TYPE, new Type[]{ Constants.TYPE_OBJECT, Constants.TYPE_OBJECT, CONVERTER });
+ private static final Signature CONVERT =
+ TypeUtils.parseSignature("Object convert(Object, Class, Object)");
+
+ interface BeanCopierKey {
+ public Object newInstance(String source, String target, boolean useConverter);
+ }
+
+ public static BeanCopier create(Class source, Class target, boolean useConverter) {
+ Generator gen = new Generator();
+ gen.setSource(source);
+ gen.setTarget(target);
+ gen.setUseConverter(useConverter);
+ return gen.create();
+ }
+
+ abstract public void copy(Object from, Object to, Converter converter);
+
+ public static class Generator extends AbstractClassGenerator {
+ private static final Source SOURCE = new Source(BeanCopier.class.getName());
+ private Class source;
+ private Class target;
+ private boolean useConverter;
+
+ public Generator() {
+ super(SOURCE);
+ }
+
+ public void setSource(Class source) {
+ if(!Modifier.isPublic(source.getModifiers())){
+ setNamePrefix(source.getName());
+ }
+ this.source = source;
+ }
+
+ public void setTarget(Class target) {
+ if(!Modifier.isPublic(target.getModifiers())){
+ setNamePrefix(target.getName());
+ }
+
+ this.target = target;
+ }
+
+ public void setUseConverter(boolean useConverter) {
+ this.useConverter = useConverter;
+ }
+
+ protected ClassLoader getDefaultClassLoader() {
+ return source.getClassLoader();
+ }
+
+ protected ProtectionDomain getProtectionDomain() {
+ return ReflectUtils.getProtectionDomain(source);
+ }
+
+ public BeanCopier create() {
+ Object key = KEY_FACTORY.newInstance(source.getName(), target.getName(), useConverter);
+ return (BeanCopier)super.create(key);
+ }
+
+ public void generateClass(ClassVisitor v) {
+ Type sourceType = Type.getType(source);
+ Type targetType = Type.getType(target);
+ ClassEmitter ce = new ClassEmitter(v);
+ ce.begin_class(Constants.V1_2,
+ Constants.ACC_PUBLIC,
+ getClassName(),
+ BEAN_COPIER,
+ null,
+ Constants.SOURCE_FILE);
+
+ EmitUtils.null_constructor(ce);
+ CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, COPY, null);
+ PropertyDescriptor[] getters = ReflectUtils.getBeanGetters(source);
+ PropertyDescriptor[] setters = ReflectUtils.getBeanSetters(target);
+
+ Map names = new HashMap();
+ for (int i = 0; i < getters.length; i++) {
+ names.put(getters[i].getName(), getters[i]);
+ }
+ Local targetLocal = e.make_local();
+ Local sourceLocal = e.make_local();
+ if (useConverter) {
+ e.load_arg(1);
+ e.checkcast(targetType);
+ e.store_local(targetLocal);
+ e.load_arg(0);
+ e.checkcast(sourceType);
+ e.store_local(sourceLocal);
+ } else {
+ e.load_arg(1);
+ e.checkcast(targetType);
+ e.load_arg(0);
+ e.checkcast(sourceType);
+ }
+ for (int i = 0; i < setters.length; i++) {
+ PropertyDescriptor setter = setters[i];
+ PropertyDescriptor getter = (PropertyDescriptor)names.get(setter.getName());
+ if (getter != null) {
+ MethodInfo read = ReflectUtils.getMethodInfo(getter.getReadMethod());
+ MethodInfo write = ReflectUtils.getMethodInfo(setter.getWriteMethod());
+ if (useConverter) {
+ Type setterType = write.getSignature().getArgumentTypes()[0];
+ e.load_local(targetLocal);
+ e.load_arg(2);
+ e.load_local(sourceLocal);
+ e.invoke(read);
+ e.box(read.getSignature().getReturnType());
+ EmitUtils.load_class(e, setterType);
+ e.push(write.getSignature().getName());
+ e.invoke_interface(CONVERTER, CONVERT);
+ e.unbox_or_zero(setterType);
+ e.invoke(write);
+ } else if (compatible(getter, setter)) {
+ e.dup2();
+ e.invoke(read);
+ e.invoke(write);
+ }
+ }
+ }
+ e.return_value();
+ e.end_method();
+ ce.end_class();
+ }
+
+ private static boolean compatible(PropertyDescriptor getter, PropertyDescriptor setter) {
+ // TODO: allow automatic widening conversions?
+ return setter.getPropertyType().isAssignableFrom(getter.getPropertyType());
+ }
+
+ protected Object firstInstance(Class type) {
+ return ReflectUtils.newInstance(type);
+ }
+
+ protected Object nextInstance(Object instance) {
+ return instance;
+ }
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanGenerator.java b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanGenerator.java
new file mode 100644
index 000000000..51ef40ffe
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanGenerator.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.beans;
+
+import java.beans.PropertyDescriptor;
+import java.security.ProtectionDomain;
+import java.util.*;
+import com.fr.third.net.sf.cglib.core.*;
+import com.fr.third.org.objectweb.asm.ClassVisitor;
+import com.fr.third.org.objectweb.asm.Type;
+
+/**
+ * @author Juozas Baliuka, Chris Nokleberg
+ */
+public class BeanGenerator extends AbstractClassGenerator
+{
+ private static final Source SOURCE = new Source(BeanGenerator.class.getName());
+ private static final BeanGeneratorKey KEY_FACTORY =
+ (BeanGeneratorKey)KeyFactory.create(BeanGeneratorKey.class);
+
+ interface BeanGeneratorKey {
+ public Object newInstance(String superclass, Map props);
+ }
+
+ private Class superclass;
+ private Map props = new HashMap();
+ private boolean classOnly;
+
+ public BeanGenerator() {
+ super(SOURCE);
+ }
+
+ /**
+ * Set the class which the generated class will extend. The class
+ * must not be declared as final, and must have a non-private
+ * no-argument constructor.
+ * @param superclass class to extend, or null to extend Object
+ */
+ public void setSuperclass(Class superclass) {
+ if (superclass != null && superclass.equals(Object.class)) {
+ superclass = null;
+ }
+ this.superclass = superclass;
+ }
+
+ public void addProperty(String name, Class type) {
+ if (props.containsKey(name)) {
+ throw new IllegalArgumentException("Duplicate property name \"" + name + "\"");
+ }
+ props.put(name, Type.getType(type));
+ }
+
+ protected ClassLoader getDefaultClassLoader() {
+ if (superclass != null) {
+ return superclass.getClassLoader();
+ } else {
+ return null;
+ }
+ }
+
+ protected ProtectionDomain getProtectionDomain() {
+ return ReflectUtils.getProtectionDomain(superclass);
+ }
+
+ public Object create() {
+ classOnly = false;
+ return createHelper();
+ }
+
+ public Object createClass() {
+ classOnly = true;
+ return createHelper();
+ }
+
+ private Object createHelper() {
+ if (superclass != null) {
+ setNamePrefix(superclass.getName());
+ }
+ String superName = (superclass != null) ? superclass.getName() : "java.lang.Object";
+ Object key = KEY_FACTORY.newInstance(superName, props);
+ return super.create(key);
+ }
+
+ public void generateClass(ClassVisitor v) throws Exception {
+ int size = props.size();
+ String[] names = (String[])props.keySet().toArray(new String[size]);
+ Type[] types = new Type[size];
+ for (int i = 0; i < size; i++) {
+ types[i] = (Type)props.get(names[i]);
+ }
+ ClassEmitter ce = new ClassEmitter(v);
+ ce.begin_class(Constants.V1_2,
+ Constants.ACC_PUBLIC,
+ getClassName(),
+ superclass != null ? Type.getType(superclass) : Constants.TYPE_OBJECT,
+ null,
+ null);
+ EmitUtils.null_constructor(ce);
+ EmitUtils.add_properties(ce, names, types);
+ ce.end_class();
+ }
+
+ protected Object firstInstance(Class type) {
+ if (classOnly) {
+ return type;
+ } else {
+ return ReflectUtils.newInstance(type);
+ }
+ }
+
+ protected Object nextInstance(Object instance) {
+ Class protoclass = (instance instanceof Class) ? (Class)instance : instance.getClass();
+ if (classOnly) {
+ return protoclass;
+ } else {
+ return ReflectUtils.newInstance(protoclass);
+ }
+ }
+
+ public static void addProperties(BeanGenerator gen, Map props) {
+ for (Iterator it = props.keySet().iterator(); it.hasNext();) {
+ String name = (String)it.next();
+ gen.addProperty(name, (Class)props.get(name));
+ }
+ }
+
+ public static void addProperties(BeanGenerator gen, Class type) {
+ addProperties(gen, ReflectUtils.getBeanProperties(type));
+ }
+
+ public static void addProperties(BeanGenerator gen, PropertyDescriptor[] descriptors) {
+ for (int i = 0; i < descriptors.length; i++) {
+ gen.addProperty(descriptors[i].getName(), descriptors[i].getPropertyType());
+ }
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanMap.java b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanMap.java
new file mode 100644
index 000000000..9dddac1b4
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanMap.java
@@ -0,0 +1,320 @@
+/*
+ * 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 com.fr.third.net.sf.cglib.beans;
+
+import java.security.ProtectionDomain;
+import java.beans.*;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.*;
+import com.fr.third.net.sf.cglib.core.*;
+import com.fr.third.org.objectweb.asm.ClassVisitor;
+
+/**
+ * A Map-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 null. Removal of objects is not a
+ * supported (the key set is fixed).
+ * @author Chris Nokleberg
+ */
+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 BeanMap. For finer
+ * control over the generated instance, use a new instance of
+ * BeanMap.Generator instead of this static method.
+ * @param bean the JavaBean underlying the map
+ * @return a new BeanMap 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();
+ }
+
+ /**
+ * 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 BeanMap. 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 BeanMap 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 BeanMap 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 BeanMap
+ * 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 BeanMap
+ * @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 BeanMap
+ * 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();
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanMapEmitter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanMapEmitter.java
new file mode 100644
index 000000000..1284f33ba
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BeanMapEmitter.java
@@ -0,0 +1,192 @@
+/*
+ * 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 com.fr.third.net.sf.cglib.beans;
+
+import java.beans.*;
+import java.util.*;
+import com.fr.third.net.sf.cglib.core.*;
+import com.fr.third.org.objectweb.asm.ClassVisitor;
+import com.fr.third.org.objectweb.asm.Label;
+import com.fr.third.org.objectweb.asm.Type;
+
+class BeanMapEmitter extends ClassEmitter {
+ private static final Type BEAN_MAP =
+ TypeUtils.parseType("com.fr.third.net.sf.cglib.beans.BeanMap");
+ private static final Type FIXED_KEY_SET =
+ TypeUtils.parseType("com.fr.third.net.sf.cglib.beans.FixedKeySet");
+ private static final Signature CSTRUCT_OBJECT =
+ TypeUtils.parseConstructor("Object");
+ private static final Signature CSTRUCT_STRING_ARRAY =
+ TypeUtils.parseConstructor("String[]");
+ private static final Signature BEAN_MAP_GET =
+ TypeUtils.parseSignature("Object get(Object, Object)");
+ private static final Signature BEAN_MAP_PUT =
+ TypeUtils.parseSignature("Object put(Object, Object, Object)");
+ private static final Signature KEY_SET =
+ TypeUtils.parseSignature("java.util.Set keySet()");
+ private static final Signature NEW_INSTANCE =
+ new Signature("newInstance", BEAN_MAP, new Type[]{ Constants.TYPE_OBJECT });
+ private static final Signature GET_PROPERTY_TYPE =
+ TypeUtils.parseSignature("Class getPropertyType(String)");
+
+ public BeanMapEmitter(ClassVisitor v, String className, Class type, int require) {
+ super(v);
+
+ begin_class(Constants.V1_2, Constants.ACC_PUBLIC, className, BEAN_MAP, null, Constants.SOURCE_FILE);
+ EmitUtils.null_constructor(this);
+ EmitUtils.factory_method(this, NEW_INSTANCE);
+ generateConstructor();
+
+ Map getters = makePropertyMap(ReflectUtils.getBeanGetters(type));
+ Map setters = makePropertyMap(ReflectUtils.getBeanSetters(type));
+ Map allProps = new HashMap();
+ allProps.putAll(getters);
+ allProps.putAll(setters);
+
+ if (require != 0) {
+ for (Iterator it = allProps.keySet().iterator(); it.hasNext();) {
+ String name = (String)it.next();
+ if ((((require & BeanMap.REQUIRE_GETTER) != 0) && !getters.containsKey(name)) ||
+ (((require & BeanMap.REQUIRE_SETTER) != 0) && !setters.containsKey(name))) {
+ it.remove();
+ getters.remove(name);
+ setters.remove(name);
+ }
+ }
+ }
+ generateGet(type, getters);
+ generatePut(type, setters);
+
+ String[] allNames = getNames(allProps);
+ generateKeySet(allNames);
+ generateGetPropertyType(allProps, allNames);
+ end_class();
+ }
+
+ private Map makePropertyMap(PropertyDescriptor[] props) {
+ Map names = new HashMap();
+ for (int i = 0; i < props.length; i++) {
+ names.put(((PropertyDescriptor)props[i]).getName(), props[i]);
+ }
+ return names;
+ }
+
+ private String[] getNames(Map propertyMap) {
+ return (String[])propertyMap.keySet().toArray(new String[propertyMap.size()]);
+ }
+
+ private void generateConstructor() {
+ CodeEmitter e = begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT, null);
+ e.load_this();
+ e.load_arg(0);
+ e.super_invoke_constructor(CSTRUCT_OBJECT);
+ e.return_value();
+ e.end_method();
+ }
+
+ private void generateGet(Class type, final Map getters) {
+ final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, BEAN_MAP_GET, null);
+ e.load_arg(0);
+ e.checkcast(Type.getType(type));
+ e.load_arg(1);
+ e.checkcast(Constants.TYPE_STRING);
+ EmitUtils.string_switch(e, getNames(getters), Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
+ public void processCase(Object key, Label end) {
+ PropertyDescriptor pd = (PropertyDescriptor)getters.get(key);
+ MethodInfo method = ReflectUtils.getMethodInfo(pd.getReadMethod());
+ e.invoke(method);
+ e.box(method.getSignature().getReturnType());
+ e.return_value();
+ }
+ public void processDefault() {
+ e.aconst_null();
+ e.return_value();
+ }
+ });
+ e.end_method();
+ }
+
+ private void generatePut(Class type, final Map setters) {
+ final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, BEAN_MAP_PUT, null);
+ e.load_arg(0);
+ e.checkcast(Type.getType(type));
+ e.load_arg(1);
+ e.checkcast(Constants.TYPE_STRING);
+ EmitUtils.string_switch(e, getNames(setters), Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
+ public void processCase(Object key, Label end) {
+ PropertyDescriptor pd = (PropertyDescriptor)setters.get(key);
+ if (pd.getReadMethod() == null) {
+ e.aconst_null();
+ } else {
+ MethodInfo read = ReflectUtils.getMethodInfo(pd.getReadMethod());
+ e.dup();
+ e.invoke(read);
+ e.box(read.getSignature().getReturnType());
+ }
+ e.swap(); // move old value behind bean
+ e.load_arg(2); // new value
+ MethodInfo write = ReflectUtils.getMethodInfo(pd.getWriteMethod());
+ e.unbox(write.getSignature().getArgumentTypes()[0]);
+ e.invoke(write);
+ e.return_value();
+ }
+ public void processDefault() {
+ // fall-through
+ }
+ });
+ e.aconst_null();
+ e.return_value();
+ e.end_method();
+ }
+
+ private void generateKeySet(String[] allNames) {
+ // static initializer
+ declare_field(Constants.ACC_STATIC | Constants.ACC_PRIVATE, "keys", FIXED_KEY_SET, null);
+
+ CodeEmitter e = begin_static();
+ e.new_instance(FIXED_KEY_SET);
+ e.dup();
+ EmitUtils.push_array(e, allNames);
+ e.invoke_constructor(FIXED_KEY_SET, CSTRUCT_STRING_ARRAY);
+ e.putfield("keys");
+ e.return_value();
+ e.end_method();
+
+ // keySet
+ e = begin_method(Constants.ACC_PUBLIC, KEY_SET, null);
+ e.load_this();
+ e.getfield("keys");
+ e.return_value();
+ e.end_method();
+ }
+
+ private void generateGetPropertyType(final Map allProps, String[] allNames) {
+ final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, GET_PROPERTY_TYPE, null);
+ e.load_arg(0);
+ EmitUtils.string_switch(e, allNames, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
+ public void processCase(Object key, Label end) {
+ PropertyDescriptor pd = (PropertyDescriptor)allProps.get(key);
+ EmitUtils.load_class(e, Type.getType(pd.getPropertyType()));
+ e.return_value();
+ }
+ public void processDefault() {
+ e.aconst_null();
+ e.return_value();
+ }
+ });
+ e.end_method();
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BulkBean.java b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BulkBean.java
new file mode 100644
index 000000000..9d6946ba5
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BulkBean.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.beans;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.security.ProtectionDomain;
+import java.util.*;
+import com.fr.third.net.sf.cglib.core.*;
+import com.fr.third.org.objectweb.asm.ClassVisitor;
+
+/**
+ * @author Juozas Baliuka
+ */
+abstract public class BulkBean
+{
+ private static final BulkBeanKey KEY_FACTORY =
+ (BulkBeanKey)KeyFactory.create(BulkBeanKey.class);
+
+ interface BulkBeanKey {
+ public Object newInstance(String target, String[] getters, String[] setters, String[] types);
+ }
+
+ protected Class target;
+ protected String[] getters, setters;
+ protected Class[] types;
+
+ protected BulkBean() { }
+
+ abstract public void getPropertyValues(Object bean, Object[] values);
+ abstract public void setPropertyValues(Object bean, Object[] values);
+
+ public Object[] getPropertyValues(Object bean) {
+ Object[] values = new Object[getters.length];
+ getPropertyValues(bean, values);
+ return values;
+ }
+
+ public Class[] getPropertyTypes() {
+ return (Class[])types.clone();
+ }
+
+ public String[] getGetters() {
+ return (String[])getters.clone();
+ }
+
+ public String[] getSetters() {
+ return (String[])setters.clone();
+ }
+
+ public static BulkBean create(Class target, String[] getters, String[] setters, Class[] types) {
+ Generator gen = new Generator();
+ gen.setTarget(target);
+ gen.setGetters(getters);
+ gen.setSetters(setters);
+ gen.setTypes(types);
+ return gen.create();
+ }
+
+ public static class Generator extends AbstractClassGenerator {
+ private static final Source SOURCE = new Source(BulkBean.class.getName());
+ private Class target;
+ private String[] getters;
+ private String[] setters;
+ private Class[] types;
+
+ public Generator() {
+ super(SOURCE);
+ }
+
+ public void setTarget(Class target) {
+ this.target = target;
+ }
+
+ public void setGetters(String[] getters) {
+ this.getters = getters;
+ }
+
+ public void setSetters(String[] setters) {
+ this.setters = setters;
+ }
+
+ public void setTypes(Class[] types) {
+ this.types = types;
+ }
+
+ protected ClassLoader getDefaultClassLoader() {
+ return target.getClassLoader();
+ }
+
+ protected ProtectionDomain getProtectionDomain() {
+ return ReflectUtils.getProtectionDomain(target);
+ }
+
+ public BulkBean create() {
+ setNamePrefix(target.getName());
+ String targetClassName = target.getName();
+ String[] typeClassNames = ReflectUtils.getNames(types);
+ Object key = KEY_FACTORY.newInstance(targetClassName, getters, setters, typeClassNames);
+ return (BulkBean)super.create(key);
+ }
+
+ public void generateClass(ClassVisitor v) throws Exception {
+ new BulkBeanEmitter(v, getClassName(), target, getters, setters, types);
+ }
+
+ protected Object firstInstance(Class type) {
+ BulkBean instance = (BulkBean)ReflectUtils.newInstance(type);
+ instance.target = target;
+
+ int length = getters.length;
+ instance.getters = new String[length];
+ System.arraycopy(getters, 0, instance.getters, 0, length);
+
+ instance.setters = new String[length];
+ System.arraycopy(setters, 0, instance.setters, 0, length);
+
+ instance.types = new Class[types.length];
+ System.arraycopy(types, 0, instance.types, 0, types.length);
+
+ return instance;
+ }
+
+ protected Object nextInstance(Object instance) {
+ return instance;
+ }
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BulkBeanEmitter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BulkBeanEmitter.java
new file mode 100644
index 000000000..9aaf2b985
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BulkBeanEmitter.java
@@ -0,0 +1,156 @@
+/*
+ * 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 com.fr.third.net.sf.cglib.beans;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.*;
+import com.fr.third.net.sf.cglib.core.*;
+import com.fr.third.org.objectweb.asm.ClassVisitor;
+import com.fr.third.org.objectweb.asm.Type;
+
+class BulkBeanEmitter extends ClassEmitter {
+ private static final Signature GET_PROPERTY_VALUES =
+ TypeUtils.parseSignature("void getPropertyValues(Object, Object[])");
+ private static final Signature SET_PROPERTY_VALUES =
+ TypeUtils.parseSignature("void setPropertyValues(Object, Object[])");
+ private static final Signature CSTRUCT_EXCEPTION =
+ TypeUtils.parseConstructor("Throwable, int");
+ private static final Type BULK_BEAN =
+ TypeUtils.parseType("com.fr.third.net.sf.cglib.beans.BulkBean");
+ private static final Type BULK_BEAN_EXCEPTION =
+ TypeUtils.parseType("com.fr.third.net.sf.cglib.beans.BulkBeanException");
+
+ public BulkBeanEmitter(ClassVisitor v,
+ String className,
+ Class target,
+ String[] getterNames,
+ String[] setterNames,
+ Class[] types) {
+ super(v);
+
+ Method[] getters = new Method[getterNames.length];
+ Method[] setters = new Method[setterNames.length];
+ validate(target, getterNames, setterNames, types, getters, setters);
+
+ begin_class(Constants.V1_2, Constants.ACC_PUBLIC, className, BULK_BEAN, null, Constants.SOURCE_FILE);
+ EmitUtils.null_constructor(this);
+ generateGet(target, getters);
+ generateSet(target, setters);
+ end_class();
+ }
+
+ private void generateGet(final Class target, final Method[] getters) {
+ CodeEmitter e = begin_method(Constants.ACC_PUBLIC, GET_PROPERTY_VALUES, null);
+ if (getters.length >= 0) {
+ e.load_arg(0);
+ e.checkcast(Type.getType(target));
+ Local bean = e.make_local();
+ e.store_local(bean);
+ for (int i = 0; i < getters.length; i++) {
+ if (getters[i] != null) {
+ MethodInfo getter = ReflectUtils.getMethodInfo(getters[i]);
+ e.load_arg(1);
+ e.push(i);
+ e.load_local(bean);
+ e.invoke(getter);
+ e.box(getter.getSignature().getReturnType());
+ e.aastore();
+ }
+ }
+ }
+ e.return_value();
+ e.end_method();
+ }
+
+ private void generateSet(final Class target, final Method[] setters) {
+ // setPropertyValues
+ CodeEmitter e = begin_method(Constants.ACC_PUBLIC, SET_PROPERTY_VALUES, null);
+ if (setters.length > 0) {
+ Local index = e.make_local(Type.INT_TYPE);
+ e.push(0);
+ e.store_local(index);
+ e.load_arg(0);
+ e.checkcast(Type.getType(target));
+ e.load_arg(1);
+ Block handler = e.begin_block();
+ int lastIndex = 0;
+ for (int i = 0; i < setters.length; i++) {
+ if (setters[i] != null) {
+ MethodInfo setter = ReflectUtils.getMethodInfo(setters[i]);
+ int diff = i - lastIndex;
+ if (diff > 0) {
+ e.iinc(index, diff);
+ lastIndex = i;
+ }
+ e.dup2();
+ e.aaload(i);
+ e.unbox(setter.getSignature().getArgumentTypes()[0]);
+ e.invoke(setter);
+ }
+ }
+ handler.end();
+ e.return_value();
+ e.catch_exception(handler, Constants.TYPE_THROWABLE);
+ e.new_instance(BULK_BEAN_EXCEPTION);
+ e.dup_x1();
+ e.swap();
+ e.load_local(index);
+ e.invoke_constructor(BULK_BEAN_EXCEPTION, CSTRUCT_EXCEPTION);
+ e.athrow();
+ } else {
+ e.return_value();
+ }
+ e.end_method();
+ }
+
+ private static void validate(Class target,
+ String[] getters,
+ String[] setters,
+ Class[] types,
+ Method[] getters_out,
+ Method[] setters_out) {
+ int i = -1;
+ if (setters.length != types.length || getters.length != types.length) {
+ throw new BulkBeanException("accessor array length must be equal type array length", i);
+ }
+ try {
+ for (i = 0; i < types.length; i++) {
+ if (getters[i] != null) {
+ Method method = ReflectUtils.findDeclaredMethod(target, getters[i], null);
+ if (method.getReturnType() != types[i]) {
+ throw new BulkBeanException("Specified type " + types[i] +
+ " does not match declared type " + method.getReturnType(), i);
+ }
+ if (Modifier.isPrivate(method.getModifiers())) {
+ throw new BulkBeanException("Property is private", i);
+ }
+ getters_out[i] = method;
+ }
+ if (setters[i] != null) {
+ Method method = ReflectUtils.findDeclaredMethod(target, setters[i], new Class[]{ types[i] });
+ if (Modifier.isPrivate(method.getModifiers()) ){
+ throw new BulkBeanException("Property is private", i);
+ }
+ setters_out[i] = method;
+ }
+ }
+ } catch (NoSuchMethodException e) {
+ throw new BulkBeanException("Cannot find specified property", i);
+ }
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BulkBeanException.java b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BulkBeanException.java
new file mode 100644
index 000000000..34c8983b2
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/BulkBeanException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.beans;
+
+import com.fr.third.net.sf.cglib.core.CodeGenerationException;
+
+public class BulkBeanException extends RuntimeException
+{
+ private int index;
+ private Throwable cause;
+
+ public BulkBeanException(String message, int index) {
+ super(message);
+ this.index = index;
+ }
+
+ public BulkBeanException(Throwable cause, int index) {
+ super(cause.getMessage());
+ this.index = index;
+ this.cause = cause;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public Throwable getCause() {
+ return cause;
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/beans/FixedKeySet.java b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/FixedKeySet.java
new file mode 100644
index 000000000..1fac58f5d
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/FixedKeySet.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.beans;
+
+import java.util.*;
+
+public /* need it for class loading */ class FixedKeySet extends AbstractSet {
+ private Set set;
+ private int size;
+
+ public FixedKeySet(String[] keys) {
+ size = keys.length;
+ set = Collections.unmodifiableSet(new HashSet(Arrays.asList(keys)));
+ }
+
+ public Iterator iterator() {
+ return set.iterator();
+ }
+
+ public int size() {
+ return size;
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/beans/ImmutableBean.java b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/ImmutableBean.java
new file mode 100644
index 000000000..1424a1e28
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/beans/ImmutableBean.java
@@ -0,0 +1,128 @@
+/*
+ * 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 com.fr.third.net.sf.cglib.beans;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+import java.security.ProtectionDomain;
+import com.fr.third.net.sf.cglib.core.*;
+import com.fr.third.org.objectweb.asm.ClassVisitor;
+import com.fr.third.org.objectweb.asm.Type;
+/**
+ * @author Chris Nokleberg
+ */
+public class ImmutableBean
+{
+ private static final Type ILLEGAL_STATE_EXCEPTION =
+ TypeUtils.parseType("IllegalStateException");
+ private static final Signature CSTRUCT_OBJECT =
+ TypeUtils.parseConstructor("Object");
+ private static final Class[] OBJECT_CLASSES = { Object.class };
+ private static final String FIELD_NAME = "CGLIB$RWBean";
+
+ private ImmutableBean() {
+ }
+
+ public static Object 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(ImmutableBean.class.getName());
+ private Object bean;
+ private Class target;
+
+ public Generator() {
+ super(SOURCE);
+ }
+
+ public void setBean(Object bean) {
+ this.bean = bean;
+ target = bean.getClass();
+ }
+
+ protected ClassLoader getDefaultClassLoader() {
+ return target.getClassLoader();
+ }
+
+ protected ProtectionDomain getProtectionDomain() {
+ return ReflectUtils.getProtectionDomain(target);
+ }
+
+ public Object create() {
+ String name = target.getName();
+ setNamePrefix(name);
+ return super.create(name);
+ }
+
+ public void generateClass(ClassVisitor v) {
+ Type targetType = Type.getType(target);
+ ClassEmitter ce = new ClassEmitter(v);
+ ce.begin_class(Constants.V1_2,
+ Constants.ACC_PUBLIC,
+ getClassName(),
+ targetType,
+ null,
+ Constants.SOURCE_FILE);
+
+ ce.declare_field(Constants.ACC_FINAL | Constants.ACC_PRIVATE, FIELD_NAME, targetType, null);
+
+ CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT, null);
+ e.load_this();
+ e.super_invoke_constructor();
+ e.load_this();
+ e.load_arg(0);
+ e.checkcast(targetType);
+ e.putfield(FIELD_NAME);
+ e.return_value();
+ e.end_method();
+
+ PropertyDescriptor[] descriptors = ReflectUtils.getBeanProperties(target);
+ Method[] getters = ReflectUtils.getPropertyMethods(descriptors, true, false);
+ Method[] setters = ReflectUtils.getPropertyMethods(descriptors, false, true);
+
+ for (int i = 0; i < getters.length; i++) {
+ MethodInfo getter = ReflectUtils.getMethodInfo(getters[i]);
+ e = EmitUtils.begin_method(ce, getter, Constants.ACC_PUBLIC);
+ e.load_this();
+ e.getfield(FIELD_NAME);
+ e.invoke(getter);
+ e.return_value();
+ e.end_method();
+ }
+
+ for (int i = 0; i < setters.length; i++) {
+ MethodInfo setter = ReflectUtils.getMethodInfo(setters[i]);
+ e = EmitUtils.begin_method(ce, setter, Constants.ACC_PUBLIC);
+ e.throw_exception(ILLEGAL_STATE_EXCEPTION, "Bean is immutable");
+ e.end_method();
+ }
+
+ ce.end_class();
+ }
+
+ protected Object firstInstance(Class type) {
+ return ReflectUtils.newInstance(type, OBJECT_CLASSES, new Object[]{ bean });
+ }
+
+ // TODO: optimize
+ protected Object nextInstance(Object instance) {
+ return firstInstance(instance.getClass());
+ }
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/AbstractClassGenerator.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/AbstractClassGenerator.java
new file mode 100644
index 000000000..928004a9a
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/AbstractClassGenerator.java
@@ -0,0 +1,353 @@
+/*
+ * 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 com.fr.third.net.sf.cglib.core;
+
+import com.fr.third.net.sf.cglib.core.internal.Function;
+import com.fr.third.net.sf.cglib.core.internal.LoadingCache;
+import com.fr.third.org.objectweb.asm.ClassReader;
+
+import java.lang.ref.WeakReference;
+import java.security.ProtectionDomain;
+import java.util.Map;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.WeakHashMap;
+
+/**
+ * Abstract class for all code-generating CGLIB utilities.
+ * In addition to caching generated classes for performance, it provides hooks for
+ * customizing the ClassLoader, name of the generated class, and transformations
+ * applied before generation.
+ */
+abstract public class AbstractClassGenerator
+implements ClassGenerator
+{
+ private static final ThreadLocal CURRENT = new ThreadLocal();
+
+ private static volatile Map CACHE = new WeakHashMap();
+
+ private GeneratorStrategy strategy = DefaultGeneratorStrategy.INSTANCE;
+ private NamingPolicy namingPolicy = DefaultNamingPolicy.INSTANCE;
+ private Source source;
+ private ClassLoader classLoader;
+ private String namePrefix;
+ private Object key;
+ private boolean useCache = true;
+ private String className;
+ private boolean attemptLoad;
+
+ protected static class ClassLoaderData {
+ private final Set reservedClassNames = new HashSet();
+
+ /**
+ * {@link AbstractClassGenerator} here holds "cache key" (e.g. {@link com.fr.third.net.sf.cglib.proxy.Enhancer}
+ * configuration), and the value is the generated class plus some additional values
+ * (see {@link #unwrapCachedValue(Object)}.
+ *
The generated classes can be reused as long as their classloader is reachable.
+ *
Note: the only way to access a class is to find it through generatedClasses cache, thus
+ * the key should not expire as long as the class itself is alive (its classloader is alive).
+ */
+ private final LoadingCache generatedClasses;
+
+ /**
+ * Note: ClassLoaderData object is stored as a value of {@code WeakHashMap} thus
+ * this classLoader reference should be weak otherwise it would make classLoader strongly reachable
+ * and alive forever.
+ * Reference queue is not required since the cleanup is handled by {@link WeakHashMap}.
+ */
+ private final WeakReference classLoader;
+
+ private final Predicate uniqueNamePredicate = new Predicate() {
+ public boolean evaluate(Object name) {
+ return reservedClassNames.contains(name);
+ }
+ };
+
+ private static final Function GET_KEY = new Function() {
+ public Object apply(AbstractClassGenerator gen) {
+ return gen.key;
+ }
+ };
+
+ public ClassLoaderData(ClassLoader classLoader) {
+ if (classLoader == null) {
+ throw new IllegalArgumentException("classLoader == null is not yet supported");
+ }
+ this.classLoader = new WeakReference(classLoader);
+ Function load =
+ new Function() {
+ public Object apply(AbstractClassGenerator gen) {
+ Class klass = gen.generate(ClassLoaderData.this);
+ return gen.wrapCachedClass(klass);
+ }
+ };
+ generatedClasses = new LoadingCache(GET_KEY, load);
+ }
+
+ public ClassLoader getClassLoader() {
+ return classLoader.get();
+ }
+
+ public void reserveName(String name) {
+ reservedClassNames.add(name);
+ }
+
+ public Predicate getUniqueNamePredicate() {
+ return uniqueNamePredicate;
+ }
+
+ public Object get(AbstractClassGenerator gen, boolean useCache) {
+ if (!useCache) {
+ return gen.generate(ClassLoaderData.this);
+ } else {
+ Object cachedValue = generatedClasses.get(gen);
+ return gen.unwrapCachedValue(cachedValue);
+ }
+ }
+ }
+
+ protected T wrapCachedClass(Class klass) {
+ return (T) new WeakReference(klass);
+ }
+
+ protected Object unwrapCachedValue(T cached) {
+ return ((WeakReference) cached).get();
+ }
+
+ protected static class Source {
+ String name;
+ public Source(String name) {
+ this.name = name;
+ }
+ }
+
+ protected AbstractClassGenerator(Source source) {
+ this.source = source;
+ }
+
+ protected void setNamePrefix(String namePrefix) {
+ this.namePrefix = namePrefix;
+ }
+
+ final protected String getClassName() {
+ return className;
+ }
+
+ private void setClassName(String className) {
+ this.className = className;
+ }
+
+ private String generateClassName(Predicate nameTestPredicate) {
+ return namingPolicy.getClassName(namePrefix, source.name, key, nameTestPredicate);
+ }
+
+ /**
+ * Set the ClassLoader in which the class will be generated.
+ * Concrete subclasses of AbstractClassGenerator (such as Enhancer)
+ * will try to choose an appropriate default if this is unset.
+ *
+ * Classes are cached per-ClassLoader using a WeakHashMap, to allow
+ * the generated classes to be removed when the associated loader is garbage collected.
+ * @param classLoader the loader to generate the new class with, or null to use the default
+ */
+ public void setClassLoader(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ /**
+ * Override the default naming policy.
+ * @see DefaultNamingPolicy
+ * @param namingPolicy the custom policy, or null to use the default
+ */
+ public void setNamingPolicy(NamingPolicy namingPolicy) {
+ if (namingPolicy == null)
+ namingPolicy = DefaultNamingPolicy.INSTANCE;
+ this.namingPolicy = namingPolicy;
+ }
+
+ /**
+ * @see #setNamingPolicy
+ */
+ public NamingPolicy getNamingPolicy() {
+ return namingPolicy;
+ }
+
+ /**
+ * Whether use and update the static cache of generated classes
+ * for a class with the same properties. Default is true.
+ */
+ public void setUseCache(boolean useCache) {
+ this.useCache = useCache;
+ }
+
+ /**
+ * @see #setUseCache
+ */
+ public boolean getUseCache() {
+ return useCache;
+ }
+
+ /**
+ * If set, CGLIB will attempt to load classes from the specified
+ * ClassLoader before generating them. Because generated
+ * class names are not guaranteed to be unique, the default is false.
+ */
+ public void setAttemptLoad(boolean attemptLoad) {
+ this.attemptLoad = attemptLoad;
+ }
+
+ public boolean getAttemptLoad() {
+ return attemptLoad;
+ }
+
+ /**
+ * Set the strategy to use to create the bytecode from this generator.
+ * By default an instance of {@see DefaultGeneratorStrategy} is used.
+ */
+ public void setStrategy(GeneratorStrategy strategy) {
+ if (strategy == null)
+ strategy = DefaultGeneratorStrategy.INSTANCE;
+ this.strategy = strategy;
+ }
+
+ /**
+ * @see #setStrategy
+ */
+ public GeneratorStrategy getStrategy() {
+ return strategy;
+ }
+
+ /**
+ * Used internally by CGLIB. Returns the AbstractClassGenerator
+ * that is being used to generate a class in the current thread.
+ */
+ public static AbstractClassGenerator getCurrent() {
+ return (AbstractClassGenerator)CURRENT.get();
+ }
+
+ public ClassLoader getClassLoader() {
+ ClassLoader t = classLoader;
+ if (t == null) {
+ t = getDefaultClassLoader();
+ }
+ if (t == null) {
+ t = getClass().getClassLoader();
+ }
+ if (t == null) {
+ t = Thread.currentThread().getContextClassLoader();
+ }
+ if (t == null) {
+ throw new IllegalStateException("Cannot determine classloader");
+ }
+ return t;
+ }
+
+ abstract protected ClassLoader getDefaultClassLoader();
+
+ /**
+ * Returns the protection domain to use when defining the class.
+ *
+ * Default implementation returns null for using a default protection domain. Sub-classes may
+ * override to use a more specific protection domain.
+ *
+ *
+ * @return the protection domain (null for using a default)
+ */
+ protected ProtectionDomain getProtectionDomain() {
+ return null;
+ }
+
+ protected Object create(Object key) {
+ try {
+ ClassLoader loader = getClassLoader();
+ Map cache = CACHE;
+ ClassLoaderData data = cache.get(loader);
+ if (data == null) {
+ synchronized (AbstractClassGenerator.class) {
+ cache = CACHE;
+ data = cache.get(loader);
+ if (data == null) {
+ Map newCache = new WeakHashMap(cache);
+ data = new ClassLoaderData(loader);
+ newCache.put(loader, data);
+ CACHE = newCache;
+ }
+ }
+ }
+ this.key = key;
+ Object obj = data.get(this, getUseCache());
+ if (obj instanceof Class) {
+ return firstInstance((Class) obj);
+ }
+ return nextInstance(obj);
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Error e) {
+ throw e;
+ } catch (Exception e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+
+ protected Class generate(ClassLoaderData data) {
+ Class gen;
+ Object save = CURRENT.get();
+ CURRENT.set(this);
+ try {
+ ClassLoader classLoader = data.getClassLoader();
+ if (classLoader == null) {
+ throw new IllegalStateException("ClassLoader is null while trying to define class " +
+ getClassName() + ". It seems that the loader has been expired from a weak reference somehow. " +
+ "Please file an issue at cglib's issue tracker.");
+ }
+ synchronized (classLoader) {
+ String name = generateClassName(data.getUniqueNamePredicate());
+ data.reserveName(name);
+ this.setClassName(name);
+ }
+ if (attemptLoad) {
+ try {
+ gen = classLoader.loadClass(getClassName());
+ return gen;
+ } catch (ClassNotFoundException e) {
+ // ignore
+ }
+ }
+ byte[] b = strategy.generate(this);
+ String className = ClassNameReader.getClassName(new ClassReader(b));
+ ProtectionDomain protectionDomain = getProtectionDomain();
+ synchronized (classLoader) { // just in case
+ if (protectionDomain == null) {
+ gen = ReflectUtils.defineClass(className, b, classLoader);
+ } else {
+ gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain);
+ }
+ }
+ return gen;
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Error e) {
+ throw e;
+ } catch (Exception e) {
+ throw new CodeGenerationException(e);
+ } finally {
+ CURRENT.set(save);
+ }
+ }
+
+ abstract protected Object firstInstance(Class type) throws Exception;
+ abstract protected Object nextInstance(Object instance) throws Exception;
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/Block.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Block.java
new file mode 100644
index 000000000..44b8ea62b
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Block.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.core;
+
+import com.fr.third.org.objectweb.asm.Label;
+
+public class Block
+{
+ private CodeEmitter e;
+ private Label start;
+ private Label end;
+
+ public Block(CodeEmitter e) {
+ this.e = e;
+ start = e.mark();
+ }
+
+ public CodeEmitter getCodeEmitter() {
+ return e;
+ }
+
+ public void end() {
+ if (end != null) {
+ throw new IllegalStateException("end of label already set");
+ }
+ end = e.mark();
+ }
+
+ public Label getStart() {
+ return start;
+ }
+
+ public Label getEnd() {
+ return end;
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassEmitter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassEmitter.java
new file mode 100644
index 000000000..fd156dfd5
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassEmitter.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.core;
+
+import com.fr.third.net.sf.cglib.transform.ClassTransformer;
+
+import com.fr.third.org.objectweb.asm.ClassVisitor;
+import com.fr.third.org.objectweb.asm.FieldVisitor;
+import com.fr.third.org.objectweb.asm.MethodVisitor;
+import com.fr.third.org.objectweb.asm.Opcodes;
+import com.fr.third.org.objectweb.asm.Type;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Juozas Baliuka, Chris Nokleberg
+ */
+public class ClassEmitter extends ClassTransformer {
+ private ClassInfo classInfo;
+ private Map fieldInfo;
+
+ private static int hookCounter;
+ private MethodVisitor rawStaticInit;
+ private CodeEmitter staticInit;
+ private CodeEmitter staticHook;
+ private Signature staticHookSig;
+
+ public ClassEmitter(ClassVisitor cv) {
+ setTarget(cv);
+ }
+
+ public ClassEmitter() {
+ super(Opcodes.ASM6);
+ }
+
+ public void setTarget(ClassVisitor cv) {
+ this.cv = cv;
+ fieldInfo = new HashMap();
+
+ // just to be safe
+ staticInit = staticHook = null;
+ staticHookSig = null;
+ }
+
+ synchronized private static int getNextHook() {
+ return ++hookCounter;
+ }
+
+ public ClassInfo getClassInfo() {
+ return classInfo;
+ }
+
+ public void begin_class(int version, final int access, String className, final Type superType, final Type[] interfaces, String source) {
+ final Type classType = Type.getType("L" + className.replace('.', '/') + ";");
+ classInfo = new ClassInfo() {
+ public Type getType() {
+ return classType;
+ }
+ public Type getSuperType() {
+ return (superType != null) ? superType : Constants.TYPE_OBJECT;
+ }
+ public Type[] getInterfaces() {
+ return interfaces;
+ }
+ public int getModifiers() {
+ return access;
+ }
+ };
+ cv.visit(version,
+ access,
+ classInfo.getType().getInternalName(),
+ null,
+ classInfo.getSuperType().getInternalName(),
+ TypeUtils.toInternalNames(interfaces));
+ if (source != null)
+ cv.visitSource(source, null);
+ init();
+ }
+
+ public CodeEmitter getStaticHook() {
+ if (TypeUtils.isInterface(getAccess())) {
+ throw new IllegalStateException("static hook is invalid for this class");
+ }
+ if (staticHook == null) {
+ staticHookSig = new Signature("CGLIB$STATICHOOK" + getNextHook(), "()V");
+ staticHook = begin_method(Constants.ACC_STATIC,
+ staticHookSig,
+ null);
+ if (staticInit != null) {
+ staticInit.invoke_static_this(staticHookSig);
+ }
+ }
+ return staticHook;
+ }
+
+ protected void init() {
+ }
+
+ public int getAccess() {
+ return classInfo.getModifiers();
+ }
+
+ public Type getClassType() {
+ return classInfo.getType();
+ }
+
+ public Type getSuperType() {
+ return classInfo.getSuperType();
+ }
+
+ public void end_class() {
+ if (staticHook != null && staticInit == null) {
+ // force creation of static init
+ begin_static();
+ }
+ if (staticInit != null) {
+ staticHook.return_value();
+ staticHook.end_method();
+ rawStaticInit.visitInsn(Constants.RETURN);
+ rawStaticInit.visitMaxs(0, 0);
+ staticInit = staticHook = null;
+ staticHookSig = null;
+ }
+ cv.visitEnd();
+ }
+
+ public CodeEmitter begin_method(int access, Signature sig, Type[] exceptions) {
+ if (classInfo == null)
+ throw new IllegalStateException("classInfo is null! " + this);
+ MethodVisitor v = cv.visitMethod(access,
+ sig.getName(),
+ sig.getDescriptor(),
+ null,
+ TypeUtils.toInternalNames(exceptions));
+ if (sig.equals(Constants.SIG_STATIC) && !TypeUtils.isInterface(getAccess())) {
+ rawStaticInit = v;
+ MethodVisitor wrapped = new MethodVisitor(Opcodes.ASM6, v) {
+ public void visitMaxs(int maxStack, int maxLocals) {
+ // ignore
+ }
+ public void visitInsn(int insn) {
+ if (insn != Constants.RETURN) {
+ super.visitInsn(insn);
+ }
+ }
+ };
+ staticInit = new CodeEmitter(this, wrapped, access, sig, exceptions);
+ if (staticHook == null) {
+ // force static hook creation
+ getStaticHook();
+ } else {
+ staticInit.invoke_static_this(staticHookSig);
+ }
+ return staticInit;
+ } else if (sig.equals(staticHookSig)) {
+ return new CodeEmitter(this, v, access, sig, exceptions) {
+ public boolean isStaticHook() {
+ return true;
+ }
+ };
+ } else {
+ return new CodeEmitter(this, v, access, sig, exceptions);
+ }
+ }
+
+ public CodeEmitter begin_static() {
+ return begin_method(Constants.ACC_STATIC, Constants.SIG_STATIC, null);
+ }
+
+ public void declare_field(int access, String name, Type type, Object value) {
+ FieldInfo existing = (FieldInfo)fieldInfo.get(name);
+ FieldInfo info = new FieldInfo(access, name, type, value);
+ if (existing != null) {
+ if (!info.equals(existing)) {
+ throw new IllegalArgumentException("Field \"" + name + "\" has been declared differently");
+ }
+ } else {
+ fieldInfo.put(name, info);
+ cv.visitField(access, name, type.getDescriptor(), null, value);
+ }
+ }
+
+ // TODO: make public?
+ boolean isFieldDeclared(String name) {
+ return fieldInfo.get(name) != null;
+ }
+
+ FieldInfo getFieldInfo(String name) {
+ FieldInfo field = (FieldInfo)fieldInfo.get(name);
+ if (field == null) {
+ throw new IllegalArgumentException("Field " + name + " is not declared in " + getClassType().getClassName());
+ }
+ return field;
+ }
+
+ static class FieldInfo {
+ int access;
+ String name;
+ Type type;
+ Object value;
+
+ public FieldInfo(int access, String name, Type type, Object value) {
+ this.access = access;
+ this.name = name;
+ this.type = type;
+ this.value = value;
+ }
+
+ public boolean equals(Object o) {
+ if (o == null)
+ return false;
+ if (!(o instanceof FieldInfo))
+ return false;
+ FieldInfo other = (FieldInfo)o;
+ if (access != other.access ||
+ !name.equals(other.name) ||
+ !type.equals(other.type)) {
+ return false;
+ }
+ if ((value == null) ^ (other.value == null))
+ return false;
+ if (value != null && !value.equals(other.value))
+ return false;
+ return true;
+ }
+
+ public int hashCode() {
+ return access ^ name.hashCode() ^ type.hashCode() ^ ((value == null) ? 0 : value.hashCode());
+ }
+ }
+
+ public void visit(int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ begin_class(version,
+ access,
+ name.replace('/', '.'),
+ TypeUtils.fromInternalName(superName),
+ TypeUtils.fromInternalNames(interfaces),
+ null); // TODO
+ }
+
+ public void visitEnd() {
+ end_class();
+ }
+
+ public FieldVisitor visitField(int access,
+ String name,
+ String desc,
+ String signature,
+ Object value) {
+ declare_field(access, name, Type.getType(desc), value);
+ return null; // TODO
+ }
+
+ public MethodVisitor visitMethod(int access,
+ String name,
+ String desc,
+ String signature,
+ String[] exceptions) {
+ return begin_method(access,
+ new Signature(name, desc),
+ TypeUtils.fromInternalNames(exceptions));
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassGenerator.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassGenerator.java
new file mode 100644
index 000000000..e191dda61
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassGenerator.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.core;
+
+import com.fr.third.org.objectweb.asm.ClassVisitor;
+
+public interface ClassGenerator {
+ void generateClass(ClassVisitor v) throws Exception;
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassInfo.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassInfo.java
new file mode 100644
index 000000000..5f71d22fe
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassInfo.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 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 com.fr.third.net.sf.cglib.core;
+
+import com.fr.third.org.objectweb.asm.Attribute;
+import com.fr.third.org.objectweb.asm.Type;
+
+abstract public class ClassInfo {
+
+ protected ClassInfo() {
+ }
+
+ abstract public Type getType();
+ abstract public Type getSuperType();
+ abstract public Type[] getInterfaces();
+ abstract public int getModifiers();
+
+ public boolean equals(Object o) {
+ if (o == null)
+ return false;
+ if (!(o instanceof ClassInfo))
+ return false;
+ return getType().equals(((ClassInfo)o).getType());
+ }
+
+ public int hashCode() {
+ return getType().hashCode();
+ }
+
+ public String toString() {
+ // TODO: include modifiers, superType, interfaces
+ return getType().getClassName();
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassNameReader.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassNameReader.java
new file mode 100644
index 000000000..66fb320fa
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassNameReader.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.core;
+
+import com.fr.third.org.objectweb.asm.ClassReader;
+import com.fr.third.org.objectweb.asm.ClassVisitor;
+import com.fr.third.org.objectweb.asm.Opcodes;
+
+import java.util.*;
+
+// TODO: optimize (ClassReader buffers entire class before accept)
+public class ClassNameReader {
+ private ClassNameReader() {
+ }
+
+ private static final EarlyExitException EARLY_EXIT = new EarlyExitException();
+ private static class EarlyExitException extends RuntimeException { }
+
+ public static String getClassName(ClassReader r) {
+
+ return getClassInfo(r)[0];
+
+ }
+
+ public static String[] getClassInfo(ClassReader r) {
+ final List array = new ArrayList();
+ try {
+ r.accept(new ClassVisitor(Opcodes.ASM6, null) {
+ public void visit(int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ array.add( name.replace('/', '.') );
+ if(superName != null){
+ array.add( superName.replace('/', '.') );
+ }
+ for(int i = 0; i < interfaces.length; i++ ){
+ array.add( interfaces[i].replace('/', '.') );
+ }
+
+ throw EARLY_EXIT;
+ }
+ }, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+ } catch (EarlyExitException e) { }
+
+ return (String[])array.toArray( new String[]{} );
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassesKey.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassesKey.java
new file mode 100644
index 000000000..5edaa58ae
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ClassesKey.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.core;
+
+public class ClassesKey {
+ private static final Key FACTORY = (Key)KeyFactory.create(Key.class);
+
+ interface Key {
+ Object newInstance(Object[] array);
+ }
+
+ private ClassesKey() {
+ }
+
+ public static Object create(Object[] array) {
+ return FACTORY.newInstance(classNames(array));
+ }
+
+ private static String[] classNames(Object[] objects) {
+ if (objects == null) {
+ return null;
+ }
+ String[] classNames = new String[objects.length];
+ for (int i = 0; i < objects.length; i++) {
+ Object object = objects[i];
+ if (object != null) {
+ Class> aClass = object.getClass();
+ classNames[i] = aClass == null ? null : aClass.getName();
+ }
+ }
+ return classNames;
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/CodeEmitter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/CodeEmitter.java
new file mode 100644
index 000000000..5ac119198
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/CodeEmitter.java
@@ -0,0 +1,864 @@
+/*
+ * 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 com.fr.third.net.sf.cglib.core;
+
+import java.io.*;
+import java.util.*;
+import com.fr.third.org.objectweb.asm.*;
+
+/**
+ * @author Juozas Baliuka, Chris Nokleberg
+ */
+public class CodeEmitter extends LocalVariablesSorter {
+ private static final Signature BOOLEAN_VALUE =
+ TypeUtils.parseSignature("boolean booleanValue()");
+ private static final Signature CHAR_VALUE =
+ TypeUtils.parseSignature("char charValue()");
+ private static final Signature LONG_VALUE =
+ TypeUtils.parseSignature("long longValue()");
+ private static final Signature DOUBLE_VALUE =
+ TypeUtils.parseSignature("double doubleValue()");
+ private static final Signature FLOAT_VALUE =
+ TypeUtils.parseSignature("float floatValue()");
+ private static final Signature INT_VALUE =
+ TypeUtils.parseSignature("int intValue()");
+ private static final Signature CSTRUCT_NULL =
+ TypeUtils.parseConstructor("");
+ private static final Signature CSTRUCT_STRING =
+ TypeUtils.parseConstructor("String");
+
+ public static final int ADD = Constants.IADD;
+ public static final int MUL = Constants.IMUL;
+ public static final int XOR = Constants.IXOR;
+ public static final int USHR = Constants.IUSHR;
+ public static final int SUB = Constants.ISUB;
+ public static final int DIV = Constants.IDIV;
+ public static final int NEG = Constants.INEG;
+ public static final int REM = Constants.IREM;
+ public static final int AND = Constants.IAND;
+ public static final int OR = Constants.IOR;
+
+ public static final int GT = Constants.IFGT;
+ public static final int LT = Constants.IFLT;
+ public static final int GE = Constants.IFGE;
+ public static final int LE = Constants.IFLE;
+ public static final int NE = Constants.IFNE;
+ public static final int EQ = Constants.IFEQ;
+
+ private ClassEmitter ce;
+ private State state;
+
+ private static class State
+ extends MethodInfo
+ {
+ ClassInfo classInfo;
+ int access;
+ Signature sig;
+ Type[] argumentTypes;
+ int localOffset;
+ Type[] exceptionTypes;
+
+ State(ClassInfo classInfo, int access, Signature sig, Type[] exceptionTypes) {
+ this.classInfo = classInfo;
+ this.access = access;
+ this.sig = sig;
+ this.exceptionTypes = exceptionTypes;
+ localOffset = TypeUtils.isStatic(access) ? 0 : 1;
+ argumentTypes = sig.getArgumentTypes();
+ }
+
+ public ClassInfo getClassInfo() {
+ return classInfo;
+ }
+
+ public int getModifiers() {
+ return access;
+ }
+
+ public Signature getSignature() {
+ return sig;
+ }
+
+ public Type[] getExceptionTypes() {
+ return exceptionTypes;
+ }
+
+ public Attribute getAttribute() {
+ // TODO
+ return null;
+ }
+ }
+
+ CodeEmitter(ClassEmitter ce, MethodVisitor mv, int access, Signature sig, Type[] exceptionTypes) {
+ super(access, sig.getDescriptor(), mv);
+ this.ce = ce;
+ state = new State(ce.getClassInfo(), access, sig, exceptionTypes);
+ }
+
+ public CodeEmitter(CodeEmitter wrap) {
+ super(wrap);
+ this.ce = wrap.ce;
+ this.state = wrap.state;
+ }
+
+ public boolean isStaticHook() {
+ return false;
+ }
+
+ public Signature getSignature() {
+ return state.sig;
+ }
+
+ public Type getReturnType() {
+ return state.sig.getReturnType();
+ }
+
+ public MethodInfo getMethodInfo() {
+ return state;
+ }
+
+ public ClassEmitter getClassEmitter() {
+ return ce;
+ }
+
+ public void end_method() {
+ visitMaxs(0, 0);
+ }
+
+ public Block begin_block() {
+ return new Block(this);
+ }
+
+ public void catch_exception(Block block, Type exception) {
+ if (block.getEnd() == null) {
+ throw new IllegalStateException("end of block is unset");
+ }
+ mv.visitTryCatchBlock(block.getStart(),
+ block.getEnd(),
+ mark(),
+ exception.getInternalName());
+ }
+
+ public void goTo(Label label) { mv.visitJumpInsn(Constants.GOTO, label); }
+ public void ifnull(Label label) { mv.visitJumpInsn(Constants.IFNULL, label); }
+ public void ifnonnull(Label label) { mv.visitJumpInsn(Constants.IFNONNULL, label); }
+
+ public void if_jump(int mode, Label label) {
+ mv.visitJumpInsn(mode, label);
+ }
+
+ public void if_icmp(int mode, Label label) {
+ if_cmp(Type.INT_TYPE, mode, label);
+ }
+
+ public void if_cmp(Type type, int mode, Label label) {
+ int intOp = -1;
+ int jumpmode = mode;
+ switch (mode) {
+ case GE: jumpmode = LT; break;
+ case LE: jumpmode = GT; break;
+ }
+ switch (type.getSort()) {
+ case Type.LONG:
+ mv.visitInsn(Constants.LCMP);
+ break;
+ case Type.DOUBLE:
+ mv.visitInsn(Constants.DCMPG);
+ break;
+ case Type.FLOAT:
+ mv.visitInsn(Constants.FCMPG);
+ break;
+ case Type.ARRAY:
+ case Type.OBJECT:
+ switch (mode) {
+ case EQ:
+ mv.visitJumpInsn(Constants.IF_ACMPEQ, label);
+ return;
+ case NE:
+ mv.visitJumpInsn(Constants.IF_ACMPNE, label);
+ return;
+ }
+ throw new IllegalArgumentException("Bad comparison for type " + type);
+ default:
+ switch (mode) {
+ case EQ: intOp = Constants.IF_ICMPEQ; break;
+ case NE: intOp = Constants.IF_ICMPNE; break;
+ case GE: swap(); /* fall through */
+ case LT: intOp = Constants.IF_ICMPLT; break;
+ case LE: swap(); /* fall through */
+ case GT: intOp = Constants.IF_ICMPGT; break;
+ }
+ mv.visitJumpInsn(intOp, label);
+ return;
+ }
+ if_jump(jumpmode, label);
+ }
+
+ public void pop() { mv.visitInsn(Constants.POP); }
+ public void pop2() { mv.visitInsn(Constants.POP2); }
+ public void dup() { mv.visitInsn(Constants.DUP); }
+ public void dup2() { mv.visitInsn(Constants.DUP2); }
+ public void dup_x1() { mv.visitInsn(Constants.DUP_X1); }
+ public void dup_x2() { mv.visitInsn(Constants.DUP_X2); }
+ public void dup2_x1() { mv.visitInsn(Constants.DUP2_X1); }
+ public void dup2_x2() { mv.visitInsn(Constants.DUP2_X2); }
+ public void swap() { mv.visitInsn(Constants.SWAP); }
+ public void aconst_null() { mv.visitInsn(Constants.ACONST_NULL); }
+
+ public void swap(Type prev, Type type) {
+ if (type.getSize() == 1) {
+ if (prev.getSize() == 1) {
+ swap(); // same as dup_x1(), pop();
+ } else {
+ dup_x2();
+ pop();
+ }
+ } else {
+ if (prev.getSize() == 1) {
+ dup2_x1();
+ pop2();
+ } else {
+ dup2_x2();
+ pop2();
+ }
+ }
+ }
+
+ public void monitorenter() { mv.visitInsn(Constants.MONITORENTER); }
+ public void monitorexit() { mv.visitInsn(Constants.MONITOREXIT); }
+
+ public void math(int op, Type type) { mv.visitInsn(type.getOpcode(op)); }
+
+ public void array_load(Type type) { mv.visitInsn(type.getOpcode(Constants.IALOAD)); }
+ public void array_store(Type type) { mv.visitInsn(type.getOpcode(Constants.IASTORE)); }
+
+ /**
+ * Casts from one primitive numeric type to another
+ */
+ public void cast_numeric(Type from, Type to) {
+ if (from != to) {
+ if (from == Type.DOUBLE_TYPE) {
+ if (to == Type.FLOAT_TYPE) {
+ mv.visitInsn(Constants.D2F);
+ } else if (to == Type.LONG_TYPE) {
+ mv.visitInsn(Constants.D2L);
+ } else {
+ mv.visitInsn(Constants.D2I);
+ cast_numeric(Type.INT_TYPE, to);
+ }
+ } else if (from == Type.FLOAT_TYPE) {
+ if (to == Type.DOUBLE_TYPE) {
+ mv.visitInsn(Constants.F2D);
+ } else if (to == Type.LONG_TYPE) {
+ mv.visitInsn(Constants.F2L);
+ } else {
+ mv.visitInsn(Constants.F2I);
+ cast_numeric(Type.INT_TYPE, to);
+ }
+ } else if (from == Type.LONG_TYPE) {
+ if (to == Type.DOUBLE_TYPE) {
+ mv.visitInsn(Constants.L2D);
+ } else if (to == Type.FLOAT_TYPE) {
+ mv.visitInsn(Constants.L2F);
+ } else {
+ mv.visitInsn(Constants.L2I);
+ cast_numeric(Type.INT_TYPE, to);
+ }
+ } else {
+ if (to == Type.BYTE_TYPE) {
+ mv.visitInsn(Constants.I2B);
+ } else if (to == Type.CHAR_TYPE) {
+ mv.visitInsn(Constants.I2C);
+ } else if (to == Type.DOUBLE_TYPE) {
+ mv.visitInsn(Constants.I2D);
+ } else if (to == Type.FLOAT_TYPE) {
+ mv.visitInsn(Constants.I2F);
+ } else if (to == Type.LONG_TYPE) {
+ mv.visitInsn(Constants.I2L);
+ } else if (to == Type.SHORT_TYPE) {
+ mv.visitInsn(Constants.I2S);
+ }
+ }
+ }
+ }
+
+ public void push(int i) {
+ if (i < -1) {
+ mv.visitLdcInsn(new Integer(i));
+ } else if (i <= 5) {
+ mv.visitInsn(TypeUtils.ICONST(i));
+ } else if (i <= Byte.MAX_VALUE) {
+ mv.visitIntInsn(Constants.BIPUSH, i);
+ } else if (i <= Short.MAX_VALUE) {
+ mv.visitIntInsn(Constants.SIPUSH, i);
+ } else {
+ mv.visitLdcInsn(new Integer(i));
+ }
+ }
+
+ public void push(long value) {
+ if (value == 0L || value == 1L) {
+ mv.visitInsn(TypeUtils.LCONST(value));
+ } else {
+ mv.visitLdcInsn(new Long(value));
+ }
+ }
+
+ public void push(float value) {
+ if (value == 0f || value == 1f || value == 2f) {
+ mv.visitInsn(TypeUtils.FCONST(value));
+ } else {
+ mv.visitLdcInsn(new Float(value));
+ }
+ }
+ public void push(double value) {
+ if (value == 0d || value == 1d) {
+ mv.visitInsn(TypeUtils.DCONST(value));
+ } else {
+ mv.visitLdcInsn(new Double(value));
+ }
+ }
+
+ public void push(String value) {
+ mv.visitLdcInsn(value);
+ }
+
+ public void newarray() {
+ newarray(Constants.TYPE_OBJECT);
+ }
+
+ public void newarray(Type type) {
+ if (TypeUtils.isPrimitive(type)) {
+ mv.visitIntInsn(Constants.NEWARRAY, TypeUtils.NEWARRAY(type));
+ } else {
+ emit_type(Constants.ANEWARRAY, type);
+ }
+ }
+
+ public void arraylength() {
+ mv.visitInsn(Constants.ARRAYLENGTH);
+ }
+
+ public void load_this() {
+ if (TypeUtils.isStatic(state.access)) {
+ throw new IllegalStateException("no 'this' pointer within static method");
+ }
+ mv.visitVarInsn(Constants.ALOAD, 0);
+ }
+
+ /**
+ * Pushes all of the arguments of the current method onto the stack.
+ */
+ public void load_args() {
+ load_args(0, state.argumentTypes.length);
+ }
+
+ /**
+ * Pushes the specified argument of the current method onto the stack.
+ * @param index the zero-based index into the argument list
+ */
+ public void load_arg(int index) {
+ load_local(state.argumentTypes[index],
+ state.localOffset + skipArgs(index));
+ }
+
+ // zero-based (see load_this)
+ public void load_args(int fromArg, int count) {
+ int pos = state.localOffset + skipArgs(fromArg);
+ for (int i = 0; i < count; i++) {
+ Type t = state.argumentTypes[fromArg + i];
+ load_local(t, pos);
+ pos += t.getSize();
+ }
+ }
+
+ private int skipArgs(int numArgs) {
+ int amount = 0;
+ for (int i = 0; i < numArgs; i++) {
+ amount += state.argumentTypes[i].getSize();
+ }
+ return amount;
+ }
+
+ private void load_local(Type t, int pos) {
+ // TODO: make t == null ok?
+ mv.visitVarInsn(t.getOpcode(Constants.ILOAD), pos);
+ }
+
+ private void store_local(Type t, int pos) {
+ // TODO: make t == null ok?
+ mv.visitVarInsn(t.getOpcode(Constants.ISTORE), pos);
+ }
+
+ public void iinc(Local local, int amount) {
+ mv.visitIincInsn(local.getIndex(), amount);
+ }
+
+ public void store_local(Local local) {
+ store_local(local.getType(), local.getIndex());
+ }
+
+ public void load_local(Local local) {
+ load_local(local.getType(), local.getIndex());
+ }
+
+ public void return_value() {
+ mv.visitInsn(state.sig.getReturnType().getOpcode(Constants.IRETURN));
+ }
+
+ public void getfield(String name) {
+ ClassEmitter.FieldInfo info = ce.getFieldInfo(name);
+ int opcode = TypeUtils.isStatic(info.access) ? Constants.GETSTATIC : Constants.GETFIELD;
+ emit_field(opcode, ce.getClassType(), name, info.type);
+ }
+
+ public void putfield(String name) {
+ ClassEmitter.FieldInfo info = ce.getFieldInfo(name);
+ int opcode = TypeUtils.isStatic(info.access) ? Constants.PUTSTATIC : Constants.PUTFIELD;
+ emit_field(opcode, ce.getClassType(), name, info.type);
+ }
+
+ public void super_getfield(String name, Type type) {
+ emit_field(Constants.GETFIELD, ce.getSuperType(), name, type);
+ }
+
+ public void super_putfield(String name, Type type) {
+ emit_field(Constants.PUTFIELD, ce.getSuperType(), name, type);
+ }
+
+ public void super_getstatic(String name, Type type) {
+ emit_field(Constants.GETSTATIC, ce.getSuperType(), name, type);
+ }
+
+ public void super_putstatic(String name, Type type) {
+ emit_field(Constants.PUTSTATIC, ce.getSuperType(), name, type);
+ }
+
+ public void getfield(Type owner, String name, Type type) {
+ emit_field(Constants.GETFIELD, owner, name, type);
+ }
+
+ public void putfield(Type owner, String name, Type type) {
+ emit_field(Constants.PUTFIELD, owner, name, type);
+ }
+
+ public void getstatic(Type owner, String name, Type type) {
+ emit_field(Constants.GETSTATIC, owner, name, type);
+ }
+
+ public void putstatic(Type owner, String name, Type type) {
+ emit_field(Constants.PUTSTATIC, owner, name, type);
+ }
+
+ // package-protected for EmitUtils, try to fix
+ void emit_field(int opcode, Type ctype, String name, Type ftype) {
+ mv.visitFieldInsn(opcode,
+ ctype.getInternalName(),
+ name,
+ ftype.getDescriptor());
+ }
+
+ public void super_invoke() {
+ super_invoke(state.sig);
+ }
+
+ public void super_invoke(Signature sig) {
+ emit_invoke(Constants.INVOKESPECIAL, ce.getSuperType(), sig);
+ }
+
+ public void invoke_constructor(Type type) {
+ invoke_constructor(type, CSTRUCT_NULL);
+ }
+
+ public void super_invoke_constructor() {
+ invoke_constructor(ce.getSuperType());
+ }
+
+ public void invoke_constructor_this() {
+ invoke_constructor(ce.getClassType());
+ }
+
+ private void emit_invoke(int opcode, Type type, Signature sig) {
+ if (sig.getName().equals(Constants.CONSTRUCTOR_NAME) &&
+ ((opcode == Constants.INVOKEVIRTUAL) ||
+ (opcode == Constants.INVOKESTATIC))) {
+ // TODO: error
+ }
+ mv.visitMethodInsn(opcode,
+ type.getInternalName(),
+ sig.getName(),
+ sig.getDescriptor(),
+ opcode == Opcodes.INVOKEINTERFACE);
+ }
+
+ public void invoke_interface(Type owner, Signature sig) {
+ emit_invoke(Constants.INVOKEINTERFACE, owner, sig);
+ }
+
+ public void invoke_virtual(Type owner, Signature sig) {
+ emit_invoke(Constants.INVOKEVIRTUAL, owner, sig);
+ }
+
+ public void invoke_static(Type owner, Signature sig) {
+ emit_invoke(Constants.INVOKESTATIC, owner, sig);
+ }
+
+ public void invoke_virtual_this(Signature sig) {
+ invoke_virtual(ce.getClassType(), sig);
+ }
+
+ public void invoke_static_this(Signature sig) {
+ invoke_static(ce.getClassType(), sig);
+ }
+
+ public void invoke_constructor(Type type, Signature sig) {
+ emit_invoke(Constants.INVOKESPECIAL, type, sig);
+ }
+
+ public void invoke_constructor_this(Signature sig) {
+ invoke_constructor(ce.getClassType(), sig);
+ }
+
+ public void super_invoke_constructor(Signature sig) {
+ invoke_constructor(ce.getSuperType(), sig);
+ }
+
+ public void new_instance_this() {
+ new_instance(ce.getClassType());
+ }
+
+ public void new_instance(Type type) {
+ emit_type(Constants.NEW, type);
+ }
+
+ private void emit_type(int opcode, Type type) {
+ String desc;
+ if (TypeUtils.isArray(type)) {
+ desc = type.getDescriptor();
+ } else {
+ desc = type.getInternalName();
+ }
+ mv.visitTypeInsn(opcode, desc);
+ }
+
+ public void aaload(int index) {
+ push(index);
+ aaload();
+ }
+
+ public void aaload() { mv.visitInsn(Constants.AALOAD); }
+ public void aastore() { mv.visitInsn(Constants.AASTORE); }
+ public void athrow() { mv.visitInsn(Constants.ATHROW); }
+
+ public Label make_label() {
+ return new Label();
+ }
+
+ public Local make_local() {
+ return make_local(Constants.TYPE_OBJECT);
+ }
+
+ public Local make_local(Type type) {
+ return new Local(newLocal(type.getSize()), type);
+ }
+
+ public void checkcast_this() {
+ checkcast(ce.getClassType());
+ }
+
+ public void checkcast(Type type) {
+ if (!type.equals(Constants.TYPE_OBJECT)) {
+ emit_type(Constants.CHECKCAST, type);
+ }
+ }
+
+ public void instance_of(Type type) {
+ emit_type(Constants.INSTANCEOF, type);
+ }
+
+ public void instance_of_this() {
+ instance_of(ce.getClassType());
+ }
+
+ public void process_switch(int[] keys, ProcessSwitchCallback callback) {
+ float density;
+ if (keys.length == 0) {
+ density = 0;
+ } else {
+ density = (float)keys.length / (keys[keys.length - 1] - keys[0] + 1);
+ }
+ process_switch(keys, callback, density >= 0.5f);
+ }
+
+ public void process_switch(int[] keys, ProcessSwitchCallback callback, boolean useTable) {
+ if (!isSorted(keys))
+ throw new IllegalArgumentException("keys to switch must be sorted ascending");
+ Label def = make_label();
+ Label end = make_label();
+
+ try {
+ if (keys.length > 0) {
+ int len = keys.length;
+ int min = keys[0];
+ int max = keys[len - 1];
+ int range = max - min + 1;
+
+ if (useTable) {
+ Label[] labels = new Label[range];
+ Arrays.fill(labels, def);
+ for (int i = 0; i < len; i++) {
+ labels[keys[i] - min] = make_label();
+ }
+ mv.visitTableSwitchInsn(min, max, def, labels);
+ for (int i = 0; i < range; i++) {
+ Label label = labels[i];
+ if (label != def) {
+ mark(label);
+ callback.processCase(i + min, end);
+ }
+ }
+ } else {
+ Label[] labels = new Label[len];
+ for (int i = 0; i < len; i++) {
+ labels[i] = make_label();
+ }
+ mv.visitLookupSwitchInsn(def, keys, labels);
+ for (int i = 0; i < len; i++) {
+ mark(labels[i]);
+ callback.processCase(keys[i], end);
+ }
+ }
+ }
+
+ mark(def);
+ callback.processDefault();
+ mark(end);
+
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Error e) {
+ throw e;
+ } catch (Exception e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+
+ private static boolean isSorted(int[] keys) {
+ for (int i = 1; i < keys.length; i++) {
+ if (keys[i] < keys[i - 1])
+ return false;
+ }
+ return true;
+ }
+
+ public void mark(Label label) {
+ mv.visitLabel(label);
+ }
+
+ Label mark() {
+ Label label = make_label();
+ mv.visitLabel(label);
+ return label;
+ }
+
+ public void push(boolean value) {
+ push(value ? 1 : 0);
+ }
+
+ /**
+ * Toggles the integer on the top of the stack from 1 to 0 or vice versa
+ */
+ public void not() {
+ push(1);
+ math(XOR, Type.INT_TYPE);
+ }
+
+ public void throw_exception(Type type, String msg) {
+ new_instance(type);
+ dup();
+ push(msg);
+ invoke_constructor(type, CSTRUCT_STRING);
+ athrow();
+ }
+
+ /**
+ * If the argument is a primitive class, replaces the primitive value
+ * on the top of the stack with the wrapped (Object) equivalent. For
+ * example, char -> Character.
+ * If the class is Void, a null is pushed onto the stack instead.
+ * @param type the class indicating the current type of the top stack value
+ */
+ public void box(Type type) {
+ if (TypeUtils.isPrimitive(type)) {
+ if (type == Type.VOID_TYPE) {
+ aconst_null();
+ } else {
+ Type boxed = TypeUtils.getBoxedType(type);
+ new_instance(boxed);
+ if (type.getSize() == 2) {
+ // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o
+ dup_x2();
+ dup_x2();
+ pop();
+ } else {
+ // p -> po -> opo -> oop -> o
+ dup_x1();
+ swap();
+ }
+ invoke_constructor(boxed, new Signature(Constants.CONSTRUCTOR_NAME, Type.VOID_TYPE, new Type[]{ type }));
+ }
+ }
+ }
+
+ /**
+ * If the argument is a primitive class, replaces the object
+ * on the top of the stack with the unwrapped (primitive)
+ * equivalent. For example, Character -> char.
+ * @param type the class indicating the desired type of the top stack value
+ * @return true if the value was unboxed
+ */
+ public void unbox(Type type) {
+ Type t = Constants.TYPE_NUMBER;
+ Signature sig = null;
+ switch (type.getSort()) {
+ case Type.VOID:
+ return;
+ case Type.CHAR:
+ t = Constants.TYPE_CHARACTER;
+ sig = CHAR_VALUE;
+ break;
+ case Type.BOOLEAN:
+ t = Constants.TYPE_BOOLEAN;
+ sig = BOOLEAN_VALUE;
+ break;
+ case Type.DOUBLE:
+ sig = DOUBLE_VALUE;
+ break;
+ case Type.FLOAT:
+ sig = FLOAT_VALUE;
+ break;
+ case Type.LONG:
+ sig = LONG_VALUE;
+ break;
+ case Type.INT:
+ case Type.SHORT:
+ case Type.BYTE:
+ sig = INT_VALUE;
+ }
+
+ if (sig == null) {
+ checkcast(type);
+ } else {
+ checkcast(t);
+ invoke_virtual(t, sig);
+ }
+ }
+
+ /**
+ * Allocates and fills an Object[] array with the arguments to the
+ * current method. Primitive values are inserted as their boxed
+ * (Object) equivalents.
+ */
+ public void create_arg_array() {
+ /* generates:
+ Object[] args = new Object[]{ arg1, new Integer(arg2) };
+ */
+
+ push(state.argumentTypes.length);
+ newarray();
+ for (int i = 0; i < state.argumentTypes.length; i++) {
+ dup();
+ push(i);
+ load_arg(i);
+ box(state.argumentTypes[i]);
+ aastore();
+ }
+ }
+
+
+ /**
+ * Pushes a zero onto the stack if the argument is a primitive class, or a null otherwise.
+ */
+ public void zero_or_null(Type type) {
+ if (TypeUtils.isPrimitive(type)) {
+ switch (type.getSort()) {
+ case Type.DOUBLE:
+ push(0d);
+ break;
+ case Type.LONG:
+ push(0L);
+ break;
+ case Type.FLOAT:
+ push(0f);
+ break;
+ case Type.VOID:
+ aconst_null();
+ default:
+ push(0);
+ }
+ } else {
+ aconst_null();
+ }
+ }
+
+ /**
+ * Unboxes the object on the top of the stack. If the object is null, the
+ * unboxed primitive value becomes zero.
+ */
+ public void unbox_or_zero(Type type) {
+ if (TypeUtils.isPrimitive(type)) {
+ if (type != Type.VOID_TYPE) {
+ Label nonNull = make_label();
+ Label end = make_label();
+ dup();
+ ifnonnull(nonNull);
+ pop();
+ zero_or_null(type);
+ goTo(end);
+ mark(nonNull);
+ unbox(type);
+ mark(end);
+ }
+ } else {
+ checkcast(type);
+ }
+ }
+
+ public void visitMaxs(int maxStack, int maxLocals) {
+ if (!TypeUtils.isAbstract(state.access)) {
+ mv.visitMaxs(0, 0);
+ }
+ }
+
+ public void invoke(MethodInfo method, Type virtualType) {
+ ClassInfo classInfo = method.getClassInfo();
+ Type type = classInfo.getType();
+ Signature sig = method.getSignature();
+ if (sig.getName().equals(Constants.CONSTRUCTOR_NAME)) {
+ invoke_constructor(type, sig);
+ } else if (TypeUtils.isInterface(classInfo.getModifiers())) {
+ invoke_interface(type, sig);
+ } else if (TypeUtils.isStatic(method.getModifiers())) {
+ invoke_static(type, sig);
+ } else {
+ invoke_virtual(virtualType, sig);
+ }
+ }
+
+ public void invoke(MethodInfo method) {
+ invoke(method, method.getClassInfo().getType());
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/CodeGenerationException.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/CodeGenerationException.java
new file mode 100644
index 000000000..78ec6d3a4
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/CodeGenerationException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.core;
+
+/**
+ * @version $Id: CodeGenerationException.java,v 1.3 2004/06/24 21:15:21 herbyderby Exp $
+ */
+public class CodeGenerationException extends RuntimeException {
+ private Throwable cause;
+
+ public CodeGenerationException(Throwable cause) {
+ super(cause.getClass().getName() + "-->" + cause.getMessage());
+ this.cause = cause;
+ }
+
+ public Throwable getCause() {
+ return cause;
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/CollectionUtils.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/CollectionUtils.java
new file mode 100644
index 000000000..d9424f339
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/CollectionUtils.java
@@ -0,0 +1,76 @@
+/*
+ * 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 com.fr.third.net.sf.cglib.core;
+
+import java.util.*;
+import java.lang.reflect.Array;
+
+/**
+ * @author Chris Nokleberg
+ * @version $Id: CollectionUtils.java,v 1.7 2004/06/24 21:15:21 herbyderby Exp $
+ */
+public class CollectionUtils {
+ private CollectionUtils() { }
+
+ public static Map bucket(Collection c, Transformer t) {
+ Map buckets = new HashMap();
+ for (Iterator it = c.iterator(); it.hasNext();) {
+ Object value = (Object)it.next();
+ Object key = t.transform(value);
+ List bucket = (List)buckets.get(key);
+ if (bucket == null) {
+ buckets.put(key, bucket = new LinkedList());
+ }
+ bucket.add(value);
+ }
+ return buckets;
+ }
+
+ public static void reverse(Map source, Map target) {
+ for (Iterator it = source.keySet().iterator(); it.hasNext();) {
+ Object key = it.next();
+ target.put(source.get(key), key);
+ }
+ }
+
+ public static Collection filter(Collection c, Predicate p) {
+ Iterator it = c.iterator();
+ while (it.hasNext()) {
+ if (!p.evaluate(it.next())) {
+ it.remove();
+ }
+ }
+ return c;
+ }
+
+ public static List transform(Collection c, Transformer t) {
+ List result = new ArrayList(c.size());
+ for (Iterator it = c.iterator(); it.hasNext();) {
+ result.add(t.transform(it.next()));
+ }
+ return result;
+ }
+
+ public static Map getIndexMap(List list) {
+ Map indexes = new HashMap();
+ int index = 0;
+ for (Iterator it = list.iterator(); it.hasNext();) {
+ indexes.put(it.next(), new Integer(index++));
+ }
+ return indexes;
+ }
+}
+
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/Constants.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Constants.java
new file mode 100644
index 000000000..08da2d0cb
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Constants.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.core;
+
+import com.fr.third.org.objectweb.asm.Type;
+
+/**
+ * @author Juozas Baliuka baliuka@mwm.lt
+ * @version $Id: Constants.java,v 1.21 2006/03/05 02:43:19 herbyderby Exp $
+ */
+public interface Constants extends com.fr.third.org.objectweb.asm.Opcodes {
+ public static final Class[] EMPTY_CLASS_ARRAY = {};
+ public static final Type[] TYPES_EMPTY = {};
+
+ public static final Signature SIG_STATIC =
+ TypeUtils.parseSignature("void ()");
+
+ public static final Type TYPE_OBJECT_ARRAY = TypeUtils.parseType("Object[]");
+ public static final Type TYPE_CLASS_ARRAY = TypeUtils.parseType("Class[]");
+ public static final Type TYPE_STRING_ARRAY = TypeUtils.parseType("String[]");
+
+ public static final Type TYPE_OBJECT = TypeUtils.parseType("Object");
+ public static final Type TYPE_CLASS = TypeUtils.parseType("Class");
+ public static final Type TYPE_CLASS_LOADER = TypeUtils.parseType("ClassLoader");
+ public static final Type TYPE_CHARACTER = TypeUtils.parseType("Character");
+ public static final Type TYPE_BOOLEAN = TypeUtils.parseType("Boolean");
+ public static final Type TYPE_DOUBLE = TypeUtils.parseType("Double");
+ public static final Type TYPE_FLOAT = TypeUtils.parseType("Float");
+ public static final Type TYPE_LONG = TypeUtils.parseType("Long");
+ public static final Type TYPE_INTEGER = TypeUtils.parseType("Integer");
+ public static final Type TYPE_SHORT = TypeUtils.parseType("Short");
+ public static final Type TYPE_BYTE = TypeUtils.parseType("Byte");
+ public static final Type TYPE_NUMBER = TypeUtils.parseType("Number");
+ public static final Type TYPE_STRING = TypeUtils.parseType("String");
+ public static final Type TYPE_THROWABLE = TypeUtils.parseType("Throwable");
+ public static final Type TYPE_BIG_INTEGER = TypeUtils.parseType("java.math.BigInteger");
+ public static final Type TYPE_BIG_DECIMAL = TypeUtils.parseType("java.math.BigDecimal");
+ public static final Type TYPE_STRING_BUFFER = TypeUtils.parseType("StringBuffer");
+ public static final Type TYPE_RUNTIME_EXCEPTION = TypeUtils.parseType("RuntimeException");
+ public static final Type TYPE_ERROR = TypeUtils.parseType("Error");
+ public static final Type TYPE_SYSTEM = TypeUtils.parseType("System");
+ public static final Type TYPE_SIGNATURE = TypeUtils.parseType("com.fr.third.net.sf.cglib.core.Signature");
+ public static final Type TYPE_TYPE = Type.getType(Type.class);
+
+ public static final String CONSTRUCTOR_NAME = "";
+ public static final String STATIC_NAME = "";
+ public static final String SOURCE_FILE = "";
+ public static final String SUID_FIELD_NAME = "serialVersionUID";
+
+ public static final int PRIVATE_FINAL_STATIC = ACC_PRIVATE | ACC_FINAL | ACC_STATIC;
+
+ public static final int SWITCH_STYLE_TRIE = 0;
+ public static final int SWITCH_STYLE_HASH = 1;
+ public static final int SWITCH_STYLE_HASHONLY = 2;
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/Converter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Converter.java
new file mode 100644
index 000000000..6b8cbffeb
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Converter.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.core;
+
+public interface Converter {
+ Object convert(Object value, Class target, Object context);
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/Customizer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Customizer.java
new file mode 100644
index 000000000..e4e7df7d4
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Customizer.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.core;
+
+import com.fr.third.org.objectweb.asm.Type;
+
+/**
+ * Customizes key types for {@link KeyFactory} when building equals, hashCode, and toString.
+ * For customization of field types, use {@link FieldTypeCustomizer}
+ *
+ * @see KeyFactory#CLASS_BY_NAME
+ */
+public interface Customizer extends KeyFactoryCustomizer {
+ void customize(CodeEmitter e, Type type);
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/DebuggingClassWriter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/DebuggingClassWriter.java
new file mode 100644
index 000000000..a6706c4b6
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/DebuggingClassWriter.java
@@ -0,0 +1,114 @@
+/*
+ * 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 com.fr.third.net.sf.cglib.core;
+
+import com.fr.third.org.objectweb.asm.ClassWriter;
+import com.fr.third.org.objectweb.asm.ClassReader;
+import com.fr.third.org.objectweb.asm.ClassVisitor;
+import com.fr.third.org.objectweb.asm.Opcodes;
+
+import java.io.*;
+import java.lang.reflect.Constructor;
+
+public class DebuggingClassWriter extends ClassVisitor {
+
+ public static final String DEBUG_LOCATION_PROPERTY = "cglib.debugLocation";
+
+ private static String debugLocation;
+ private static Constructor traceCtor;
+
+ private String className;
+ private String superName;
+
+ static {
+ debugLocation = System.getProperty(DEBUG_LOCATION_PROPERTY);
+ if (debugLocation != null) {
+ System.err.println("CGLIB debugging enabled, writing to '" + debugLocation + "'");
+ try {
+ Class clazz = Class.forName("com.fr.third.org.objectweb.asm.util.TraceClassVisitor");
+ traceCtor = clazz.getConstructor(new Class[]{ClassVisitor.class, PrintWriter.class});
+ } catch (Throwable ignore) {
+ }
+ }
+ }
+
+ public DebuggingClassWriter(int flags) {
+ super(Opcodes.ASM6, new ClassWriter(flags));
+ }
+
+ public void visit(int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ className = name.replace('/', '.');
+ this.superName = superName.replace('/', '.');
+ super.visit(version, access, name, signature, superName, interfaces);
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public String getSuperName() {
+ return superName;
+ }
+
+ public byte[] toByteArray() {
+
+ return (byte[]) java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+
+
+ byte[] b = ((ClassWriter) DebuggingClassWriter.super.cv).toByteArray();
+ if (debugLocation != null) {
+ String dirs = className.replace('.', File.separatorChar);
+ try {
+ new File(debugLocation + File.separatorChar + dirs).getParentFile().mkdirs();
+
+ File file = new File(new File(debugLocation), dirs + ".class");
+ OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
+ try {
+ out.write(b);
+ } finally {
+ out.close();
+ }
+
+ if (traceCtor != null) {
+ file = new File(new File(debugLocation), dirs + ".asm");
+ out = new BufferedOutputStream(new FileOutputStream(file));
+ try {
+ ClassReader cr = new ClassReader(b);
+ PrintWriter pw = new PrintWriter(new OutputStreamWriter(out));
+ ClassVisitor tcv = (ClassVisitor)traceCtor.newInstance(new Object[]{null, pw});
+ cr.accept(tcv, 0);
+ pw.flush();
+ } finally {
+ out.close();
+ }
+ }
+ } catch (Exception e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+ return b;
+ }
+ });
+
+ }
+ }
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/DefaultGeneratorStrategy.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/DefaultGeneratorStrategy.java
new file mode 100644
index 000000000..2ec6b3016
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/DefaultGeneratorStrategy.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.core;
+
+import com.fr.third.org.objectweb.asm.ClassWriter;
+
+public class DefaultGeneratorStrategy implements GeneratorStrategy {
+ public static final DefaultGeneratorStrategy INSTANCE = new DefaultGeneratorStrategy();
+
+ public byte[] generate(ClassGenerator cg) throws Exception {
+ DebuggingClassWriter cw = getClassVisitor();
+ transform(cg).generateClass(cw);
+ return transform(cw.toByteArray());
+ }
+
+ protected DebuggingClassWriter getClassVisitor() throws Exception {
+ return new DebuggingClassWriter(ClassWriter.COMPUTE_FRAMES);
+ }
+
+ protected final ClassWriter getClassWriter() {
+ // Cause compile / runtime errors for people who implemented the old
+ // interface without using @Override
+ throw new UnsupportedOperationException("You are calling " +
+ "getClassWriter, which no longer exists in this cglib version.");
+ }
+
+ protected byte[] transform(byte[] b) throws Exception {
+ return b;
+ }
+
+ protected ClassGenerator transform(ClassGenerator cg) throws Exception {
+ return cg;
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/DefaultNamingPolicy.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/DefaultNamingPolicy.java
new file mode 100644
index 000000000..6609ef9fd
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/DefaultNamingPolicy.java
@@ -0,0 +1,71 @@
+/*
+ * 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 com.fr.third.net.sf.cglib.core;
+
+import java.util.Set;
+
+/**
+ * The default policy used by {@link AbstractClassGenerator}.
+ * Generates names such as
+ *
+ * This is composed of a prefix based on the name of the superclass, a fixed
+ * string incorporating the CGLIB class responsible for generation, and a
+ * hashcode derived from the parameters used to create the object. If the same
+ * name has been previously been used in the same ClassLoader, a
+ * suffix is added to ensure uniqueness.
+ */
+public class DefaultNamingPolicy implements NamingPolicy {
+ public static final DefaultNamingPolicy INSTANCE = new DefaultNamingPolicy();
+
+ /**
+ * This allows to test collisions of {@code key.hashCode()}.
+ */
+ private final static boolean STRESS_HASH_CODE = Boolean.getBoolean("com.fr.third.net.sf.cglib.test.stressHashCodes");
+
+ public String getClassName(String prefix, String source, Object key, Predicate names) {
+ if (prefix == null) {
+ prefix = "com.fr.third.net.sf.cglib.empty.Object";
+ } else if (prefix.startsWith("java")) {
+ prefix = "$" + prefix;
+ }
+ String base =
+ prefix + "$$" +
+ source.substring(source.lastIndexOf('.') + 1) +
+ getTag() + "$$" +
+ Integer.toHexString(STRESS_HASH_CODE ? 0 : key.hashCode());
+ String attempt = base;
+ int index = 2;
+ while (names.evaluate(attempt))
+ attempt = base + "_" + index++;
+ return attempt;
+ }
+
+ /**
+ * Returns a string which is incorporated into every generated class name.
+ * By default returns "ByCGLIB"
+ */
+ protected String getTag() {
+ return "ByCGLIB";
+ }
+
+ public int hashCode() {
+ return getTag().hashCode();
+ }
+
+ public boolean equals(Object o) {
+ return (o instanceof DefaultNamingPolicy) && ((DefaultNamingPolicy) o).getTag().equals(getTag());
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/DuplicatesPredicate.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/DuplicatesPredicate.java
new file mode 100644
index 000000000..67c2204ec
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/DuplicatesPredicate.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.core;
+
+import java.lang.reflect.Method;
+import java.util.*;
+
+public class DuplicatesPredicate implements Predicate {
+ private Set unique = new HashSet();
+
+ public boolean evaluate(Object arg) {
+ return unique.add(MethodWrapper.create((Method)arg));
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/EmitUtils.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/EmitUtils.java
new file mode 100644
index 000000000..ac1812b6d
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/EmitUtils.java
@@ -0,0 +1,960 @@
+/*
+ * 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 com.fr.third.net.sf.cglib.core;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.*;
+
+import com.fr.third.net.sf.cglib.core.internal.CustomizerRegistry;
+import com.fr.third.org.objectweb.asm.Label;
+import com.fr.third.org.objectweb.asm.Type;
+
+public class EmitUtils {
+ private static final Signature CSTRUCT_NULL =
+ TypeUtils.parseConstructor("");
+ private static final Signature CSTRUCT_THROWABLE =
+ TypeUtils.parseConstructor("Throwable");
+
+ private static final Signature GET_NAME =
+ TypeUtils.parseSignature("String getName()");
+ private static final Signature HASH_CODE =
+ TypeUtils.parseSignature("int hashCode()");
+ private static final Signature EQUALS =
+ TypeUtils.parseSignature("boolean equals(Object)");
+ private static final Signature STRING_LENGTH =
+ TypeUtils.parseSignature("int length()");
+ private static final Signature STRING_CHAR_AT =
+ TypeUtils.parseSignature("char charAt(int)");
+ private static final Signature FOR_NAME =
+ TypeUtils.parseSignature("Class forName(String)");
+ private static final Signature DOUBLE_TO_LONG_BITS =
+ TypeUtils.parseSignature("long doubleToLongBits(double)");
+ private static final Signature FLOAT_TO_INT_BITS =
+ TypeUtils.parseSignature("int floatToIntBits(float)");
+ private static final Signature TO_STRING =
+ TypeUtils.parseSignature("String toString()");
+ private static final Signature APPEND_STRING =
+ TypeUtils.parseSignature("StringBuffer append(String)");
+ private static final Signature APPEND_INT =
+ TypeUtils.parseSignature("StringBuffer append(int)");
+ private static final Signature APPEND_DOUBLE =
+ TypeUtils.parseSignature("StringBuffer append(double)");
+ private static final Signature APPEND_FLOAT =
+ TypeUtils.parseSignature("StringBuffer append(float)");
+ private static final Signature APPEND_CHAR =
+ TypeUtils.parseSignature("StringBuffer append(char)");
+ private static final Signature APPEND_LONG =
+ TypeUtils.parseSignature("StringBuffer append(long)");
+ private static final Signature APPEND_BOOLEAN =
+ TypeUtils.parseSignature("StringBuffer append(boolean)");
+ private static final Signature LENGTH =
+ TypeUtils.parseSignature("int length()");
+ private static final Signature SET_LENGTH =
+ TypeUtils.parseSignature("void setLength(int)");
+ private static final Signature GET_DECLARED_METHOD =
+ TypeUtils.parseSignature("java.lang.reflect.Method getDeclaredMethod(String, Class[])");
+
+
+
+ public static final ArrayDelimiters DEFAULT_DELIMITERS = new ArrayDelimiters("{", ", ", "}");
+
+ private EmitUtils() {
+ }
+
+ public static void factory_method(ClassEmitter ce, Signature sig) {
+ CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, sig, null);
+ e.new_instance_this();
+ e.dup();
+ e.load_args();
+ e.invoke_constructor_this(TypeUtils.parseConstructor(sig.getArgumentTypes()));
+ e.return_value();
+ e.end_method();
+ }
+
+ public static void null_constructor(ClassEmitter ce) {
+ CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, CSTRUCT_NULL, null);
+ e.load_this();
+ e.super_invoke_constructor();
+ e.return_value();
+ e.end_method();
+ }
+
+ /**
+ * Process an array on the stack. Assumes the top item on the stack
+ * is an array of the specified type. For each element in the array,
+ * puts the element on the stack and triggers the callback.
+ * @param type the type of the array (type.isArray() must be true)
+ * @param callback the callback triggered for each element
+ */
+ public static void process_array(CodeEmitter e, Type type, ProcessArrayCallback callback) {
+ Type componentType = TypeUtils.getComponentType(type);
+ Local array = e.make_local();
+ Local loopvar = e.make_local(Type.INT_TYPE);
+ Label loopbody = e.make_label();
+ Label checkloop = e.make_label();
+ e.store_local(array);
+ e.push(0);
+ e.store_local(loopvar);
+ e.goTo(checkloop);
+
+ e.mark(loopbody);
+ e.load_local(array);
+ e.load_local(loopvar);
+ e.array_load(componentType);
+ callback.processElement(componentType);
+ e.iinc(loopvar, 1);
+
+ e.mark(checkloop);
+ e.load_local(loopvar);
+ e.load_local(array);
+ e.arraylength();
+ e.if_icmp(e.LT, loopbody);
+ }
+
+ /**
+ * Process two arrays on the stack in parallel. Assumes the top two items on the stack
+ * are arrays of the specified class. The arrays must be the same length. For each pair
+ * of elements in the arrays, puts the pair on the stack and triggers the callback.
+ * @param type the type of the arrays (type.isArray() must be true)
+ * @param callback the callback triggered for each pair of elements
+ */
+ public static void process_arrays(CodeEmitter e, Type type, ProcessArrayCallback callback) {
+ Type componentType = TypeUtils.getComponentType(type);
+ Local array1 = e.make_local();
+ Local array2 = e.make_local();
+ Local loopvar = e.make_local(Type.INT_TYPE);
+ Label loopbody = e.make_label();
+ Label checkloop = e.make_label();
+ e.store_local(array1);
+ e.store_local(array2);
+ e.push(0);
+ e.store_local(loopvar);
+ e.goTo(checkloop);
+
+ e.mark(loopbody);
+ e.load_local(array1);
+ e.load_local(loopvar);
+ e.array_load(componentType);
+ e.load_local(array2);
+ e.load_local(loopvar);
+ e.array_load(componentType);
+ callback.processElement(componentType);
+ e.iinc(loopvar, 1);
+
+ e.mark(checkloop);
+ e.load_local(loopvar);
+ e.load_local(array1);
+ e.arraylength();
+ e.if_icmp(e.LT, loopbody);
+ }
+
+ public static void string_switch(CodeEmitter e, String[] strings, int switchStyle, ObjectSwitchCallback callback) {
+ try {
+ switch (switchStyle) {
+ case Constants.SWITCH_STYLE_TRIE:
+ string_switch_trie(e, strings, callback);
+ break;
+ case Constants.SWITCH_STYLE_HASH:
+ string_switch_hash(e, strings, callback, false);
+ break;
+ case Constants.SWITCH_STYLE_HASHONLY:
+ string_switch_hash(e, strings, callback, true);
+ break;
+ default:
+ throw new IllegalArgumentException("unknown switch style " + switchStyle);
+ }
+ } catch (RuntimeException ex) {
+ throw ex;
+ } catch (Error ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new CodeGenerationException(ex);
+ }
+ }
+
+ private static void string_switch_trie(final CodeEmitter e,
+ String[] strings,
+ final ObjectSwitchCallback callback) throws Exception {
+ final Label def = e.make_label();
+ final Label end = e.make_label();
+ final Map buckets = CollectionUtils.bucket(Arrays.asList(strings), new Transformer() {
+ public Object transform(Object value) {
+ return new Integer(((String)value).length());
+ }
+ });
+ e.dup();
+ e.invoke_virtual(Constants.TYPE_STRING, STRING_LENGTH);
+ e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() {
+ public void processCase(int key, Label ignore_end) throws Exception {
+ List bucket = (List)buckets.get(new Integer(key));
+ stringSwitchHelper(e, bucket, callback, def, end, 0);
+ }
+ public void processDefault() {
+ e.goTo(def);
+ }
+ });
+ e.mark(def);
+ e.pop();
+ callback.processDefault();
+ e.mark(end);
+ }
+
+ private static void stringSwitchHelper(final CodeEmitter e,
+ List strings,
+ final ObjectSwitchCallback callback,
+ final Label def,
+ final Label end,
+ final int index) throws Exception {
+ final int len = ((String)strings.get(0)).length();
+ final Map buckets = CollectionUtils.bucket(strings, new Transformer() {
+ public Object transform(Object value) {
+ return new Integer(((String)value).charAt(index));
+ }
+ });
+ e.dup();
+ e.push(index);
+ e.invoke_virtual(Constants.TYPE_STRING, STRING_CHAR_AT);
+ e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() {
+ public void processCase(int key, Label ignore_end) throws Exception {
+ List bucket = (List)buckets.get(new Integer(key));
+ if (index + 1 == len) {
+ e.pop();
+ callback.processCase(bucket.get(0), end);
+ } else {
+ stringSwitchHelper(e, bucket, callback, def, end, index + 1);
+ }
+ }
+ public void processDefault() {
+ e.goTo(def);
+ }
+ });
+ }
+
+ static int[] getSwitchKeys(Map buckets) {
+ int[] keys = new int[buckets.size()];
+ int index = 0;
+ for (Iterator it = buckets.keySet().iterator(); it.hasNext();) {
+ keys[index++] = ((Integer)it.next()).intValue();
+ }
+ Arrays.sort(keys);
+ return keys;
+ }
+
+ private static void string_switch_hash(final CodeEmitter e,
+ final String[] strings,
+ final ObjectSwitchCallback callback,
+ final boolean skipEquals) throws Exception {
+ final Map buckets = CollectionUtils.bucket(Arrays.asList(strings), new Transformer() {
+ public Object transform(Object value) {
+ return new Integer(value.hashCode());
+ }
+ });
+ final Label def = e.make_label();
+ final Label end = e.make_label();
+ e.dup();
+ e.invoke_virtual(Constants.TYPE_OBJECT, HASH_CODE);
+ e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() {
+ public void processCase(int key, Label ignore_end) throws Exception {
+ List bucket = (List)buckets.get(new Integer(key));
+ Label next = null;
+ if (skipEquals && bucket.size() == 1) {
+ if (skipEquals)
+ e.pop();
+ callback.processCase((String)bucket.get(0), end);
+ } else {
+ for (Iterator it = bucket.iterator(); it.hasNext();) {
+ String string = (String)it.next();
+ if (next != null) {
+ e.mark(next);
+ }
+ if (it.hasNext()) {
+ e.dup();
+ }
+ e.push(string);
+ e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS);
+ if (it.hasNext()) {
+ e.if_jump(e.EQ, next = e.make_label());
+ e.pop();
+ } else {
+ e.if_jump(e.EQ, def);
+ }
+ callback.processCase(string, end);
+ }
+ }
+ }
+ public void processDefault() {
+ e.pop();
+ }
+ });
+ e.mark(def);
+ callback.processDefault();
+ e.mark(end);
+ }
+
+ public static void load_class_this(CodeEmitter e) {
+ load_class_helper(e, e.getClassEmitter().getClassType());
+ }
+
+ public static void load_class(CodeEmitter e, Type type) {
+ if (TypeUtils.isPrimitive(type)) {
+ if (type == Type.VOID_TYPE) {
+ throw new IllegalArgumentException("cannot load void type");
+ }
+ e.getstatic(TypeUtils.getBoxedType(type), "TYPE", Constants.TYPE_CLASS);
+ } else {
+ load_class_helper(e, type);
+ }
+ }
+
+ private static void load_class_helper(CodeEmitter e, final Type type) {
+ if (e.isStaticHook()) {
+ // have to fall back on non-optimized load
+ e.push(TypeUtils.emulateClassGetName(type));
+ e.invoke_static(Constants.TYPE_CLASS, FOR_NAME);
+ } else {
+ ClassEmitter ce = e.getClassEmitter();
+ String typeName = TypeUtils.emulateClassGetName(type);
+
+ // TODO: can end up with duplicated field names when using chained transformers; incorporate static hook # somehow
+ String fieldName = "CGLIB$load_class$" + TypeUtils.escapeType(typeName);
+ if (!ce.isFieldDeclared(fieldName)) {
+ ce.declare_field(Constants.PRIVATE_FINAL_STATIC, fieldName, Constants.TYPE_CLASS, null);
+ CodeEmitter hook = ce.getStaticHook();
+ hook.push(typeName);
+ hook.invoke_static(Constants.TYPE_CLASS, FOR_NAME);
+ hook.putstatic(ce.getClassType(), fieldName, Constants.TYPE_CLASS);
+ }
+ e.getfield(fieldName);
+ }
+ }
+
+ public static void push_array(CodeEmitter e, Object[] array) {
+ e.push(array.length);
+ e.newarray(Type.getType(remapComponentType(array.getClass().getComponentType())));
+ for (int i = 0; i < array.length; i++) {
+ e.dup();
+ e.push(i);
+ push_object(e, array[i]);
+ e.aastore();
+ }
+ }
+
+ private static Class remapComponentType(Class componentType) {
+ if (componentType.equals(Type.class))
+ return Class.class;
+ return componentType;
+ }
+
+ public static void push_object(CodeEmitter e, Object obj) {
+ if (obj == null) {
+ e.aconst_null();
+ } else {
+ Class type = obj.getClass();
+ if (type.isArray()) {
+ push_array(e, (Object[])obj);
+ } else if (obj instanceof String) {
+ e.push((String)obj);
+ } else if (obj instanceof Type) {
+ load_class(e, (Type)obj);
+ } else if (obj instanceof Class) {
+ load_class(e, Type.getType((Class)obj));
+ } else if (obj instanceof BigInteger) {
+ e.new_instance(Constants.TYPE_BIG_INTEGER);
+ e.dup();
+ e.push(obj.toString());
+ e.invoke_constructor(Constants.TYPE_BIG_INTEGER);
+ } else if (obj instanceof BigDecimal) {
+ e.new_instance(Constants.TYPE_BIG_DECIMAL);
+ e.dup();
+ e.push(obj.toString());
+ e.invoke_constructor(Constants.TYPE_BIG_DECIMAL);
+ } else {
+ throw new IllegalArgumentException("unknown type: " + obj.getClass());
+ }
+ }
+ }
+
+ /**
+ * @deprecated use {@link #hash_code(CodeEmitter, Type, int, CustomizerRegistry)} instead
+ */
+ @Deprecated
+ public static void hash_code(CodeEmitter e, Type type, int multiplier, final Customizer customizer) {
+ hash_code(e, type, multiplier, CustomizerRegistry.singleton(customizer));
+ }
+
+ public static void hash_code(CodeEmitter e, Type type, int multiplier, final CustomizerRegistry registry) {
+ if (TypeUtils.isArray(type)) {
+ hash_array(e, type, multiplier, registry);
+ } else {
+ e.swap(Type.INT_TYPE, type);
+ e.push(multiplier);
+ e.math(e.MUL, Type.INT_TYPE);
+ e.swap(type, Type.INT_TYPE);
+ if (TypeUtils.isPrimitive(type)) {
+ hash_primitive(e, type);
+ } else {
+ hash_object(e, type, registry);
+ }
+ e.math(e.ADD, Type.INT_TYPE);
+ }
+ }
+
+ private static void hash_array(final CodeEmitter e, Type type, final int multiplier, final CustomizerRegistry registry) {
+ Label skip = e.make_label();
+ Label end = e.make_label();
+ e.dup();
+ e.ifnull(skip);
+ EmitUtils.process_array(e, type, new ProcessArrayCallback() {
+ public void processElement(Type type) {
+ hash_code(e, type, multiplier, registry);
+ }
+ });
+ e.goTo(end);
+ e.mark(skip);
+ e.pop();
+ e.mark(end);
+ }
+
+ private static void hash_object(CodeEmitter e, Type type, CustomizerRegistry registry) {
+ // (f == null) ? 0 : f.hashCode();
+ Label skip = e.make_label();
+ Label end = e.make_label();
+ e.dup();
+ e.ifnull(skip);
+ boolean customHashCode = false;
+ for (HashCodeCustomizer customizer : registry.get(HashCodeCustomizer.class)) {
+ if (customizer.customize(e, type)) {
+ customHashCode = true;
+ break;
+ }
+ }
+ if (!customHashCode) {
+ for (Customizer customizer : registry.get(Customizer.class)) {
+ customizer.customize(e, type);
+ }
+ e.invoke_virtual(Constants.TYPE_OBJECT, HASH_CODE);
+ }
+ e.goTo(end);
+ e.mark(skip);
+ e.pop();
+ e.push(0);
+ e.mark(end);
+ }
+
+ private static void hash_primitive(CodeEmitter e, Type type) {
+ switch (type.getSort()) {
+ case Type.BOOLEAN:
+ // f ? 0 : 1
+ e.push(1);
+ e.math(e.XOR, Type.INT_TYPE);
+ break;
+ case Type.FLOAT:
+ // Float.floatToIntBits(f)
+ e.invoke_static(Constants.TYPE_FLOAT, FLOAT_TO_INT_BITS);
+ break;
+ case Type.DOUBLE:
+ // Double.doubleToLongBits(f), hash_code(Long.TYPE)
+ e.invoke_static(Constants.TYPE_DOUBLE, DOUBLE_TO_LONG_BITS);
+ // fall through
+ case Type.LONG:
+ hash_long(e);
+ }
+ }
+
+ private static void hash_long(CodeEmitter e) {
+ // (int)(f ^ (f >>> 32))
+ e.dup2();
+ e.push(32);
+ e.math(e.USHR, Type.LONG_TYPE);
+ e.math(e.XOR, Type.LONG_TYPE);
+ e.cast_numeric(Type.LONG_TYPE, Type.INT_TYPE);
+ }
+
+// public static void not_equals(CodeEmitter e, Type type, Label notEquals) {
+// not_equals(e, type, notEquals, null);
+// }
+
+ /**
+ * @deprecated use {@link #not_equals(CodeEmitter, Type, Label, CustomizerRegistry)} instead
+ */
+ @Deprecated
+ public static void not_equals(CodeEmitter e, Type type, final Label notEquals, final Customizer customizer) {
+ not_equals(e, type, notEquals, CustomizerRegistry.singleton(customizer));
+ }
+
+ /**
+ * Branches to the specified label if the top two items on the stack
+ * are not equal. The items must both be of the specified
+ * class. Equality is determined by comparing primitive values
+ * directly and by invoking the equals method for
+ * Objects. Arrays are recursively processed in the same manner.
+ */
+ public static void not_equals(final CodeEmitter e, Type type, final Label notEquals, final CustomizerRegistry registry) {
+ (new ProcessArrayCallback() {
+ public void processElement(Type type) {
+ not_equals_helper(e, type, notEquals, registry, this);
+ }
+ }).processElement(type);
+ }
+
+ private static void not_equals_helper(CodeEmitter e,
+ Type type,
+ Label notEquals,
+ CustomizerRegistry registry,
+ ProcessArrayCallback callback) {
+ if (TypeUtils.isPrimitive(type)) {
+ e.if_cmp(type, e.NE, notEquals);
+ } else {
+ Label end = e.make_label();
+ nullcmp(e, notEquals, end);
+ if (TypeUtils.isArray(type)) {
+ Label checkContents = e.make_label();
+ e.dup2();
+ e.arraylength();
+ e.swap();
+ e.arraylength();
+ e.if_icmp(e.EQ, checkContents);
+ e.pop2();
+ e.goTo(notEquals);
+ e.mark(checkContents);
+ EmitUtils.process_arrays(e, type, callback);
+ } else {
+ List customizers = registry.get(Customizer.class);
+ if (!customizers.isEmpty()) {
+ for (Customizer customizer : customizers) {
+ customizer.customize(e, type);
+ }
+ e.swap();
+ for (Customizer customizer : customizers) {
+ customizer.customize(e, type);
+ }
+ }
+ e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS);
+ e.if_jump(e.EQ, notEquals);
+ }
+ e.mark(end);
+ }
+ }
+
+ /**
+ * If both objects on the top of the stack are non-null, does nothing.
+ * If one is null, or both are null, both are popped off and execution
+ * branches to the respective label.
+ * @param oneNull label to branch to if only one of the objects is null
+ * @param bothNull label to branch to if both of the objects are null
+ */
+ private static void nullcmp(CodeEmitter e, Label oneNull, Label bothNull) {
+ e.dup2();
+ Label nonNull = e.make_label();
+ Label oneNullHelper = e.make_label();
+ Label end = e.make_label();
+ e.ifnonnull(nonNull);
+ e.ifnonnull(oneNullHelper);
+ e.pop2();
+ e.goTo(bothNull);
+
+ e.mark(nonNull);
+ e.ifnull(oneNullHelper);
+ e.goTo(end);
+
+ e.mark(oneNullHelper);
+ e.pop2();
+ e.goTo(oneNull);
+
+ e.mark(end);
+ }
+
+ /*
+ public static void to_string(CodeEmitter e,
+ Type type,
+ ArrayDelimiters delims,
+ CustomizerRegistry registry) {
+ e.new_instance(Constants.TYPE_STRING_BUFFER);
+ e.dup();
+ e.invoke_constructor(Constants.TYPE_STRING_BUFFER);
+ e.swap();
+ append_string(e, type, delims, registry);
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, TO_STRING);
+ }
+ */
+
+ /**
+ * @deprecated use {@link #append_string(CodeEmitter, Type, ArrayDelimiters, CustomizerRegistry)} instead
+ */
+ @Deprecated
+ public static void append_string(final CodeEmitter e,
+ Type type,
+ final ArrayDelimiters delims,
+ final Customizer customizer) {
+ append_string(e, type, delims, CustomizerRegistry.singleton(customizer));
+ }
+
+ public static void append_string(final CodeEmitter e,
+ Type type,
+ final ArrayDelimiters delims,
+ final CustomizerRegistry registry) {
+ final ArrayDelimiters d = (delims != null) ? delims : DEFAULT_DELIMITERS;
+ ProcessArrayCallback callback = new ProcessArrayCallback() {
+ public void processElement(Type type) {
+ append_string_helper(e, type, d, registry, this);
+ e.push(d.inside);
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
+ }
+ };
+ append_string_helper(e, type, d, registry, callback);
+ }
+
+ private static void append_string_helper(CodeEmitter e,
+ Type type,
+ ArrayDelimiters delims,
+ CustomizerRegistry registry,
+ ProcessArrayCallback callback) {
+ Label skip = e.make_label();
+ Label end = e.make_label();
+ if (TypeUtils.isPrimitive(type)) {
+ switch (type.getSort()) {
+ case Type.INT:
+ case Type.SHORT:
+ case Type.BYTE:
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_INT);
+ break;
+ case Type.DOUBLE:
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_DOUBLE);
+ break;
+ case Type.FLOAT:
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_FLOAT);
+ break;
+ case Type.LONG:
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_LONG);
+ break;
+ case Type.BOOLEAN:
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_BOOLEAN);
+ break;
+ case Type.CHAR:
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_CHAR);
+ break;
+ }
+ } else if (TypeUtils.isArray(type)) {
+ e.dup();
+ e.ifnull(skip);
+ e.swap();
+ if (delims != null && delims.before != null && !"".equals(delims.before)) {
+ e.push(delims.before);
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
+ e.swap();
+ }
+ EmitUtils.process_array(e, type, callback);
+ shrinkStringBuffer(e, 2);
+ if (delims != null && delims.after != null && !"".equals(delims.after)) {
+ e.push(delims.after);
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
+ }
+ } else {
+ e.dup();
+ e.ifnull(skip);
+ for (Customizer customizer : registry.get(Customizer.class)) {
+ customizer.customize(e, type);
+ }
+ e.invoke_virtual(Constants.TYPE_OBJECT, TO_STRING);
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
+ }
+ e.goTo(end);
+ e.mark(skip);
+ e.pop();
+ e.push("null");
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
+ e.mark(end);
+ }
+
+ private static void shrinkStringBuffer(CodeEmitter e, int amt) {
+ e.dup();
+ e.dup();
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, LENGTH);
+ e.push(amt);
+ e.math(e.SUB, Type.INT_TYPE);
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, SET_LENGTH);
+ }
+
+ public static class ArrayDelimiters {
+ private String before;
+ private String inside;
+ private String after;
+
+ public ArrayDelimiters(String before, String inside, String after) {
+ this.before = before;
+ this.inside = inside;
+ this.after = after;
+ }
+ }
+
+ public static void load_method(CodeEmitter e, MethodInfo method) {
+ load_class(e, method.getClassInfo().getType());
+ e.push(method.getSignature().getName());
+ push_object(e, method.getSignature().getArgumentTypes());
+ e.invoke_virtual(Constants.TYPE_CLASS, GET_DECLARED_METHOD);
+ }
+
+ private interface ParameterTyper {
+ Type[] getParameterTypes(MethodInfo member);
+ }
+
+ public static void method_switch(CodeEmitter e,
+ List methods,
+ ObjectSwitchCallback callback) {
+ member_switch_helper(e, methods, callback, true);
+ }
+
+ public static void constructor_switch(CodeEmitter e,
+ List constructors,
+ ObjectSwitchCallback callback) {
+ member_switch_helper(e, constructors, callback, false);
+ }
+
+ private static void member_switch_helper(final CodeEmitter e,
+ List members,
+ final ObjectSwitchCallback callback,
+ boolean useName) {
+ try {
+ final Map cache = new HashMap();
+ final ParameterTyper cached = new ParameterTyper() {
+ public Type[] getParameterTypes(MethodInfo member) {
+ Type[] types = (Type[])cache.get(member);
+ if (types == null) {
+ cache.put(member, types = member.getSignature().getArgumentTypes());
+ }
+ return types;
+ }
+ };
+ final Label def = e.make_label();
+ final Label end = e.make_label();
+ if (useName) {
+ e.swap();
+ final Map buckets = CollectionUtils.bucket(members, new Transformer() {
+ public Object transform(Object value) {
+ return ((MethodInfo)value).getSignature().getName();
+ }
+ });
+ String[] names = (String[])buckets.keySet().toArray(new String[buckets.size()]);
+ EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
+ public void processCase(Object key, Label dontUseEnd) throws Exception {
+ member_helper_size(e, (List)buckets.get(key), callback, cached, def, end);
+ }
+ public void processDefault() throws Exception {
+ e.goTo(def);
+ }
+ });
+ } else {
+ member_helper_size(e, members, callback, cached, def, end);
+ }
+ e.mark(def);
+ e.pop();
+ callback.processDefault();
+ e.mark(end);
+ } catch (RuntimeException ex) {
+ throw ex;
+ } catch (Error ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new CodeGenerationException(ex);
+ }
+ }
+
+ private static void member_helper_size(final CodeEmitter e,
+ List members,
+ final ObjectSwitchCallback callback,
+ final ParameterTyper typer,
+ final Label def,
+ final Label end) throws Exception {
+ final Map buckets = CollectionUtils.bucket(members, new Transformer() {
+ public Object transform(Object value) {
+ return new Integer(typer.getParameterTypes((MethodInfo)value).length);
+ }
+ });
+ e.dup();
+ e.arraylength();
+ e.process_switch(EmitUtils.getSwitchKeys(buckets), new ProcessSwitchCallback() {
+ public void processCase(int key, Label dontUseEnd) throws Exception {
+ List bucket = (List)buckets.get(new Integer(key));
+ member_helper_type(e, bucket, callback, typer, def, end, new BitSet());
+ }
+ public void processDefault() throws Exception {
+ e.goTo(def);
+ }
+ });
+ }
+
+ private static void member_helper_type(final CodeEmitter e,
+ List members,
+ final ObjectSwitchCallback callback,
+ final ParameterTyper typer,
+ final Label def,
+ final Label end,
+ final BitSet checked) throws Exception {
+ if (members.size() == 1) {
+ MethodInfo member = (MethodInfo)members.get(0);
+ Type[] types = typer.getParameterTypes(member);
+ // need to check classes that have not already been checked via switches
+ for (int i = 0; i < types.length; i++) {
+ if (checked == null || !checked.get(i)) {
+ e.dup();
+ e.aaload(i);
+ e.invoke_virtual(Constants.TYPE_CLASS, GET_NAME);
+ e.push(TypeUtils.emulateClassGetName(types[i]));
+ e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS);
+ e.if_jump(e.EQ, def);
+ }
+ }
+ e.pop();
+ callback.processCase(member, end);
+ } else {
+ // choose the index that has the best chance of uniquely identifying member
+ Type[] example = typer.getParameterTypes((MethodInfo)members.get(0));
+ Map buckets = null;
+ int index = -1;
+ for (int i = 0; i < example.length; i++) {
+ final int j = i;
+ Map test = CollectionUtils.bucket(members, new Transformer() {
+ public Object transform(Object value) {
+ return TypeUtils.emulateClassGetName(typer.getParameterTypes((MethodInfo)value)[j]);
+ }
+ });
+ if (buckets == null || test.size() > buckets.size()) {
+ buckets = test;
+ index = i;
+ }
+ }
+ if (buckets == null || buckets.size() == 1) {
+ // TODO: switch by returnType
+ // must have two methods with same name, types, and different return types
+ e.goTo(def);
+ } else {
+ checked.set(index);
+
+ e.dup();
+ e.aaload(index);
+ e.invoke_virtual(Constants.TYPE_CLASS, GET_NAME);
+
+ final Map fbuckets = buckets;
+ String[] names = (String[])buckets.keySet().toArray(new String[buckets.size()]);
+ EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
+ public void processCase(Object key, Label dontUseEnd) throws Exception {
+ member_helper_type(e, (List)fbuckets.get(key), callback, typer, def, end, checked);
+ }
+ public void processDefault() throws Exception {
+ e.goTo(def);
+ }
+ });
+ }
+ }
+ }
+
+ public static void wrap_throwable(Block block, Type wrapper) {
+ CodeEmitter e = block.getCodeEmitter();
+ e.catch_exception(block, Constants.TYPE_THROWABLE);
+ e.new_instance(wrapper);
+ e.dup_x1();
+ e.swap();
+ e.invoke_constructor(wrapper, CSTRUCT_THROWABLE);
+ e.athrow();
+ }
+
+ public static void add_properties(ClassEmitter ce, String[] names, Type[] types) {
+ for (int i = 0; i < names.length; i++) {
+ String fieldName = "$cglib_prop_" + names[i];
+ ce.declare_field(Constants.ACC_PRIVATE, fieldName, types[i], null);
+ EmitUtils.add_property(ce, names[i], types[i], fieldName);
+ }
+ }
+
+ public static void add_property(ClassEmitter ce, String name, Type type, String fieldName) {
+ String property = TypeUtils.upperFirst(name);
+ CodeEmitter e;
+ e = ce.begin_method(Constants.ACC_PUBLIC,
+ new Signature("get" + property,
+ type,
+ Constants.TYPES_EMPTY),
+ null);
+ e.load_this();
+ e.getfield(fieldName);
+ e.return_value();
+ e.end_method();
+
+ e = ce.begin_method(Constants.ACC_PUBLIC,
+ new Signature("set" + property,
+ Type.VOID_TYPE,
+ new Type[]{ type }),
+ null);
+ e.load_this();
+ e.load_arg(0);
+ e.putfield(fieldName);
+ e.return_value();
+ e.end_method();
+ }
+
+ /* generates:
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Error e) {
+ throw e;
+ } catch ( e) {
+ throw e;
+ } catch (Throwable e) {
+ throw new (e);
+ }
+ */
+ public static void wrap_undeclared_throwable(CodeEmitter e, Block handler, Type[] exceptions, Type wrapper) {
+ Set set = (exceptions == null) ? Collections.EMPTY_SET : new HashSet(Arrays.asList(exceptions));
+
+ if (set.contains(Constants.TYPE_THROWABLE))
+ return;
+
+ boolean needThrow = exceptions != null;
+ if (!set.contains(Constants.TYPE_RUNTIME_EXCEPTION)) {
+ e.catch_exception(handler, Constants.TYPE_RUNTIME_EXCEPTION);
+ needThrow = true;
+ }
+ if (!set.contains(Constants.TYPE_ERROR)) {
+ e.catch_exception(handler, Constants.TYPE_ERROR);
+ needThrow = true;
+ }
+ if (exceptions != null) {
+ for (int i = 0; i < exceptions.length; i++) {
+ e.catch_exception(handler, exceptions[i]);
+ }
+ }
+ if (needThrow) {
+ e.athrow();
+ }
+ // e -> eo -> oeo -> ooe -> o
+ e.catch_exception(handler, Constants.TYPE_THROWABLE);
+ e.new_instance(wrapper);
+ e.dup_x1();
+ e.swap();
+ e.invoke_constructor(wrapper, CSTRUCT_THROWABLE);
+ e.athrow();
+ }
+
+ public static CodeEmitter begin_method(ClassEmitter e, MethodInfo method) {
+ return begin_method(e, method, method.getModifiers());
+ }
+
+ public static CodeEmitter begin_method(ClassEmitter e, MethodInfo method, int access) {
+ return e.begin_method(access,
+ method.getSignature(),
+ method.getExceptionTypes());
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/FieldTypeCustomizer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/FieldTypeCustomizer.java
new file mode 100644
index 000000000..36fa9ce3d
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/FieldTypeCustomizer.java
@@ -0,0 +1,23 @@
+package com.fr.third.net.sf.cglib.core;
+
+import com.fr.third.org.objectweb.asm.Type;
+
+/**
+ * Customizes key types for {@link KeyFactory} right in constructor.
+ */
+public interface FieldTypeCustomizer extends KeyFactoryCustomizer {
+ /**
+ * Customizes {@code this.FIELD_0 = ?} assignment in key constructor
+ * @param e code emitter
+ * @param index parameter index
+ * @param type parameter type
+ */
+ void customize(CodeEmitter e, int index, Type type);
+
+ /**
+ * Computes type of field for storing given parameter
+ * @param index parameter index
+ * @param type parameter type
+ */
+ Type getOutType(int index, Type type);
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/GeneratorStrategy.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/GeneratorStrategy.java
new file mode 100644
index 000000000..f4dca84e0
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/GeneratorStrategy.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.core;
+
+/**
+ * The GeneratorStrategyClass. By providing your
+ * own strategy you may examine or modify the generated class before
+ * it is loaded. Typically this will be accomplished by subclassing
+ * {@link DefaultGeneratorStrategy} and overriding the appropriate
+ * protected method.
+ * @see AbstractClassGenerator#setStrategy
+ */
+public interface GeneratorStrategy {
+ /**
+ * Generate the class.
+ * @param cg a class generator on which you can call {@link ClassGenerator#generateClass}
+ * @return a byte array containing the bits of a valid Class
+ */
+ byte[] generate(ClassGenerator cg) throws Exception;
+
+ /**
+ * The GeneratorStrategy in use does not currently, but may
+ * in the future, affect the caching of classes generated by {@link
+ * AbstractClassGenerator}, so this is a reminder that you should
+ * correctly implement equals and hashCode
+ * to avoid generating too many classes.
+ */
+ boolean equals(Object o);
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/HashCodeCustomizer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/HashCodeCustomizer.java
new file mode 100644
index 000000000..cbfcb93cf
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/HashCodeCustomizer.java
@@ -0,0 +1,12 @@
+package com.fr.third.net.sf.cglib.core;
+
+import com.fr.third.org.objectweb.asm.Type;
+
+public interface HashCodeCustomizer extends KeyFactoryCustomizer {
+ /**
+ * Customizes calculation of hashcode
+ * @param e code emitter
+ * @param type parameter type
+ */
+ boolean customize(CodeEmitter e, Type type);
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/KeyFactory.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/KeyFactory.java
new file mode 100644
index 000000000..f1d5cea1e
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/KeyFactory.java
@@ -0,0 +1,346 @@
+/*
+ * 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 com.fr.third.net.sf.cglib.core;
+
+import com.fr.third.net.sf.cglib.core.internal.CustomizerRegistry;
+import com.fr.third.org.objectweb.asm.ClassVisitor;
+import com.fr.third.org.objectweb.asm.Label;
+import com.fr.third.org.objectweb.asm.Type;
+
+import java.lang.reflect.Method;
+import java.security.ProtectionDomain;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Generates classes to handle multi-valued keys, for use in things such as Maps and Sets.
+ * Code for equals and hashCode methods follow the
+ * the rules laid out in Effective Java by Joshua Bloch.
+ *
+ * To generate a KeyFactory, you need to supply an interface which
+ * describes the structure of the key. The interface should have a
+ * single method named newInstance, which returns an
+ * Object. The arguments array can be
+ * anything--Objects, primitive values, or single or
+ * multi-dimension arrays of either. For example:
+ *
+ * private interface IntStringKey {
+ * public Object newInstance(int i, String s);
+ * }
+ *
+ * Once you have made a KeyFactory, you generate a new key by calling
+ * the newInstance method defined by your interface.
+ *
+ * Note:
+ * hashCode equality between two keys key1 and key2 is only guaranteed if
+ * key1.equals(key2)and the keys were produced by the same factory.
+ *
+ * @version $Id: KeyFactory.java,v 1.26 2006/03/05 02:43:19 herbyderby Exp $
+ */
+abstract public class KeyFactory {
+ private static final Signature GET_NAME =
+ TypeUtils.parseSignature("String getName()");
+ private static final Signature GET_CLASS =
+ TypeUtils.parseSignature("Class getClass()");
+ private static final Signature HASH_CODE =
+ TypeUtils.parseSignature("int hashCode()");
+ private static final Signature EQUALS =
+ TypeUtils.parseSignature("boolean equals(Object)");
+ private static final Signature TO_STRING =
+ TypeUtils.parseSignature("String toString()");
+ private static final Signature APPEND_STRING =
+ TypeUtils.parseSignature("StringBuffer append(String)");
+ private static final Type KEY_FACTORY =
+ TypeUtils.parseType("com.fr.third.net.sf.cglib.core.KeyFactory");
+ private static final Signature GET_SORT =
+ TypeUtils.parseSignature("int getSort()");
+
+ //generated numbers:
+ private final static int PRIMES[] = {
+ 11, 73, 179, 331,
+ 521, 787, 1213, 1823,
+ 2609, 3691, 5189, 7247,
+ 10037, 13931, 19289, 26627,
+ 36683, 50441, 69403, 95401,
+ 131129, 180179, 247501, 340057,
+ 467063, 641371, 880603, 1209107,
+ 1660097, 2279161, 3129011, 4295723,
+ 5897291, 8095873, 11114263, 15257791,
+ 20946017, 28754629, 39474179, 54189869,
+ 74391461, 102123817, 140194277, 192456917,
+ 264202273, 362693231, 497900099, 683510293,
+ 938313161, 1288102441, 1768288259 };
+
+
+ public static final Customizer CLASS_BY_NAME = new Customizer() {
+ public void customize(CodeEmitter e, Type type) {
+ if (type.equals(Constants.TYPE_CLASS)) {
+ e.invoke_virtual(Constants.TYPE_CLASS, GET_NAME);
+ }
+ }
+ };
+
+ public static final FieldTypeCustomizer STORE_CLASS_AS_STRING = new FieldTypeCustomizer() {
+ public void customize(CodeEmitter e, int index, Type type) {
+ if (type.equals(Constants.TYPE_CLASS)) {
+ e.invoke_virtual(Constants.TYPE_CLASS, GET_NAME);
+ }
+ }
+
+ public Type getOutType(int index, Type type) {
+ if (type.equals(Constants.TYPE_CLASS)) {
+ return Constants.TYPE_STRING;
+ }
+ return type;
+ }
+ };
+
+ /**
+ * {@link Type#hashCode()} is very expensive as it traverses full descriptor to calculate hash code.
+ * This customizer uses {@link Type#getSort()} as a hash code.
+ */
+ public static final HashCodeCustomizer HASH_ASM_TYPE = new HashCodeCustomizer() {
+ public boolean customize(CodeEmitter e, Type type) {
+ if (Constants.TYPE_TYPE.equals(type)) {
+ e.invoke_virtual(type, GET_SORT);
+ return true;
+ }
+ return false;
+ }
+ };
+
+ /**
+ * @deprecated this customizer might result in unexpected class leak since key object still holds a strong reference to the Object and class.
+ * It is recommended to have pre-processing method that would strip Objects and represent Classes as Strings
+ */
+ @Deprecated
+ public static final Customizer OBJECT_BY_CLASS = new Customizer() {
+ public void customize(CodeEmitter e, Type type) {
+ e.invoke_virtual(Constants.TYPE_OBJECT, GET_CLASS);
+ }
+ };
+
+ protected KeyFactory() {
+ }
+
+ public static KeyFactory create(Class keyInterface) {
+ return create(keyInterface, null);
+ }
+
+ public static KeyFactory create(Class keyInterface, Customizer customizer) {
+ return create(keyInterface.getClassLoader(), keyInterface, customizer);
+ }
+
+ public static KeyFactory create(Class keyInterface, KeyFactoryCustomizer first, List next) {
+ return create(keyInterface.getClassLoader(), keyInterface, first, next);
+ }
+
+ public static KeyFactory create(ClassLoader loader, Class keyInterface, Customizer customizer) {
+ return create(loader, keyInterface, customizer, Collections.emptyList());
+ }
+
+ public static KeyFactory create(ClassLoader loader, Class keyInterface, KeyFactoryCustomizer customizer,
+ List next) {
+ Generator gen = new Generator();
+ gen.setInterface(keyInterface);
+
+ if (customizer != null) {
+ gen.addCustomizer(customizer);
+ }
+ if (next != null && !next.isEmpty()) {
+ for (KeyFactoryCustomizer keyFactoryCustomizer : next) {
+ gen.addCustomizer(keyFactoryCustomizer);
+ }
+ }
+ gen.setClassLoader(loader);
+ return gen.create();
+ }
+
+ public static class Generator extends AbstractClassGenerator {
+ private static final Source SOURCE = new Source(KeyFactory.class.getName());
+ private static final Class[] KNOWN_CUSTOMIZER_TYPES = new Class[]{Customizer.class, FieldTypeCustomizer.class};
+
+ private Class keyInterface;
+ // TODO: Make me final when deprecated methods are removed
+ private CustomizerRegistry customizers = new CustomizerRegistry(KNOWN_CUSTOMIZER_TYPES);
+ private int constant;
+ private int multiplier;
+
+ public Generator() {
+ super(SOURCE);
+ }
+
+ protected ClassLoader getDefaultClassLoader() {
+ return keyInterface.getClassLoader();
+ }
+
+ protected ProtectionDomain getProtectionDomain() {
+ return ReflectUtils.getProtectionDomain(keyInterface);
+ }
+
+ /**
+ * @deprecated Use {@link #addCustomizer(KeyFactoryCustomizer)} instead.
+ */
+ @Deprecated
+ public void setCustomizer(Customizer customizer) {
+ customizers = CustomizerRegistry.singleton(customizer);
+ }
+
+ public void addCustomizer(KeyFactoryCustomizer customizer) {
+ customizers.add(customizer);
+ }
+
+ public List getCustomizers(Class klass) {
+ return customizers.get(klass);
+ }
+
+ public void setInterface(Class keyInterface) {
+ this.keyInterface = keyInterface;
+ }
+
+ public KeyFactory create() {
+ setNamePrefix(keyInterface.getName());
+ return (KeyFactory)super.create(keyInterface.getName());
+ }
+
+ public void setHashConstant(int constant) {
+ this.constant = constant;
+ }
+
+ public void setHashMultiplier(int multiplier) {
+ this.multiplier = multiplier;
+ }
+
+ protected Object firstInstance(Class type) {
+ return ReflectUtils.newInstance(type);
+ }
+
+ protected Object nextInstance(Object instance) {
+ return instance;
+ }
+
+ public void generateClass(ClassVisitor v) {
+ ClassEmitter ce = new ClassEmitter(v);
+
+ Method newInstance = ReflectUtils.findNewInstance(keyInterface);
+ if (!newInstance.getReturnType().equals(Object.class)) {
+ throw new IllegalArgumentException("newInstance method must return Object");
+ }
+
+ Type[] parameterTypes = TypeUtils.getTypes(newInstance.getParameterTypes());
+ ce.begin_class(Constants.V1_2,
+ Constants.ACC_PUBLIC,
+ getClassName(),
+ KEY_FACTORY,
+ new Type[]{ Type.getType(keyInterface) },
+ Constants.SOURCE_FILE);
+ EmitUtils.null_constructor(ce);
+ EmitUtils.factory_method(ce, ReflectUtils.getSignature(newInstance));
+
+ int seed = 0;
+ CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC,
+ TypeUtils.parseConstructor(parameterTypes),
+ null);
+ e.load_this();
+ e.super_invoke_constructor();
+ e.load_this();
+ List fieldTypeCustomizers = getCustomizers(FieldTypeCustomizer.class);
+ for (int i = 0; i < parameterTypes.length; i++) {
+ Type parameterType = parameterTypes[i];
+ Type fieldType = parameterType;
+ for (FieldTypeCustomizer customizer : fieldTypeCustomizers) {
+ fieldType = customizer.getOutType(i, fieldType);
+ }
+ seed += fieldType.hashCode();
+ ce.declare_field(Constants.ACC_PRIVATE | Constants.ACC_FINAL,
+ getFieldName(i),
+ fieldType,
+ null);
+ e.dup();
+ e.load_arg(i);
+ for (FieldTypeCustomizer customizer : fieldTypeCustomizers) {
+ customizer.customize(e, i, parameterType);
+ }
+ e.putfield(getFieldName(i));
+ }
+ e.return_value();
+ e.end_method();
+
+ // hash code
+ e = ce.begin_method(Constants.ACC_PUBLIC, HASH_CODE, null);
+ int hc = (constant != 0) ? constant : PRIMES[(int)(Math.abs(seed) % PRIMES.length)];
+ int hm = (multiplier != 0) ? multiplier : PRIMES[(int)(Math.abs(seed * 13) % PRIMES.length)];
+ e.push(hc);
+ for (int i = 0; i < parameterTypes.length; i++) {
+ e.load_this();
+ e.getfield(getFieldName(i));
+ EmitUtils.hash_code(e, parameterTypes[i], hm, customizers);
+ }
+ e.return_value();
+ e.end_method();
+
+ // equals
+ e = ce.begin_method(Constants.ACC_PUBLIC, EQUALS, null);
+ Label fail = e.make_label();
+ e.load_arg(0);
+ e.instance_of_this();
+ e.if_jump(e.EQ, fail);
+ for (int i = 0; i < parameterTypes.length; i++) {
+ e.load_this();
+ e.getfield(getFieldName(i));
+ e.load_arg(0);
+ e.checkcast_this();
+ e.getfield(getFieldName(i));
+ EmitUtils.not_equals(e, parameterTypes[i], fail, customizers);
+ }
+ e.push(1);
+ e.return_value();
+ e.mark(fail);
+ e.push(0);
+ e.return_value();
+ e.end_method();
+
+ // toString
+ e = ce.begin_method(Constants.ACC_PUBLIC, TO_STRING, null);
+ e.new_instance(Constants.TYPE_STRING_BUFFER);
+ e.dup();
+ e.invoke_constructor(Constants.TYPE_STRING_BUFFER);
+ for (int i = 0; i < parameterTypes.length; i++) {
+ if (i > 0) {
+ e.push(", ");
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
+ }
+ e.load_this();
+ e.getfield(getFieldName(i));
+ EmitUtils.append_string(e, parameterTypes[i], EmitUtils.DEFAULT_DELIMITERS, customizers);
+ }
+ e.invoke_virtual(Constants.TYPE_STRING_BUFFER, TO_STRING);
+ e.return_value();
+ e.end_method();
+
+ ce.end_class();
+ }
+
+ private String getFieldName(int arg) {
+ return "FIELD_" + arg;
+ }
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/KeyFactoryCustomizer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/KeyFactoryCustomizer.java
new file mode 100644
index 000000000..1339e145d
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/KeyFactoryCustomizer.java
@@ -0,0 +1,7 @@
+package com.fr.third.net.sf.cglib.core;
+
+/**
+ * Marker interface for customizers of {@link KeyFactory}
+ */
+public interface KeyFactoryCustomizer {
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/Local.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Local.java
new file mode 100644
index 000000000..d8959284e
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Local.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.core;
+
+import com.fr.third.org.objectweb.asm.Type;
+
+public class Local
+{
+ private Type type;
+ private int index;
+
+ public Local(int index, Type type) {
+ this.type = type;
+ this.index = index;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public Type getType() {
+ return type;
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/LocalVariablesSorter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/LocalVariablesSorter.java
new file mode 100644
index 000000000..f80ce99ca
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/LocalVariablesSorter.java
@@ -0,0 +1,158 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.fr.third.net.sf.cglib.core;
+
+import com.fr.third.org.objectweb.asm.Label;
+import com.fr.third.org.objectweb.asm.MethodVisitor;
+import com.fr.third.org.objectweb.asm.Opcodes;
+import com.fr.third.org.objectweb.asm.Type;
+
+/**
+ * A {@link MethodVisitor} that renumbers local variables in their order of
+ * appearance. This adapter allows one to easily add new local variables to a
+ * method.
+ *
+ * @author Chris Nokleberg
+ * @author Eric Bruneton
+ */
+public class LocalVariablesSorter extends MethodVisitor {
+
+ /**
+ * Mapping from old to new local variable indexes. A local variable at index
+ * i of size 1 is remapped to 'mapping[2*i]', while a local variable at
+ * index i of size 2 is remapped to 'mapping[2*i+1]'.
+ */
+ private static class State
+ {
+ int[] mapping = new int[40];
+ int nextLocal;
+ }
+
+ protected final int firstLocal;
+ private final State state;
+
+ public LocalVariablesSorter(
+ final int access,
+ final String desc,
+ final MethodVisitor mv)
+ {
+ super(Opcodes.ASM6, mv);
+ state = new State();
+ Type[] args = Type.getArgumentTypes(desc);
+ state.nextLocal = ((Opcodes.ACC_STATIC & access) != 0) ? 0 : 1;
+ for (int i = 0; i < args.length; i++) {
+ state.nextLocal += args[i].getSize();
+ }
+ firstLocal = state.nextLocal;
+ }
+
+ public LocalVariablesSorter(LocalVariablesSorter lvs) {
+ super(Opcodes.ASM6, lvs.mv);
+ state = lvs.state;
+ firstLocal = lvs.firstLocal;
+ }
+
+ public void visitVarInsn(final int opcode, final int var) {
+ int size;
+ switch (opcode) {
+ case Opcodes.LLOAD:
+ case Opcodes.LSTORE:
+ case Opcodes.DLOAD:
+ case Opcodes.DSTORE:
+ size = 2;
+ break;
+ default:
+ size = 1;
+ }
+ mv.visitVarInsn(opcode, remap(var, size));
+ }
+
+ public void visitIincInsn(final int var, final int increment) {
+ mv.visitIincInsn(remap(var, 1), increment);
+ }
+
+ public void visitMaxs(final int maxStack, final int maxLocals) {
+ mv.visitMaxs(maxStack, state.nextLocal);
+ }
+
+ public void visitLocalVariable(
+ final String name,
+ final String desc,
+ final String signature,
+ final Label start,
+ final Label end,
+ final int index)
+ {
+ mv.visitLocalVariable(name, desc, signature, start, end, remap(index));
+ }
+
+ // -------------
+
+ protected int newLocal(final int size) {
+ int var = state.nextLocal;
+ state.nextLocal += size;
+ return var;
+ }
+
+ private int remap(final int var, final int size) {
+ if (var < firstLocal) {
+ return var;
+ }
+ int key = 2 * var + size - 1;
+ int length = state.mapping.length;
+ if (key >= length) {
+ int[] newMapping = new int[Math.max(2 * length, key + 1)];
+ System.arraycopy(state.mapping, 0, newMapping, 0, length);
+ state.mapping = newMapping;
+ }
+ int value = state.mapping[key];
+ if (value == 0) {
+ value = state.nextLocal + 1;
+ state.mapping[key] = value;
+ state.nextLocal += size;
+ }
+ return value - 1;
+ }
+
+ private int remap(final int var) {
+ if (var < firstLocal) {
+ return var;
+ }
+ int key = 2 * var;
+ int value = key < state.mapping.length ? state.mapping[key] : 0;
+ if (value == 0) {
+ value = key + 1 < state.mapping.length ? state.mapping[key + 1] : 0;
+ }
+ if (value == 0) {
+ throw new IllegalStateException("Unknown local variable " + var);
+ }
+ return value - 1;
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/MethodInfo.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/MethodInfo.java
new file mode 100644
index 000000000..daee5afa3
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/MethodInfo.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 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 com.fr.third.net.sf.cglib.core;
+
+import com.fr.third.org.objectweb.asm.Attribute;
+import com.fr.third.org.objectweb.asm.Type;
+
+abstract public class MethodInfo {
+
+ protected MethodInfo() {
+ }
+
+ abstract public ClassInfo getClassInfo();
+ abstract public int getModifiers();
+ abstract public Signature getSignature();
+ abstract public Type[] getExceptionTypes();
+
+ public boolean equals(Object o) {
+ if (o == null)
+ return false;
+ if (!(o instanceof MethodInfo))
+ return false;
+ return getSignature().equals(((MethodInfo)o).getSignature());
+ }
+
+ public int hashCode() {
+ return getSignature().hashCode();
+ }
+
+ public String toString() {
+ // TODO: include modifiers, exceptions
+ return getSignature().toString();
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/MethodInfoTransformer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/MethodInfoTransformer.java
new file mode 100644
index 000000000..706af2cfe
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/MethodInfoTransformer.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 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 com.fr.third.net.sf.cglib.core;
+
+import java.lang.reflect.*;
+
+public class MethodInfoTransformer implements Transformer
+{
+ private static final MethodInfoTransformer INSTANCE = new MethodInfoTransformer();
+
+ public static MethodInfoTransformer getInstance() {
+ return INSTANCE;
+ }
+
+ public Object transform(Object value) {
+ if (value instanceof Method) {
+ return ReflectUtils.getMethodInfo((Method)value);
+ } else if (value instanceof Constructor) {
+ return ReflectUtils.getMethodInfo((Constructor)value);
+ } else {
+ throw new IllegalArgumentException("cannot get method info for " + value);
+ }
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/MethodWrapper.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/MethodWrapper.java
new file mode 100644
index 000000000..fac82d827
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/MethodWrapper.java
@@ -0,0 +1,46 @@
+/*
+ * 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 com.fr.third.net.sf.cglib.core;
+
+import java.lang.reflect.Method;
+import java.util.*;
+
+public class MethodWrapper {
+ private static final MethodWrapperKey KEY_FACTORY =
+ (MethodWrapperKey)KeyFactory.create(MethodWrapperKey.class);
+
+ /** Internal interface, only public due to ClassLoader issues. */
+ public interface MethodWrapperKey {
+ public Object newInstance(String name, String[] parameterTypes, String returnType);
+ }
+
+ private MethodWrapper() {
+ }
+
+ public static Object create(Method method) {
+ return KEY_FACTORY.newInstance(method.getName(),
+ ReflectUtils.getNames(method.getParameterTypes()),
+ method.getReturnType().getName());
+ }
+
+ public static Set createSet(Collection methods) {
+ Set set = new HashSet();
+ for (Iterator it = methods.iterator(); it.hasNext();) {
+ set.add(create((Method)it.next()));
+ }
+ return set;
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/NamingPolicy.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/NamingPolicy.java
new file mode 100644
index 000000000..d240cc4e1
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/NamingPolicy.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.core;
+
+import java.util.Set;
+
+/**
+ * Customize the generated class name for {@link AbstractClassGenerator}-based utilities.
+ */
+public interface NamingPolicy {
+ /**
+ * Choose a name for a generated class.
+ * @param prefix a dotted-name chosen by the generating class (possibly to put the generated class in a particular package)
+ * @param source the fully-qualified class name of the generating class (for example "com.fr.third.net.sf.cglib.Enhancer")
+ * @param key A key object representing the state of the parameters; for caching to work properly, equal keys should result
+ * in the same generated class name. The default policy incorporates key.hashCode() into the class name.
+ * @param names a predicate that returns true if the given classname has already been used in the same ClassLoader.
+ * @return the fully-qualified class name
+ */
+ String getClassName(String prefix, String source, Object key, Predicate names);
+
+ /**
+ * The NamingPolicy in use does not currently, but may
+ * in the future, affect the caching of classes generated by {@link
+ * AbstractClassGenerator}, so this is a reminder that you should
+ * correctly implement equals and hashCode
+ * to avoid generating too many classes.
+ */
+ boolean equals(Object o);
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/ObjectSwitchCallback.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ObjectSwitchCallback.java
new file mode 100644
index 000000000..0af9a429b
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ObjectSwitchCallback.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.core;
+
+import com.fr.third.org.objectweb.asm.Label;
+
+public interface ObjectSwitchCallback {
+ void processCase(Object key, Label end) throws Exception;
+ void processDefault() throws Exception;
+}
+
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/Predicate.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Predicate.java
new file mode 100644
index 000000000..9fcb746af
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Predicate.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.core;
+
+public interface Predicate {
+ boolean evaluate(Object arg);
+}
+
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/ProcessArrayCallback.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ProcessArrayCallback.java
new file mode 100644
index 000000000..153074815
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ProcessArrayCallback.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.core;
+
+import com.fr.third.org.objectweb.asm.Type;
+
+public interface ProcessArrayCallback {
+ void processElement(Type type);
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/ProcessSwitchCallback.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ProcessSwitchCallback.java
new file mode 100644
index 000000000..b3cbf7745
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ProcessSwitchCallback.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.core;
+
+import com.fr.third.org.objectweb.asm.Label;
+
+public interface ProcessSwitchCallback {
+ void processCase(int key, Label end) throws Exception;
+ void processDefault() throws Exception;
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/ReflectUtils.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ReflectUtils.java
new file mode 100644
index 000000000..e829b1834
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/ReflectUtils.java
@@ -0,0 +1,544 @@
+/*
+ * 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 com.fr.third.net.sf.cglib.core;
+
+import java.beans.*;
+import java.lang.reflect.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
+import java.security.ProtectionDomain;
+import java.util.*;
+import com.fr.third.org.objectweb.asm.Attribute;
+import com.fr.third.org.objectweb.asm.Type;
+
+/**
+ * @version $Id: ReflectUtils.java,v 1.30 2009/01/11 19:47:49 herbyderby Exp $
+ */
+public class ReflectUtils {
+ private ReflectUtils() { }
+
+ private static final Map primitives = new HashMap(8);
+ private static final Map transforms = new HashMap(8);
+ private static final ClassLoader defaultLoader = ReflectUtils.class.getClassLoader();
+ private static Method DEFINE_CLASS, DEFINE_CLASS_UNSAFE;
+ private static final ProtectionDomain PROTECTION_DOMAIN;
+ private static final Object UNSAFE;
+ private static final Throwable THROWABLE;
+
+ private static final List OBJECT_METHODS = new ArrayList();
+
+ static {
+ ProtectionDomain protectionDomain;
+ Method defineClass, defineClassUnsafe;
+ Object unsafe;
+ Throwable throwable = null;
+ try {
+ protectionDomain = getProtectionDomain(ReflectUtils.class);
+ try {
+ defineClass = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() {
+ public Object run() throws Exception {
+ Class loader = Class.forName("java.lang.ClassLoader"); // JVM crash w/o this
+ Method defineClass = loader.getDeclaredMethod("defineClass",
+ new Class[]{ String.class,
+ byte[].class,
+ Integer.TYPE,
+ Integer.TYPE,
+ ProtectionDomain.class });
+ defineClass.setAccessible(true);
+ return defineClass;
+ }
+ });
+ defineClassUnsafe = null;
+ unsafe = null;
+ } catch (Throwable t) {
+ // Fallback on Jigsaw where this method is not available.
+ throwable = t;
+ defineClass = null;
+ unsafe = AccessController.doPrivileged(new PrivilegedExceptionAction() {
+ public Object run() throws Exception {
+ Class u = Class.forName("sun.misc.Unsafe");
+ Field theUnsafe = u.getDeclaredField("theUnsafe");
+ theUnsafe.setAccessible(true);
+ return theUnsafe.get(null);
+ }
+ });
+ Class u = Class.forName("sun.misc.Unsafe");
+ defineClassUnsafe = u.getMethod("defineClass",
+ new Class[]{ String.class,
+ byte[].class,
+ Integer.TYPE,
+ Integer.TYPE,
+ ClassLoader.class,
+ ProtectionDomain.class });
+ }
+ AccessController.doPrivileged(new PrivilegedExceptionAction() {
+ public Object run() throws Exception {
+ Method[] methods = Object.class.getDeclaredMethods();
+ for (Method method : methods) {
+ if ("finalize".equals(method.getName())
+ || (method.getModifiers() & (Modifier.FINAL | Modifier.STATIC)) > 0) {
+ continue;
+ }
+ OBJECT_METHODS.add(method);
+ }
+ return null;
+ }
+ });
+ } catch (Throwable t) {
+ if (throwable == null) {
+ throwable = t;
+ }
+ protectionDomain = null;
+ defineClass = null;
+ defineClassUnsafe = null;
+ unsafe = null;
+ }
+ PROTECTION_DOMAIN = protectionDomain;
+ DEFINE_CLASS = defineClass;
+ DEFINE_CLASS_UNSAFE = defineClassUnsafe;
+ UNSAFE = unsafe;
+ THROWABLE = throwable;
+ }
+
+ private static final String[] CGLIB_PACKAGES = {
+ "java.lang",
+ };
+
+ static {
+ primitives.put("byte", Byte.TYPE);
+ primitives.put("char", Character.TYPE);
+ primitives.put("double", Double.TYPE);
+ primitives.put("float", Float.TYPE);
+ primitives.put("int", Integer.TYPE);
+ primitives.put("long", Long.TYPE);
+ primitives.put("short", Short.TYPE);
+ primitives.put("boolean", Boolean.TYPE);
+
+ transforms.put("byte", "B");
+ transforms.put("char", "C");
+ transforms.put("double", "D");
+ transforms.put("float", "F");
+ transforms.put("int", "I");
+ transforms.put("long", "J");
+ transforms.put("short", "S");
+ transforms.put("boolean", "Z");
+ }
+
+ public static ProtectionDomain getProtectionDomain(final Class source) {
+ if(source == null) {
+ return null;
+ }
+ return (ProtectionDomain)AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ return source.getProtectionDomain();
+ }
+ });
+ }
+
+ public static Type[] getExceptionTypes(Member member) {
+ if (member instanceof Method) {
+ return TypeUtils.getTypes(((Method)member).getExceptionTypes());
+ } else if (member instanceof Constructor) {
+ return TypeUtils.getTypes(((Constructor)member).getExceptionTypes());
+ } else {
+ throw new IllegalArgumentException("Cannot get exception types of a field");
+ }
+ }
+
+ public static Signature getSignature(Member member) {
+ if (member instanceof Method) {
+ return new Signature(member.getName(), Type.getMethodDescriptor((Method)member));
+ } else if (member instanceof Constructor) {
+ Type[] types = TypeUtils.getTypes(((Constructor)member).getParameterTypes());
+ return new Signature(Constants.CONSTRUCTOR_NAME,
+ Type.getMethodDescriptor(Type.VOID_TYPE, types));
+
+ } else {
+ throw new IllegalArgumentException("Cannot get signature of a field");
+ }
+ }
+
+ public static Constructor findConstructor(String desc) {
+ return findConstructor(desc, defaultLoader);
+ }
+
+ public static Constructor findConstructor(String desc, ClassLoader loader) {
+ try {
+ int lparen = desc.indexOf('(');
+ String className = desc.substring(0, lparen).trim();
+ return getClass(className, loader).getConstructor(parseTypes(desc, loader));
+ } catch (ClassNotFoundException e) {
+ throw new CodeGenerationException(e);
+ } catch (NoSuchMethodException e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+
+ public static Method findMethod(String desc) {
+ return findMethod(desc, defaultLoader);
+ }
+
+ public static Method findMethod(String desc, ClassLoader loader) {
+ try {
+ int lparen = desc.indexOf('(');
+ int dot = desc.lastIndexOf('.', lparen);
+ String className = desc.substring(0, dot).trim();
+ String methodName = desc.substring(dot + 1, lparen).trim();
+ return getClass(className, loader).getDeclaredMethod(methodName, parseTypes(desc, loader));
+ } catch (ClassNotFoundException e) {
+ throw new CodeGenerationException(e);
+ } catch (NoSuchMethodException e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+
+ private static Class[] parseTypes(String desc, ClassLoader loader) throws ClassNotFoundException {
+ int lparen = desc.indexOf('(');
+ int rparen = desc.indexOf(')', lparen);
+ List params = new ArrayList();
+ int start = lparen + 1;
+ for (;;) {
+ int comma = desc.indexOf(',', start);
+ if (comma < 0) {
+ break;
+ }
+ params.add(desc.substring(start, comma).trim());
+ start = comma + 1;
+ }
+ if (start < rparen) {
+ params.add(desc.substring(start, rparen).trim());
+ }
+ Class[] types = new Class[params.size()];
+ for (int i = 0; i < types.length; i++) {
+ types[i] = getClass((String)params.get(i), loader);
+ }
+ return types;
+ }
+
+ private static Class getClass(String className, ClassLoader loader) throws ClassNotFoundException {
+ return getClass(className, loader, CGLIB_PACKAGES);
+ }
+
+ private static Class getClass(String className, ClassLoader loader, String[] packages) throws ClassNotFoundException {
+ String save = className;
+ int dimensions = 0;
+ int index = 0;
+ while ((index = className.indexOf("[]", index) + 1) > 0) {
+ dimensions++;
+ }
+ StringBuffer brackets = new StringBuffer(className.length() - dimensions);
+ for (int i = 0; i < dimensions; i++) {
+ brackets.append('[');
+ }
+ className = className.substring(0, className.length() - 2 * dimensions);
+
+ String prefix = (dimensions > 0) ? brackets + "L" : "";
+ String suffix = (dimensions > 0) ? ";" : "";
+ try {
+ return Class.forName(prefix + className + suffix, false, loader);
+ } catch (ClassNotFoundException ignore) { }
+ for (int i = 0; i < packages.length; i++) {
+ try {
+ return Class.forName(prefix + packages[i] + '.' + className + suffix, false, loader);
+ } catch (ClassNotFoundException ignore) { }
+ }
+ if (dimensions == 0) {
+ Class c = (Class)primitives.get(className);
+ if (c != null) {
+ return c;
+ }
+ } else {
+ String transform = (String)transforms.get(className);
+ if (transform != null) {
+ try {
+ return Class.forName(brackets + transform, false, loader);
+ } catch (ClassNotFoundException ignore) { }
+ }
+ }
+ throw new ClassNotFoundException(save);
+ }
+
+
+ public static Object newInstance(Class type) {
+ return newInstance(type, Constants.EMPTY_CLASS_ARRAY, null);
+ }
+
+ public static Object newInstance(Class type, Class[] parameterTypes, Object[] args) {
+ return newInstance(getConstructor(type, parameterTypes), args);
+ }
+
+ public static Object newInstance(final Constructor cstruct, final Object[] args) {
+
+ boolean flag = cstruct.isAccessible();
+ try {
+ if (!flag) {
+ cstruct.setAccessible(true);
+ }
+ Object result = cstruct.newInstance(args);
+ return result;
+ } catch (InstantiationException e) {
+ throw new CodeGenerationException(e);
+ } catch (IllegalAccessException e) {
+ throw new CodeGenerationException(e);
+ } catch (InvocationTargetException e) {
+ throw new CodeGenerationException(e.getTargetException());
+ } finally {
+ if (!flag) {
+ cstruct.setAccessible(flag);
+ }
+ }
+
+ }
+
+ public static Constructor getConstructor(Class type, Class[] parameterTypes) {
+ try {
+ Constructor constructor = type.getDeclaredConstructor(parameterTypes);
+ constructor.setAccessible(true);
+ return constructor;
+ } catch (NoSuchMethodException e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+
+ public static String[] getNames(Class[] classes)
+ {
+ if (classes == null)
+ return null;
+ String[] names = new String[classes.length];
+ for (int i = 0; i < names.length; i++) {
+ names[i] = classes[i].getName();
+ }
+ return names;
+ }
+
+ public static Class[] getClasses(Object[] objects) {
+ Class[] classes = new Class[objects.length];
+ for (int i = 0; i < objects.length; i++) {
+ classes[i] = objects[i].getClass();
+ }
+ return classes;
+ }
+
+ public static Method findNewInstance(Class iface) {
+ Method m = findInterfaceMethod(iface);
+ if (!m.getName().equals("newInstance")) {
+ throw new IllegalArgumentException(iface + " missing newInstance method");
+ }
+ return m;
+ }
+
+ public static Method[] getPropertyMethods(PropertyDescriptor[] properties, boolean read, boolean write) {
+ Set methods = new HashSet();
+ for (int i = 0; i < properties.length; i++) {
+ PropertyDescriptor pd = properties[i];
+ if (read) {
+ methods.add(pd.getReadMethod());
+ }
+ if (write) {
+ methods.add(pd.getWriteMethod());
+ }
+ }
+ methods.remove(null);
+ return (Method[])methods.toArray(new Method[methods.size()]);
+ }
+
+ public static PropertyDescriptor[] getBeanProperties(Class type) {
+ return getPropertiesHelper(type, true, true);
+ }
+
+ public static PropertyDescriptor[] getBeanGetters(Class type) {
+ return getPropertiesHelper(type, true, false);
+ }
+
+ public static PropertyDescriptor[] getBeanSetters(Class type) {
+ return getPropertiesHelper(type, false, true);
+ }
+
+ private static PropertyDescriptor[] getPropertiesHelper(Class type, boolean read, boolean write) {
+ try {
+ BeanInfo info = Introspector.getBeanInfo(type, Object.class);
+ PropertyDescriptor[] all = info.getPropertyDescriptors();
+ if (read && write) {
+ return all;
+ }
+ List properties = new ArrayList(all.length);
+ for (int i = 0; i < all.length; i++) {
+ PropertyDescriptor pd = all[i];
+ if ((read && pd.getReadMethod() != null) ||
+ (write && pd.getWriteMethod() != null)) {
+ properties.add(pd);
+ }
+ }
+ return (PropertyDescriptor[])properties.toArray(new PropertyDescriptor[properties.size()]);
+ } catch (IntrospectionException e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+
+
+
+ public static Method findDeclaredMethod(final Class type,
+ final String methodName, final Class[] parameterTypes)
+ throws NoSuchMethodException {
+
+ Class cl = type;
+ while (cl != null) {
+ try {
+ return cl.getDeclaredMethod(methodName, parameterTypes);
+ } catch (NoSuchMethodException e) {
+ cl = cl.getSuperclass();
+ }
+ }
+ throw new NoSuchMethodException(methodName);
+
+ }
+
+ public static List addAllMethods(final Class type, final List list) {
+
+
+ if (type == Object.class) {
+ list.addAll(OBJECT_METHODS);
+ } else
+ list.addAll(java.util.Arrays.asList(type.getDeclaredMethods()));
+
+ Class superclass = type.getSuperclass();
+ if (superclass != null) {
+ addAllMethods(superclass, list);
+ }
+ Class[] interfaces = type.getInterfaces();
+ for (int i = 0; i < interfaces.length; i++) {
+ addAllMethods(interfaces[i], list);
+ }
+
+ return list;
+ }
+
+ public static List addAllInterfaces(Class type, List list) {
+ Class superclass = type.getSuperclass();
+ if (superclass != null) {
+ list.addAll(Arrays.asList(type.getInterfaces()));
+ addAllInterfaces(superclass, list);
+ }
+ return list;
+ }
+
+
+ public static Method findInterfaceMethod(Class iface) {
+ if (!iface.isInterface()) {
+ throw new IllegalArgumentException(iface + " is not an interface");
+ }
+ Method[] methods = iface.getDeclaredMethods();
+ if (methods.length != 1) {
+ throw new IllegalArgumentException("expecting exactly 1 method in " + iface);
+ }
+ return methods[0];
+ }
+
+ public static Class defineClass(String className, byte[] b, ClassLoader loader) throws Exception {
+ return defineClass(className, b, loader, PROTECTION_DOMAIN);
+ }
+
+ public static Class defineClass(String className, byte[] b, ClassLoader loader, ProtectionDomain protectionDomain) throws Exception {
+ Class c;
+ if (DEFINE_CLASS != null) {
+ Object[] args = new Object[]{className, b, new Integer(0), new Integer(b.length), protectionDomain };
+ c = (Class)DEFINE_CLASS.invoke(loader, args);
+ } else if (DEFINE_CLASS_UNSAFE != null) {
+ Object[] args = new Object[]{className, b, new Integer(0), new Integer(b.length), loader, protectionDomain };
+ c = (Class)DEFINE_CLASS_UNSAFE.invoke(UNSAFE, args);
+ } else {
+ throw new CodeGenerationException(THROWABLE);
+ }
+ // Force static initializers to run.
+ Class.forName(className, true, loader);
+ return c;
+ }
+
+ public static int findPackageProtected(Class[] classes) {
+ for (int i = 0; i < classes.length; i++) {
+ if (!Modifier.isPublic(classes[i].getModifiers())) {
+ return i;
+ }
+ }
+ return 0;
+ }
+
+ public static MethodInfo getMethodInfo(final Member member, final int modifiers) {
+ final Signature sig = getSignature(member);
+ return new MethodInfo() {
+ private ClassInfo ci;
+ public ClassInfo getClassInfo() {
+ if (ci == null)
+ ci = ReflectUtils.getClassInfo(member.getDeclaringClass());
+ return ci;
+ }
+ public int getModifiers() {
+ return modifiers;
+ }
+ public Signature getSignature() {
+ return sig;
+ }
+ public Type[] getExceptionTypes() {
+ return ReflectUtils.getExceptionTypes(member);
+ }
+ public Attribute getAttribute() {
+ return null;
+ }
+ };
+ }
+
+ public static MethodInfo getMethodInfo(Member member) {
+ return getMethodInfo(member, member.getModifiers());
+ }
+
+ public static ClassInfo getClassInfo(final Class clazz) {
+ final Type type = Type.getType(clazz);
+ final Type sc = (clazz.getSuperclass() == null) ? null : Type.getType(clazz.getSuperclass());
+ return new ClassInfo() {
+ public Type getType() {
+ return type;
+ }
+ public Type getSuperType() {
+ return sc;
+ }
+ public Type[] getInterfaces() {
+ return TypeUtils.getTypes(clazz.getInterfaces());
+ }
+ public int getModifiers() {
+ return clazz.getModifiers();
+ }
+ };
+ }
+
+ // used by MethodInterceptorGenerated generated code
+ public static Method[] findMethods(String[] namesAndDescriptors, Method[] methods)
+ {
+ Map map = new HashMap();
+ for (int i = 0; i < methods.length; i++) {
+ Method method = methods[i];
+ map.put(method.getName() + Type.getMethodDescriptor(method), method);
+ }
+ Method[] result = new Method[namesAndDescriptors.length / 2];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = (Method)map.get(namesAndDescriptors[i * 2] + namesAndDescriptors[i * 2 + 1]);
+ if (result[i] == null) {
+ // TODO: error?
+ }
+ }
+ return result;
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/RejectModifierPredicate.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/RejectModifierPredicate.java
new file mode 100644
index 000000000..587df62c1
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/RejectModifierPredicate.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 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 com.fr.third.net.sf.cglib.core;
+
+import java.lang.reflect.*;
+
+public class RejectModifierPredicate implements Predicate {
+ private int rejectMask;
+
+ public RejectModifierPredicate(int rejectMask) {
+ this.rejectMask = rejectMask;
+ }
+
+ public boolean evaluate(Object arg) {
+ return (((Member)arg).getModifiers() & rejectMask) == 0;
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/Signature.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Signature.java
new file mode 100644
index 000000000..63576bb17
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Signature.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.core;
+
+import com.fr.third.org.objectweb.asm.Type;
+
+/**
+ * A representation of a method signature, containing the method name,
+ * return type, and parameter types.
+ */
+public class Signature {
+ private String name;
+ private String desc;
+
+ public Signature(String name, String desc) {
+ // TODO: better error checking
+ if (name.indexOf('(') >= 0) {
+ throw new IllegalArgumentException("Name '" + name + "' is invalid");
+ }
+ this.name = name;
+ this.desc = desc;
+ }
+
+ public Signature(String name, Type returnType, Type[] argumentTypes) {
+ this(name, Type.getMethodDescriptor(returnType, argumentTypes));
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDescriptor() {
+ return desc;
+ }
+
+ public Type getReturnType() {
+ return Type.getReturnType(desc);
+ }
+
+ public Type[] getArgumentTypes() {
+ return Type.getArgumentTypes(desc);
+ }
+
+ public String toString() {
+ return name + desc;
+ }
+
+ public boolean equals(Object o) {
+ if (o == null)
+ return false;
+ if (!(o instanceof Signature))
+ return false;
+ Signature other = (Signature)o;
+ return name.equals(other.name) && desc.equals(other.desc);
+ }
+
+ public int hashCode() {
+ return name.hashCode() ^ desc.hashCode();
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/TinyBitSet.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/TinyBitSet.java
new file mode 100644
index 000000000..6815bf24f
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/TinyBitSet.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.core;
+
+@Deprecated
+public class TinyBitSet {
+ private static int[] T = new int[256];
+ private int value = 0;
+
+ private static int gcount(int x) {
+ int c = 0;
+ while (x != 0) {
+ c++;
+ x &= (x - 1);
+ }
+ return c;
+ }
+
+ static {
+ for (int j = 0; j < 256; j++) {
+ T[j] = gcount(j);
+ }
+ }
+
+ private static int topbit(int i) {
+ int j;
+ for (j = 0; i != 0; i ^= j) {
+ j = i & -i;
+ }
+ return j;
+ }
+
+ private static int log2(int i) {
+ int j = 0;
+ for (j = 0; i != 0; i >>= 1) {
+ j++;
+ }
+ return j;
+ }
+
+ public int length() {
+ return log2(topbit(value));
+ }
+
+ /**
+ * If bit 31 is set then this method results in an infinite loop.
+ *
+ * @return the number of bits set to true in this TinyBitSet.
+ */
+ public int cardinality() {
+ int w = value;
+ int c = 0;
+ while (w != 0) {
+ c += T[w & 255];
+ w >>= 8;
+ }
+ return c;
+ }
+
+ public boolean get(int index) {
+ return (value & (1 << index)) != 0;
+ }
+
+ public void set(int index) {
+ value |= (1 << index);
+ }
+
+ public void clear(int index) {
+ value &= ~(1 << index);
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/Transformer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Transformer.java
new file mode 100644
index 000000000..d4f605ac1
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/Transformer.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.core;
+
+public interface Transformer {
+ Object transform(Object value);
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/TypeUtils.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/TypeUtils.java
new file mode 100644
index 000000000..038e0c6d0
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/TypeUtils.java
@@ -0,0 +1,421 @@
+/*
+ * 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 com.fr.third.net.sf.cglib.core;
+
+import java.util.*;
+import com.fr.third.org.objectweb.asm.Type;
+
+public class TypeUtils {
+ private static final Map transforms = new HashMap();
+ private static final Map rtransforms = new HashMap();
+
+ private TypeUtils() {
+ }
+
+ static {
+ transforms.put("void", "V");
+ transforms.put("byte", "B");
+ transforms.put("char", "C");
+ transforms.put("double", "D");
+ transforms.put("float", "F");
+ transforms.put("int", "I");
+ transforms.put("long", "J");
+ transforms.put("short", "S");
+ transforms.put("boolean", "Z");
+
+ CollectionUtils.reverse(transforms, rtransforms);
+ }
+
+ public static Type getType(String className) {
+ return Type.getType("L" + className.replace('.', '/') + ";");
+ }
+
+ public static boolean isFinal(int access) {
+ return (Constants.ACC_FINAL & access) != 0;
+ }
+
+ public static boolean isStatic(int access) {
+ return (Constants.ACC_STATIC & access) != 0;
+ }
+
+ public static boolean isProtected(int access) {
+ return (Constants.ACC_PROTECTED & access) != 0;
+ }
+
+ public static boolean isPublic(int access) {
+ return (Constants.ACC_PUBLIC & access) != 0;
+ }
+
+ public static boolean isAbstract(int access) {
+ return (Constants.ACC_ABSTRACT & access) != 0;
+ }
+
+ public static boolean isInterface(int access) {
+ return (Constants.ACC_INTERFACE & access) != 0;
+ }
+
+ public static boolean isPrivate(int access) {
+ return (Constants.ACC_PRIVATE & access) != 0;
+ }
+
+ public static boolean isSynthetic(int access) {
+ return (Constants.ACC_SYNTHETIC & access) != 0;
+ }
+
+ public static boolean isBridge(int access) {
+ return (Constants.ACC_BRIDGE & access) != 0;
+ }
+
+ // getPackage returns null on JDK 1.2
+ public static String getPackageName(Type type) {
+ return getPackageName(getClassName(type));
+ }
+
+ public static String getPackageName(String className) {
+ int idx = className.lastIndexOf('.');
+ return (idx < 0) ? "" : className.substring(0, idx);
+ }
+
+ public static String upperFirst(String s) {
+ if (s == null || s.length() == 0) {
+ return s;
+ }
+ return Character.toUpperCase(s.charAt(0)) + s.substring(1);
+ }
+
+ public static String getClassName(Type type) {
+ if (isPrimitive(type)) {
+ return (String)rtransforms.get(type.getDescriptor());
+ } else if (isArray(type)) {
+ return getClassName(getComponentType(type)) + "[]";
+ } else {
+ return type.getClassName();
+ }
+ }
+
+ public static Type[] add(Type[] types, Type extra) {
+ if (types == null) {
+ return new Type[]{ extra };
+ } else {
+ List list = Arrays.asList(types);
+ if (list.contains(extra)) {
+ return types;
+ }
+ Type[] copy = new Type[types.length + 1];
+ System.arraycopy(types, 0, copy, 0, types.length);
+ copy[types.length] = extra;
+ return copy;
+ }
+ }
+
+ public static Type[] add(Type[] t1, Type[] t2) {
+ // TODO: set semantics?
+ Type[] all = new Type[t1.length + t2.length];
+ System.arraycopy(t1, 0, all, 0, t1.length);
+ System.arraycopy(t2, 0, all, t1.length, t2.length);
+ return all;
+ }
+
+ public static Type fromInternalName(String name) {
+ // TODO; primitives?
+ return Type.getType("L" + name + ";");
+ }
+
+ public static Type[] fromInternalNames(String[] names) {
+ if (names == null) {
+ return null;
+ }
+ Type[] types = new Type[names.length];
+ for (int i = 0; i < names.length; i++) {
+ types[i] = fromInternalName(names[i]);
+ }
+ return types;
+ }
+
+ public static int getStackSize(Type[] types) {
+ int size = 0;
+ for (int i = 0; i < types.length; i++) {
+ size += types[i].getSize();
+ }
+ return size;
+ }
+
+ public static String[] toInternalNames(Type[] types) {
+ if (types == null) {
+ return null;
+ }
+ String[] names = new String[types.length];
+ for (int i = 0; i < types.length; i++) {
+ names[i] = types[i].getInternalName();
+ }
+ return names;
+ }
+
+ public static Signature parseSignature(String s) {
+ int space = s.indexOf(' ');
+ int lparen = s.indexOf('(', space);
+ int rparen = s.indexOf(')', lparen);
+ String returnType = s.substring(0, space);
+ String methodName = s.substring(space + 1, lparen);
+ StringBuffer sb = new StringBuffer();
+ sb.append('(');
+ for (Iterator it = parseTypes(s, lparen + 1, rparen).iterator(); it.hasNext();) {
+ sb.append(it.next());
+ }
+ sb.append(')');
+ sb.append(map(returnType));
+ return new Signature(methodName, sb.toString());
+ }
+
+ public static Type parseType(String s) {
+ return Type.getType(map(s));
+ }
+
+ public static Type[] parseTypes(String s) {
+ List names = parseTypes(s, 0, s.length());
+ Type[] types = new Type[names.size()];
+ for (int i = 0; i < types.length; i++) {
+ types[i] = Type.getType((String)names.get(i));
+ }
+ return types;
+ }
+
+ public static Signature parseConstructor(Type[] types) {
+ StringBuffer sb = new StringBuffer();
+ sb.append("(");
+ for (int i = 0; i < types.length; i++) {
+ sb.append(types[i].getDescriptor());
+ }
+ sb.append(")");
+ sb.append("V");
+ return new Signature(Constants.CONSTRUCTOR_NAME, sb.toString());
+ }
+
+ public static Signature parseConstructor(String sig) {
+ return parseSignature("void (" + sig + ")"); // TODO
+ }
+
+ private static List parseTypes(String s, int mark, int end) {
+ List types = new ArrayList(5);
+ for (;;) {
+ int next = s.indexOf(',', mark);
+ if (next < 0) {
+ break;
+ }
+ types.add(map(s.substring(mark, next).trim()));
+ mark = next + 1;
+ }
+ types.add(map(s.substring(mark, end).trim()));
+ return types;
+ }
+
+ private static String map(String type) {
+ if (type.equals("")) {
+ return type;
+ }
+ String t = (String)transforms.get(type);
+ if (t != null) {
+ return t;
+ } else if (type.indexOf('.') < 0) {
+ return map("java.lang." + type);
+ } else {
+ StringBuffer sb = new StringBuffer();
+ int index = 0;
+ while ((index = type.indexOf("[]", index) + 1) > 0) {
+ sb.append('[');
+ }
+ type = type.substring(0, type.length() - sb.length() * 2);
+ sb.append('L').append(type.replace('.', '/')).append(';');
+ return sb.toString();
+ }
+ }
+
+ public static Type getBoxedType(Type type) {
+ switch (type.getSort()) {
+ case Type.CHAR:
+ return Constants.TYPE_CHARACTER;
+ case Type.BOOLEAN:
+ return Constants.TYPE_BOOLEAN;
+ case Type.DOUBLE:
+ return Constants.TYPE_DOUBLE;
+ case Type.FLOAT:
+ return Constants.TYPE_FLOAT;
+ case Type.LONG:
+ return Constants.TYPE_LONG;
+ case Type.INT:
+ return Constants.TYPE_INTEGER;
+ case Type.SHORT:
+ return Constants.TYPE_SHORT;
+ case Type.BYTE:
+ return Constants.TYPE_BYTE;
+ default:
+ return type;
+ }
+ }
+
+ public static Type getUnboxedType(Type type) {
+ if (Constants.TYPE_INTEGER.equals(type)) {
+ return Type.INT_TYPE;
+ } else if (Constants.TYPE_BOOLEAN.equals(type)) {
+ return Type.BOOLEAN_TYPE;
+ } else if (Constants.TYPE_DOUBLE.equals(type)) {
+ return Type.DOUBLE_TYPE;
+ } else if (Constants.TYPE_LONG.equals(type)) {
+ return Type.LONG_TYPE;
+ } else if (Constants.TYPE_CHARACTER.equals(type)) {
+ return Type.CHAR_TYPE;
+ } else if (Constants.TYPE_BYTE.equals(type)) {
+ return Type.BYTE_TYPE;
+ } else if (Constants.TYPE_FLOAT.equals(type)) {
+ return Type.FLOAT_TYPE;
+ } else if (Constants.TYPE_SHORT.equals(type)) {
+ return Type.SHORT_TYPE;
+ } else {
+ return type;
+ }
+ }
+
+ public static boolean isArray(Type type) {
+ return type.getSort() == Type.ARRAY;
+ }
+
+ public static Type getComponentType(Type type) {
+ if (!isArray(type)) {
+ throw new IllegalArgumentException("Type " + type + " is not an array");
+ }
+ return Type.getType(type.getDescriptor().substring(1));
+ }
+
+ public static boolean isPrimitive(Type type) {
+ switch (type.getSort()) {
+ case Type.ARRAY:
+ case Type.OBJECT:
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ public static String emulateClassGetName(Type type) {
+ if (isArray(type)) {
+ return type.getDescriptor().replace('/', '.');
+ } else {
+ return getClassName(type);
+ }
+ }
+
+ public static boolean isConstructor(MethodInfo method) {
+ return method.getSignature().getName().equals(Constants.CONSTRUCTOR_NAME);
+ }
+
+ public static Type[] getTypes(Class[] classes) {
+ if (classes == null) {
+ return null;
+ }
+ Type[] types = new Type[classes.length];
+ for (int i = 0; i < classes.length; i++) {
+ types[i] = Type.getType(classes[i]);
+ }
+ return types;
+ }
+
+ public static int ICONST(int value) {
+ switch (value) {
+ case -1: return Constants.ICONST_M1;
+ case 0: return Constants.ICONST_0;
+ case 1: return Constants.ICONST_1;
+ case 2: return Constants.ICONST_2;
+ case 3: return Constants.ICONST_3;
+ case 4: return Constants.ICONST_4;
+ case 5: return Constants.ICONST_5;
+ }
+ return -1; // error
+ }
+
+ public static int LCONST(long value) {
+ if (value == 0L) {
+ return Constants.LCONST_0;
+ } else if (value == 1L) {
+ return Constants.LCONST_1;
+ } else {
+ return -1; // error
+ }
+ }
+
+ public static int FCONST(float value) {
+ if (value == 0f) {
+ return Constants.FCONST_0;
+ } else if (value == 1f) {
+ return Constants.FCONST_1;
+ } else if (value == 2f) {
+ return Constants.FCONST_2;
+ } else {
+ return -1; // error
+ }
+ }
+
+ public static int DCONST(double value) {
+ if (value == 0d) {
+ return Constants.DCONST_0;
+ } else if (value == 1d) {
+ return Constants.DCONST_1;
+ } else {
+ return -1; // error
+ }
+ }
+
+ public static int NEWARRAY(Type type) {
+ switch (type.getSort()) {
+ case Type.BYTE:
+ return Constants.T_BYTE;
+ case Type.CHAR:
+ return Constants.T_CHAR;
+ case Type.DOUBLE:
+ return Constants.T_DOUBLE;
+ case Type.FLOAT:
+ return Constants.T_FLOAT;
+ case Type.INT:
+ return Constants.T_INT;
+ case Type.LONG:
+ return Constants.T_LONG;
+ case Type.SHORT:
+ return Constants.T_SHORT;
+ case Type.BOOLEAN:
+ return Constants.T_BOOLEAN;
+ default:
+ return -1; // error
+ }
+ }
+
+ public static String escapeType(String s) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0, len = s.length(); i < len; i++) {
+ char c = s.charAt(i);
+ switch (c) {
+ case '$': sb.append("$24"); break;
+ case '.': sb.append("$2E"); break;
+ case '[': sb.append("$5B"); break;
+ case ';': sb.append("$3B"); break;
+ case '(': sb.append("$28"); break;
+ case ')': sb.append("$29"); break;
+ case '/': sb.append("$2F"); break;
+ default:
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/VisibilityPredicate.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/VisibilityPredicate.java
new file mode 100644
index 000000000..031aa6d44
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/VisibilityPredicate.java
@@ -0,0 +1,52 @@
+/*
+ * 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 com.fr.third.net.sf.cglib.core;
+
+import java.lang.reflect.*;
+import com.fr.third.org.objectweb.asm.Type;
+
+public class VisibilityPredicate implements Predicate {
+ private boolean protectedOk;
+ private String pkg;
+ private boolean samePackageOk;
+
+ public VisibilityPredicate(Class source, boolean protectedOk) {
+ this.protectedOk = protectedOk;
+ // same package is not ok for the bootstrap loaded classes. In all other cases we are
+ // generating classes in the same classloader
+ this.samePackageOk = source.getClassLoader() != null;
+ pkg = TypeUtils.getPackageName(Type.getType(source));
+ }
+
+ public boolean evaluate(Object arg) {
+ Member member = (Member)arg;
+ int mod = member.getModifiers();
+ if (Modifier.isPrivate(mod)) {
+ return false;
+ } else if (Modifier.isPublic(mod)) {
+ return true;
+ } else if (Modifier.isProtected(mod) && protectedOk) {
+ // protected is fine if 'protectedOk' is true (for subclasses)
+ return true;
+ } else {
+ // protected/package private if the member is in the same package as the source class
+ // and we are generating into the same classloader.
+ return samePackageOk
+ && pkg.equals(TypeUtils.getPackageName(Type.getType(member.getDeclaringClass())));
+ }
+ }
+}
+
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/WeakCacheKey.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/WeakCacheKey.java
new file mode 100644
index 000000000..0f0a4666e
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/WeakCacheKey.java
@@ -0,0 +1,42 @@
+package com.fr.third.net.sf.cglib.core;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Allows to check for object equality, yet the class does not keep strong reference to the target.
+ * {@link #equals(Object)} returns true if and only if the reference is not yet expired and target
+ * objects are equal in terms of {@link #equals(Object)}.
+ *
+ * This an internal class, thus it might disappear in future cglib releases.
+ *
+ * @param type of the reference
+ */
+public class WeakCacheKey extends WeakReference {
+ private final int hash;
+
+ public WeakCacheKey(T referent) {
+ super(referent);
+ this.hash = referent.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof WeakCacheKey)) {
+ return false;
+ }
+ Object ours = get();
+ Object theirs = ((WeakCacheKey) obj).get();
+ return ours != null && theirs != null && ours.equals(theirs);
+ }
+
+ @Override
+ public int hashCode() {
+ return hash;
+ }
+
+ @Override
+ public String toString() {
+ T t = get();
+ return t == null ? "Clean WeakIdentityKey, hash: " + hash : t.toString();
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/internal/CustomizerRegistry.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/internal/CustomizerRegistry.java
new file mode 100644
index 000000000..b8732803c
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/internal/CustomizerRegistry.java
@@ -0,0 +1,48 @@
+package com.fr.third.net.sf.cglib.core.internal;
+
+import com.fr.third.net.sf.cglib.core.Customizer;
+import com.fr.third.net.sf.cglib.core.FieldTypeCustomizer;
+import com.fr.third.net.sf.cglib.core.KeyFactoryCustomizer;
+
+import java.util.*;
+
+public class CustomizerRegistry {
+ private final Class[] customizerTypes;
+ private Map> customizers = new HashMap>();
+
+ public CustomizerRegistry(Class[] customizerTypes) {
+ this.customizerTypes = customizerTypes;
+ }
+
+ public void add(KeyFactoryCustomizer customizer) {
+ Class extends KeyFactoryCustomizer> klass = customizer.getClass();
+ for (Class type : customizerTypes) {
+ if (type.isAssignableFrom(klass)) {
+ List list = customizers.get(type);
+ if (list == null) {
+ customizers.put(type, list = new ArrayList());
+ }
+ list.add(customizer);
+ }
+ }
+ }
+
+ public List get(Class klass) {
+ List list = customizers.get(klass);
+ if (list == null) {
+ return Collections.emptyList();
+ }
+ return (List) list;
+ }
+
+ /**
+ * @deprecated Only to keep backward compatibility.
+ */
+ @Deprecated
+ public static CustomizerRegistry singleton(Customizer customizer)
+ {
+ CustomizerRegistry registry = new CustomizerRegistry(new Class[]{Customizer.class});
+ registry.add(customizer);
+ return registry;
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/internal/Function.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/internal/Function.java
new file mode 100644
index 000000000..bf4f75e07
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/internal/Function.java
@@ -0,0 +1,5 @@
+package com.fr.third.net.sf.cglib.core.internal;
+
+public interface Function {
+ V apply(K key);
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/core/internal/LoadingCache.java b/fine-cglib/src/com/fr/third/net/sf/cglib/core/internal/LoadingCache.java
new file mode 100644
index 000000000..4a255fcd6
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/core/internal/LoadingCache.java
@@ -0,0 +1,86 @@
+package com.fr.third.net.sf.cglib.core.internal;
+
+import java.util.concurrent.*;
+
+public class LoadingCache {
+ protected final ConcurrentMap map;
+ protected final Function loader;
+ protected final Function keyMapper;
+
+ public static final Function IDENTITY = new Function() {
+ public Object apply(Object key) {
+ return key;
+ }
+ };
+
+ public LoadingCache(Function keyMapper, Function loader) {
+ this.keyMapper = keyMapper;
+ this.loader = loader;
+ this.map = new ConcurrentHashMap();
+ }
+
+ @SuppressWarnings("unchecked")
+ public static Function identity() {
+ return IDENTITY;
+ }
+
+ public V get(K key) {
+ final KK cacheKey = keyMapper.apply(key);
+ Object v = map.get(cacheKey);
+ if (v != null && !(v instanceof FutureTask)) {
+ return (V) v;
+ }
+
+ return createEntry(key, cacheKey, v);
+ }
+
+ /**
+ * Loads entry to the cache.
+ * If entry is missing, put {@link FutureTask} first so other competing thread might wait for the result.
+ * @param key original key that would be used to load the instance
+ * @param cacheKey key that would be used to store the entry in internal map
+ * @param v null or {@link FutureTask}
+ * @return newly created instance
+ */
+ protected V createEntry(final K key, KK cacheKey, Object v) {
+ FutureTask task;
+ boolean creator = false;
+ if (v != null) {
+ // Another thread is already loading an instance
+ task = (FutureTask) v;
+ } else {
+ task = new FutureTask(new Callable() {
+ public V call() throws Exception {
+ return loader.apply(key);
+ }
+ });
+ Object prevTask = map.putIfAbsent(cacheKey, task);
+ if (prevTask == null) {
+ // creator does the load
+ creator = true;
+ task.run();
+ } else if (prevTask instanceof FutureTask) {
+ task = (FutureTask) prevTask;
+ } else {
+ return (V) prevTask;
+ }
+ }
+
+ V result;
+ try {
+ result = task.get();
+ } catch (InterruptedException e) {
+ throw new IllegalStateException("Interrupted while loading cache item", e);
+ } catch (ExecutionException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof RuntimeException) {
+ throw ((RuntimeException) cause);
+ }
+ throw new IllegalStateException("Unable to load cache item", cause);
+ }
+ if (creator) {
+ map.put(cacheKey, result);
+ }
+ return result;
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/BridgeMethodResolver.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/BridgeMethodResolver.java
new file mode 100644
index 000000000..7d77d7c79
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/BridgeMethodResolver.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2011 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 com.fr.third.net.sf.cglib.proxy;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import com.fr.third.net.sf.cglib.core.Signature;
+
+import com.fr.third.org.objectweb.asm.AnnotationVisitor;
+import com.fr.third.org.objectweb.asm.Attribute;
+import com.fr.third.org.objectweb.asm.ClassReader;
+import com.fr.third.org.objectweb.asm.ClassVisitor;
+import com.fr.third.org.objectweb.asm.FieldVisitor;
+import com.fr.third.org.objectweb.asm.Label;
+import com.fr.third.org.objectweb.asm.MethodVisitor;
+import com.fr.third.org.objectweb.asm.Opcodes;
+
+/**
+ * Uses bytecode reflection to figure out the targets of all bridge methods
+ * that use invokespecial, so that we can later rewrite them to use invokevirtual.
+ *
+ * @author sberlin@gmail.com (Sam Berlin)
+ */
+class BridgeMethodResolver {
+
+ private final Map/* */declToBridge;
+ private final ClassLoader classLoader;
+
+ public BridgeMethodResolver(Map declToBridge, ClassLoader classLoader) {
+ this.declToBridge = declToBridge;
+ this.classLoader = classLoader;
+ }
+
+ /**
+ * Finds all bridge methods that are being called with invokespecial &
+ * returns them.
+ */
+ public Map/**/resolveAll() {
+ Map resolved = new HashMap();
+ for (Iterator entryIter = declToBridge.entrySet().iterator(); entryIter.hasNext(); ) {
+ Map.Entry entry = (Map.Entry) entryIter.next();
+ Class owner = (Class) entry.getKey();
+ Set bridges = (Set) entry.getValue();
+ try {
+ InputStream is = classLoader.getResourceAsStream(owner.getName().replace('.', '/') + ".class");
+ if (is == null) {
+ return resolved;
+ }
+ try {
+ new ClassReader(is)
+ .accept(new BridgedFinder(bridges, resolved),
+ ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG);
+ } finally {
+ is.close();
+ }
+ } catch (IOException ignored) {}
+ }
+ return resolved;
+ }
+
+ private static class BridgedFinder extends ClassVisitor {
+ private Map/**/ resolved;
+ private Set/**/ eligibleMethods;
+
+ private Signature currentMethod = null;
+
+ BridgedFinder(Set eligibleMethods, Map resolved) {
+ super(Opcodes.ASM6);
+ this.resolved = resolved;
+ this.eligibleMethods = eligibleMethods;
+ }
+
+ public void visit(int version, int access, String name,
+ String signature, String superName, String[] interfaces) {
+ }
+
+ public MethodVisitor visitMethod(int access, String name, String desc,
+ String signature, String[] exceptions) {
+ Signature sig = new Signature(name, desc);
+ if (eligibleMethods.remove(sig)) {
+ currentMethod = sig;
+ return new MethodVisitor(Opcodes.ASM6) {
+ public void visitMethodInsn(int opcode, String owner, String name,
+ String desc, boolean itf) {
+ if (opcode == Opcodes.INVOKESPECIAL && currentMethod != null) {
+ Signature target = new Signature(name, desc);
+ // If the target signature is the same as the current,
+ // we shouldn't change our bridge becaues invokespecial
+ // is the only way to make progress (otherwise we'll
+ // get infinite recursion). This would typically
+ // only happen when a bridge method is created to widen
+ // the visibility of a superclass' method.
+ if (!target.equals(currentMethod)) {
+ resolved.put(currentMethod, target);
+ }
+ currentMethod = null;
+ }
+ }
+ };
+ } else {
+ return null;
+ }
+ }
+ }
+
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Callback.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Callback.java
new file mode 100644
index 000000000..ae918cdfd
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Callback.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.proxy;
+
+/**
+ * All callback interfaces used by {@link Enhancer} extend this interface.
+ * @see MethodInterceptor
+ * @see NoOp
+ * @see LazyLoader
+ * @see Dispatcher
+ * @see InvocationHandler
+ * @see FixedValue
+ */
+public interface Callback
+{
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackFilter.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackFilter.java
new file mode 100644
index 000000000..c08dea41b
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackFilter.java
@@ -0,0 +1,46 @@
+/*
+ * 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 com.fr.third.net.sf.cglib.proxy;
+
+import java.lang.reflect.Method;
+
+/**
+ * Map methods of subclasses generated by {@link Enhancer} to a particular
+ * callback. The type of the callbacks chosen for each method affects
+ * the bytecode generated for that method in the subclass, and cannot
+ * change for the life of the class.
+ *
Note: {@link CallbackFilter} implementations are supposed to be
+ * lightweight as cglib might keep {@link CallbackFilter} objects
+ * alive to enable caching of generated classes. Prefer using {@code static}
+ * classes for implementation of {@link CallbackFilter}.
+ */
+public interface CallbackFilter {
+ /**
+ * Map a method to a callback.
+ * @param method the intercepted method
+ * @return the index into the array of callbacks (as specified by {@link Enhancer#setCallbacks}) to use for the method,
+ */
+ int accept(Method method);
+
+ /**
+ * The CallbackFilter in use affects which cached class
+ * the Enhancer will use, so this is a reminder that
+ * you should correctly implement equals and
+ * hashCode for custom CallbackFilter
+ * implementations in order to improve performance.
+ */
+ boolean equals(Object o);
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackGenerator.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackGenerator.java
new file mode 100644
index 000000000..66c1957d6
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackGenerator.java
@@ -0,0 +1,36 @@
+/*
+ * 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 com.fr.third.net.sf.cglib.proxy;
+
+import java.util.List;
+import com.fr.third.net.sf.cglib.core.*;
+
+interface CallbackGenerator
+{
+ void generate(ClassEmitter ce, Context context, List methods) throws Exception;
+ void generateStatic(CodeEmitter e, Context context, List methods) throws Exception;
+
+ interface Context
+ {
+ ClassLoader getClassLoader();
+ CodeEmitter beginMethod(ClassEmitter ce, MethodInfo method);
+ int getOriginalModifiers(MethodInfo method);
+ int getIndex(MethodInfo method);
+ void emitCallback(CodeEmitter ce, int index);
+ Signature getImplSignature(MethodInfo method);
+ void emitLoadArgsAndInvoke(CodeEmitter e, MethodInfo method);
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackHelper.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackHelper.java
new file mode 100644
index 000000000..81add3184
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackHelper.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 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 com.fr.third.net.sf.cglib.proxy;
+
+import com.fr.third.net.sf.cglib.core.ReflectUtils;
+import java.lang.reflect.Method;
+import java.util.*;
+
+/**
+ * @version $Id: CallbackHelper.java,v 1.2 2004/06/24 21:15:20 herbyderby Exp $
+ */
+abstract public class CallbackHelper
+implements CallbackFilter
+{
+ private Map methodMap = new HashMap();
+ private List callbacks = new ArrayList();
+
+ public CallbackHelper(Class superclass, Class[] interfaces)
+ {
+ List methods = new ArrayList();
+ Enhancer.getMethods(superclass, interfaces, methods);
+ Map indexes = new HashMap();
+ for (int i = 0, size = methods.size(); i < size; i++) {
+ Method method = (Method)methods.get(i);
+ Object callback = getCallback(method);
+ if (callback == null)
+ throw new IllegalStateException("getCallback cannot return null");
+ boolean isCallback = callback instanceof Callback;
+ if (!(isCallback || (callback instanceof Class)))
+ throw new IllegalStateException("getCallback must return a Callback or a Class");
+ if (i > 0 && ((callbacks.get(i - 1) instanceof Callback) ^ isCallback))
+ throw new IllegalStateException("getCallback must return a Callback or a Class consistently for every Method");
+ Integer index = (Integer)indexes.get(callback);
+ if (index == null) {
+ index = new Integer(callbacks.size());
+ indexes.put(callback, index);
+ }
+ methodMap.put(method, index);
+ callbacks.add(callback);
+ }
+ }
+
+ abstract protected Object getCallback(Method method);
+
+ public Callback[] getCallbacks()
+ {
+ if (callbacks.size() == 0)
+ return new Callback[0];
+ if (callbacks.get(0) instanceof Callback) {
+ return (Callback[])callbacks.toArray(new Callback[callbacks.size()]);
+ } else {
+ throw new IllegalStateException("getCallback returned classes, not callbacks; call getCallbackTypes instead");
+ }
+ }
+
+ public Class[] getCallbackTypes()
+ {
+ if (callbacks.size() == 0)
+ return new Class[0];
+ if (callbacks.get(0) instanceof Callback) {
+ return ReflectUtils.getClasses(getCallbacks());
+ } else {
+ return (Class[])callbacks.toArray(new Class[callbacks.size()]);
+ }
+ }
+
+ public int accept(Method method)
+ {
+ return ((Integer)methodMap.get(method)).intValue();
+ }
+
+ public int hashCode()
+ {
+ return methodMap.hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ if (o == null)
+ return false;
+ if (!(o instanceof CallbackHelper))
+ return false;
+ return methodMap.equals(((CallbackHelper)o).methodMap);
+ }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackInfo.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackInfo.java
new file mode 100644
index 000000000..fdcdfebf4
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/CallbackInfo.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 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 com.fr.third.net.sf.cglib.proxy;
+
+import com.fr.third.org.objectweb.asm.Type;
+
+class CallbackInfo
+{
+ public static Type[] determineTypes(Class[] callbackTypes) {
+ return determineTypes(callbackTypes, true);
+ }
+
+ public static Type[] determineTypes(Class[] callbackTypes, boolean checkAll) {
+ Type[] types = new Type[callbackTypes.length];
+ for (int i = 0; i < types.length; i++) {
+ types[i] = determineType(callbackTypes[i], checkAll);
+ }
+ return types;
+ }
+
+ public static Type[] determineTypes(Callback[] callbacks) {
+ return determineTypes(callbacks, true);
+ }
+
+ public static Type[] determineTypes(Callback[] callbacks, boolean checkAll) {
+ Type[] types = new Type[callbacks.length];
+ for (int i = 0; i < types.length; i++) {
+ types[i] = determineType(callbacks[i], checkAll);
+ }
+ return types;
+ }
+
+ public static CallbackGenerator[] getGenerators(Type[] callbackTypes) {
+ CallbackGenerator[] generators = new CallbackGenerator[callbackTypes.length];
+ for (int i = 0; i < generators.length; i++) {
+ generators[i] = getGenerator(callbackTypes[i]);
+ }
+ return generators;
+ }
+
+ //////////////////// PRIVATE ////////////////////
+
+ private Class cls;
+ private CallbackGenerator generator;
+ private Type type;
+
+ private static final CallbackInfo[] CALLBACKS = {
+ new CallbackInfo(NoOp.class, NoOpGenerator.INSTANCE),
+ new CallbackInfo(MethodInterceptor.class, MethodInterceptorGenerator.INSTANCE),
+ new CallbackInfo(InvocationHandler.class, InvocationHandlerGenerator.INSTANCE),
+ new CallbackInfo(LazyLoader.class, LazyLoaderGenerator.INSTANCE),
+ new CallbackInfo(Dispatcher.class, DispatcherGenerator.INSTANCE),
+ new CallbackInfo(FixedValue.class, FixedValueGenerator.INSTANCE),
+ new CallbackInfo(ProxyRefDispatcher.class, DispatcherGenerator.PROXY_REF_INSTANCE),
+ };
+
+ private CallbackInfo(Class cls, CallbackGenerator generator) {
+ this.cls = cls;
+ this.generator = generator;
+ type = Type.getType(cls);
+ }
+
+ private static Type determineType(Callback callback, boolean checkAll) {
+ if (callback == null) {
+ throw new IllegalStateException("Callback is null");
+ }
+ return determineType(callback.getClass(), checkAll);
+ }
+
+ private static Type determineType(Class callbackType, boolean checkAll) {
+ Class cur = null;
+ Type type = null;
+ for (int i = 0; i < CALLBACKS.length; i++) {
+ CallbackInfo info = CALLBACKS[i];
+ if (info.cls.isAssignableFrom(callbackType)) {
+ if (cur != null) {
+ throw new IllegalStateException("Callback implements both " + cur + " and " + info.cls);
+ }
+ cur = info.cls;
+ type = info.type;
+ if (!checkAll) {
+ break;
+ }
+ }
+ }
+ if (cur == null) {
+ throw new IllegalStateException("Unknown callback type " + callbackType);
+ }
+ return type;
+ }
+
+ private static CallbackGenerator getGenerator(Type callbackType) {
+ for (int i = 0; i < CALLBACKS.length; i++) {
+ CallbackInfo info = CALLBACKS[i];
+ if (info.type.equals(callbackType)) {
+ return info.generator;
+ }
+ }
+ throw new IllegalStateException("Unknown callback type " + callbackType);
+ }
+}
+
+
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Dispatcher.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Dispatcher.java
new file mode 100644
index 000000000..f940d1d0d
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Dispatcher.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2003 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 com.fr.third.net.sf.cglib.proxy;
+
+/**
+ * Dispatching {@link Enhancer} callback. This is identical to the
+ * {@link LazyLoader} interface but needs to be separate so that Enhancer
+ * knows which type of code to generate.
+ */
+public interface Dispatcher extends Callback {
+ /**
+ * Return the object which the original method invocation should
+ * be dispatched. This method is called for every method invocation.
+ * @return an object that can invoke the method
+ */
+ Object loadObject() throws Exception;
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/DispatcherGenerator.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/DispatcherGenerator.java
new file mode 100644
index 000000000..8c34d1dd6
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/DispatcherGenerator.java
@@ -0,0 +1,65 @@
+/*
+ * 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 com.fr.third.net.sf.cglib.proxy;
+
+import java.util.*;
+import com.fr.third.net.sf.cglib.core.*;
+import com.fr.third.org.objectweb.asm.Type;
+
+class DispatcherGenerator implements CallbackGenerator {
+ public static final DispatcherGenerator INSTANCE =
+ new DispatcherGenerator(false);
+ public static final DispatcherGenerator PROXY_REF_INSTANCE =
+ new DispatcherGenerator(true);
+
+ private static final Type DISPATCHER =
+ TypeUtils.parseType("com.fr.third.net.sf.cglib.proxy.Dispatcher");
+ private static final Type PROXY_REF_DISPATCHER =
+ TypeUtils.parseType("com.fr.third.net.sf.cglib.proxy.ProxyRefDispatcher");
+ private static final Signature LOAD_OBJECT =
+ TypeUtils.parseSignature("Object loadObject()");
+ private static final Signature PROXY_REF_LOAD_OBJECT =
+ TypeUtils.parseSignature("Object loadObject(Object)");
+
+ private boolean proxyRef;
+
+ private DispatcherGenerator(boolean proxyRef) {
+ this.proxyRef = proxyRef;
+ }
+
+ public void generate(ClassEmitter ce, Context context, List methods) {
+ for (Iterator it = methods.iterator(); it.hasNext();) {
+ MethodInfo method = (MethodInfo)it.next();
+ if (!TypeUtils.isProtected(method.getModifiers())) {
+ CodeEmitter e = context.beginMethod(ce, method);
+ context.emitCallback(e, context.getIndex(method));
+ if (proxyRef) {
+ e.load_this();
+ e.invoke_interface(PROXY_REF_DISPATCHER, PROXY_REF_LOAD_OBJECT);
+ } else {
+ e.invoke_interface(DISPATCHER, LOAD_OBJECT);
+ }
+ e.checkcast(method.getClassInfo().getType());
+ e.load_args();
+ e.invoke(method);
+ e.return_value();
+ e.end_method();
+ }
+ }
+ }
+
+ public void generateStatic(CodeEmitter e, Context context, List methods) { }
+}
diff --git a/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Enhancer.java b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Enhancer.java
new file mode 100644
index 000000000..873a88bab
--- /dev/null
+++ b/fine-cglib/src/com/fr/third/net/sf/cglib/proxy/Enhancer.java
@@ -0,0 +1,1319 @@
+/*
+ * Copyright 2002,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 com.fr.third.net.sf.cglib.proxy;
+
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.ProtectionDomain;
+import java.util.*;
+
+import com.fr.third.net.sf.cglib.core.*;
+import com.fr.third.org.objectweb.asm.ClassVisitor;
+import com.fr.third.org.objectweb.asm.Type;
+import com.fr.third.org.objectweb.asm.Label;
+
+/**
+ * Generates dynamic subclasses to enable method interception. This
+ * class started as a substitute for the standard Dynamic Proxy support
+ * included with JDK 1.3, but one that allowed the proxies to extend a
+ * concrete base class, in addition to implementing interfaces. The dynamically
+ * generated subclasses override the non-final methods of the superclass and
+ * have hooks which callback to user-defined interceptor
+ * implementations.
+ *
+ * The original and most general callback type is the {@link MethodInterceptor}, which
+ * in AOP terms enables "around advice"--that is, you can invoke custom code both before
+ * and after the invocation of the "super" method. In addition you can modify the
+ * arguments before calling the super method, or not call it at all.
+ *
+ * Although MethodInterceptor is generic enough to meet any
+ * interception need, it is often overkill. For simplicity and performance, additional
+ * specialized callback types, such as {@link LazyLoader} are also available.
+ * Often a single callback will be used per enhanced class, but you can control
+ * which callback is used on a per-method basis with a {@link CallbackFilter}.
+ *
+ * The most common uses of this class are embodied in the static helper methods. For
+ * advanced needs, such as customizing the ClassLoader to use, you should create
+ * a new instance of Enhancer. Other classes within CGLIB follow a similar pattern.
+ *
+ * All enhanced objects implement the {@link Factory} interface, unless {@link #setUseFactory} is
+ * used to explicitly disable this feature. The Factory interface provides an API
+ * to change the callbacks of an existing object, as well as a faster and easier way to create
+ * new instances of the same type.
+ *
+ * For an almost drop-in replacement for
+ * java.lang.reflect.Proxy, see the {@link Proxy} class.
+ */
+public class Enhancer extends AbstractClassGenerator
+{
+ private static final CallbackFilter ALL_ZERO = new CallbackFilter(){
+ public int accept(Method method) {
+ return 0;
+ }
+ };
+
+ private static final Source SOURCE = new Source(Enhancer.class.getName());
+ private static final EnhancerKey KEY_FACTORY =
+ (EnhancerKey)KeyFactory.create(EnhancerKey.class, KeyFactory.HASH_ASM_TYPE, null);
+
+ private static final String BOUND_FIELD = "CGLIB$BOUND";
+ private static final String FACTORY_DATA_FIELD = "CGLIB$FACTORY_DATA";
+ private static final String THREAD_CALLBACKS_FIELD = "CGLIB$THREAD_CALLBACKS";
+ private static final String STATIC_CALLBACKS_FIELD = "CGLIB$STATIC_CALLBACKS";
+ private static final String SET_THREAD_CALLBACKS_NAME = "CGLIB$SET_THREAD_CALLBACKS";
+ private static final String SET_STATIC_CALLBACKS_NAME = "CGLIB$SET_STATIC_CALLBACKS";
+ private static final String CONSTRUCTED_FIELD = "CGLIB$CONSTRUCTED";
+ /**
+ * {@link com.fr.third.net.sf.cglib.core.AbstractClassGenerator.ClassLoaderData#generatedClasses} requires to keep cache key
+ * in a good shape (the keys should be up and running if the proxy class is alive), and one of the cache keys is
+ * {@link CallbackFilter}. That is why the generated class contains static field that keeps strong reference to
+ * the {@link #filter}.
+ *
This dance achieves two goals: ensures generated class is reusable and available through generatedClasses
+ * cache, and it enables to unload classloader and the related {@link CallbackFilter} in case user does not need
+ * that
+ */
+ private static final String CALLBACK_FILTER_FIELD = "CGLIB$CALLBACK_FILTER";
+
+ private static final Type OBJECT_TYPE =
+ TypeUtils.parseType("Object");
+ private static final Type FACTORY =
+ TypeUtils.parseType("com.fr.third.net.sf.cglib.proxy.Factory");
+ private static final Type ILLEGAL_STATE_EXCEPTION =
+ TypeUtils.parseType("IllegalStateException");
+ private static final Type ILLEGAL_ARGUMENT_EXCEPTION =
+ TypeUtils.parseType("IllegalArgumentException");
+ private static final Type THREAD_LOCAL =
+ TypeUtils.parseType("ThreadLocal");
+ private static final Type CALLBACK =
+ TypeUtils.parseType("com.fr.third.net.sf.cglib.proxy.Callback");
+ private static final Type CALLBACK_ARRAY =
+ Type.getType(Callback[].class);
+ private static final Signature CSTRUCT_NULL =
+ TypeUtils.parseConstructor("");
+ private static final Signature SET_THREAD_CALLBACKS =
+ new Signature(SET_THREAD_CALLBACKS_NAME, Type.VOID_TYPE, new Type[]{ CALLBACK_ARRAY });
+ private static final Signature SET_STATIC_CALLBACKS =
+ new Signature(SET_STATIC_CALLBACKS_NAME, Type.VOID_TYPE, new Type[]{ CALLBACK_ARRAY });
+ private static final Signature NEW_INSTANCE =
+ new Signature("newInstance", Constants.TYPE_OBJECT, new Type[]{ CALLBACK_ARRAY });
+ private static final Signature MULTIARG_NEW_INSTANCE =
+ new Signature("newInstance", Constants.TYPE_OBJECT, new Type[]{
+ Constants.TYPE_CLASS_ARRAY,
+ Constants.TYPE_OBJECT_ARRAY,
+ CALLBACK_ARRAY,
+ });
+ private static final Signature SINGLE_NEW_INSTANCE =
+ new Signature("newInstance", Constants.TYPE_OBJECT, new Type[]{ CALLBACK });
+ private static final Signature SET_CALLBACK =
+ new Signature("setCallback", Type.VOID_TYPE, new Type[]{ Type.INT_TYPE, CALLBACK });
+ private static final Signature GET_CALLBACK =
+ new Signature("getCallback", CALLBACK, new Type[]{ Type.INT_TYPE });
+ private static final Signature SET_CALLBACKS =
+ new Signature("setCallbacks", Type.VOID_TYPE, new Type[]{ CALLBACK_ARRAY });
+ private static final Signature GET_CALLBACKS =
+ new Signature("getCallbacks", CALLBACK_ARRAY, new Type[0]);
+ private static final Signature THREAD_LOCAL_GET =
+ TypeUtils.parseSignature("Object get()");
+ private static final Signature THREAD_LOCAL_SET =
+ TypeUtils.parseSignature("void set(Object)");
+ private static final Signature BIND_CALLBACKS =
+ TypeUtils.parseSignature("void CGLIB$BIND_CALLBACKS(Object)");
+
+ private EnhancerFactoryData currentData;
+ private Object currentKey;
+
+ /** Internal interface, only public due to ClassLoader issues. */
+ public interface EnhancerKey {
+ public Object newInstance(String type,
+ String[] interfaces,
+ WeakCacheKey filter,
+ Type[] callbackTypes,
+ boolean useFactory,
+ boolean interceptDuringConstruction,
+ Long serialVersionUID);
+ }
+
+ private Class[] interfaces;
+ private CallbackFilter filter;
+ private Callback[] callbacks;
+ private Type[] callbackTypes;
+ private boolean validateCallbackTypes;
+ private boolean classOnly;
+ private Class superclass;
+ private Class[] argumentTypes;
+ private Object[] arguments;
+ private boolean useFactory = true;
+ private Long serialVersionUID;
+ private boolean interceptDuringConstruction = true;
+
+ /**
+ * Create a new Enhancer. A new Enhancer
+ * object should be used for each generated object, and should not
+ * be shared across threads. To create additional instances of a
+ * generated class, use the Factory interface.
+ * @see Factory
+ */
+ public Enhancer() {
+ super(SOURCE);
+ }
+
+ /**
+ * Set the class which the generated class will extend. As a convenience,
+ * if the supplied superclass is actually an interface, setInterfaces
+ * will be called with the appropriate argument instead.
+ * A non-interface argument must not be declared as final, and must have an
+ * accessible constructor.
+ * @param superclass class to extend or interface to implement
+ * @see #setInterfaces(Class[])
+ */
+ public void setSuperclass(Class superclass) {
+ if (superclass != null && superclass.isInterface()) {
+ setInterfaces(new Class[]{ superclass });
+ } else if (superclass != null && superclass.equals(Object.class)) {
+ // affects choice of ClassLoader
+ this.superclass = null;
+ } else {
+ this.superclass = superclass;
+ }
+ }
+
+ /**
+ * Set the interfaces to implement. The Factory interface will
+ * always be implemented regardless of what is specified here.
+ * @param interfaces array of interfaces to implement, or null
+ * @see Factory
+ */
+ public void setInterfaces(Class[] interfaces) {
+ this.interfaces = interfaces;
+ }
+
+ /**
+ * Set the {@link CallbackFilter} used to map the generated class' methods
+ * to a particular callback index.
+ * New object instances will always use the same mapping, but may use different
+ * actual callback objects.
+ * @param filter the callback filter to use when generating a new class
+ * @see #setCallbacks
+ */
+ public void setCallbackFilter(CallbackFilter filter) {
+ this.filter = filter;
+ }
+
+
+ /**
+ * Set the single {@link Callback} to use.
+ * Ignored if you use {@link #createClass}.
+ * @param callback the callback to use for all methods
+ * @see #setCallbacks
+ */
+ public void setCallback(final Callback callback) {
+ setCallbacks(new Callback[]{ callback });
+ }
+
+ /**
+ * Set the array of callbacks to use.
+ * Ignored if you use {@link #createClass}.
+ * You must use a {@link CallbackFilter} to specify the index into this
+ * array for each method in the proxied class.
+ * @param callbacks the callback array
+ * @see #setCallbackFilter
+ * @see #setCallback
+ */
+ public void setCallbacks(Callback[] callbacks) {
+ if (callbacks != null && callbacks.length == 0) {
+ throw new IllegalArgumentException("Array cannot be empty");
+ }
+ this.callbacks = callbacks;
+ }
+
+ /**
+ * Set whether the enhanced object instances should implement
+ * the {@link Factory} interface.
+ * This was added for tools that need for proxies to be more
+ * indistinguishable from their targets. Also, in some cases it may
+ * be necessary to disable the Factory interface to
+ * prevent code from changing the underlying callbacks.
+ * @param useFactory whether to implement Factory; default is true
+ */
+ public void setUseFactory(boolean useFactory) {
+ this.useFactory = useFactory;
+ }
+
+ /**
+ * Set whether methods called from within the proxy's constructer
+ * will be intercepted. The default value is true. Unintercepted methods
+ * will call the method of the proxy's base class, if it exists.
+ * @param interceptDuringConstruction whether to intercept methods called from the constructor
+ */
+ public void setInterceptDuringConstruction(boolean interceptDuringConstruction) {
+ this.interceptDuringConstruction = interceptDuringConstruction;
+ }
+
+ /**
+ * Set the single type of {@link Callback} to use.
+ * This may be used instead of {@link #setCallback} when calling
+ * {@link #createClass}, since it may not be possible to have
+ * an array of actual callback instances.
+ * @param callbackType the type of callback to use for all methods
+ * @see #setCallbackTypes
+ */
+ public void setCallbackType(Class callbackType) {
+ setCallbackTypes(new Class[]{ callbackType });
+ }
+
+ /**
+ * Set the array of callback types to use.
+ * This may be used instead of {@link #setCallbacks} when calling
+ * {@link #createClass}, since it may not be possible to have
+ * an array of actual callback instances.
+ * You must use a {@link CallbackFilter} to specify the index into this
+ * array for each method in the proxied class.
+ * @param callbackTypes the array of callback types
+ */
+ public void setCallbackTypes(Class[] callbackTypes) {
+ if (callbackTypes != null && callbackTypes.length == 0) {
+ throw new IllegalArgumentException("Array cannot be empty");
+ }
+ this.callbackTypes = CallbackInfo.determineTypes(callbackTypes);
+ }
+
+ /**
+ * Generate a new class if necessary and uses the specified
+ * callbacks (if any) to create a new object instance.
+ * Uses the no-arg constructor of the superclass.
+ * @return a new instance
+ */
+ public Object create() {
+ classOnly = false;
+ argumentTypes = null;
+ return createHelper();
+ }
+
+ /**
+ * Generate a new class if necessary and uses the specified
+ * callbacks (if any) to create a new object instance.
+ * Uses the constructor of the superclass matching the argumentTypes
+ * parameter, with the given arguments.
+ * @param argumentTypes constructor signature
+ * @param arguments compatible wrapped arguments to pass to constructor
+ * @return a new instance
+ */
+ public Object create(Class[] argumentTypes, Object[] arguments) {
+ classOnly = false;
+ if (argumentTypes == null || arguments == null || argumentTypes.length != arguments.length) {
+ throw new IllegalArgumentException("Arguments must be non-null and of equal length");
+ }
+ this.argumentTypes = argumentTypes;
+ this.arguments = arguments;
+ return createHelper();
+ }
+
+ /**
+ * Generate a new class if necessary and return it without creating a new instance.
+ * This ignores any callbacks that have been set.
+ * To create a new instance you will have to use reflection, and methods
+ * called during the constructor will not be intercepted. To avoid this problem,
+ * use the multi-arg create method.
+ * @see #create(Class[], Object[])
+ */
+ public Class createClass() {
+ classOnly = true;
+ return (Class)createHelper();
+ }
+
+ /**
+ * Insert a static serialVersionUID field into the generated class.
+ * @param sUID the field value, or null to avoid generating field.
+ */
+ public void setSerialVersionUID(Long sUID) {
+ serialVersionUID = sUID;
+ }
+
+ private void preValidate() {
+ if (callbackTypes == null) {
+ callbackTypes = CallbackInfo.determineTypes(callbacks, false);
+ validateCallbackTypes = true;
+ }
+ if (filter == null) {
+ if (callbackTypes.length > 1) {
+ throw new IllegalStateException("Multiple callback types possible but no filter specified");
+ }
+ filter = ALL_ZERO;
+ }
+ }
+
+ private void validate() {
+ if (classOnly ^ (callbacks == null)) {
+ if (classOnly) {
+ throw new IllegalStateException("createClass does not accept callbacks");
+ } else {
+ throw new IllegalStateException("Callbacks are required");
+ }
+ }
+ if (classOnly && (callbackTypes == null)) {
+ throw new IllegalStateException("Callback types are required");
+ }
+ if (validateCallbackTypes) {
+ callbackTypes = null;
+ }
+ if (callbacks != null && callbackTypes != null) {
+ if (callbacks.length != callbackTypes.length) {
+ throw new IllegalStateException("Lengths of callback and callback types array must be the same");
+ }
+ Type[] check = CallbackInfo.determineTypes(callbacks);
+ for (int i = 0; i < check.length; i++) {
+ if (!check[i].equals(callbackTypes[i])) {
+ throw new IllegalStateException("Callback " + check[i] + " is not assignable to " + callbackTypes[i]);
+ }
+ }
+ } else if (callbacks != null) {
+ callbackTypes = CallbackInfo.determineTypes(callbacks);
+ }
+ if (interfaces != null) {
+ for (int i = 0; i < interfaces.length; i++) {
+ if (interfaces[i] == null) {
+ throw new IllegalStateException("Interfaces cannot be null");
+ }
+ if (!interfaces[i].isInterface()) {
+ throw new IllegalStateException(interfaces[i] + " is not an interface");
+ }
+ }
+ }
+ }
+
+ /**
+ * The idea of the class is to cache relevant java.lang.reflect instances so
+ * proxy-class can be instantiated faster that when using {@link ReflectUtils#newInstance(Class, Class[], Object[])}
+ * and {@link Enhancer#setThreadCallbacks(Class, Callback[])}
+ */
+ static class EnhancerFactoryData {
+ public final Class generatedClass;
+ private final Method setThreadCallbacks;
+ private final Class[] primaryConstructorArgTypes;
+ private final Constructor primaryConstructor;
+
+ public EnhancerFactoryData(Class generatedClass, Class[] primaryConstructorArgTypes, boolean classOnly) {
+ this.generatedClass = generatedClass;
+ try {
+ setThreadCallbacks = getCallbacksSetter(generatedClass, SET_THREAD_CALLBACKS_NAME);
+ if (classOnly) {
+ this.primaryConstructorArgTypes = null;
+ this.primaryConstructor = null;
+ } else {
+ this.primaryConstructorArgTypes = primaryConstructorArgTypes;
+ this.primaryConstructor = ReflectUtils.getConstructor(generatedClass, primaryConstructorArgTypes);
+ }
+ } catch (NoSuchMethodException e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+
+ /**
+ * Creates proxy instance for given argument types, and assigns the callbacks.
+ * Ideally, for each proxy class, just one set of argument types should be used,
+ * otherwise it would have to spend time on constructor lookup.
+ * Technically, it is a re-implementation of {@link Enhancer#createUsingReflection(Class)},
+ * with "cache {@link #setThreadCallbacks} and {@link #primaryConstructor}"
+ *
+ * @see #createUsingReflection(Class)
+ * @param argumentTypes constructor argument types
+ * @param arguments constructor arguments
+ * @param callbacks callbacks to set for the new instance
+ * @return newly created proxy
+ */
+ public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) {
+ setThreadCallbacks(callbacks);
+ try {
+ // Explicit reference equality is added here just in case Arrays.equals does not have one
+ if (primaryConstructorArgTypes == argumentTypes ||
+ Arrays.equals(primaryConstructorArgTypes, argumentTypes)) {
+ // If we have relevant Constructor instance at hand, just call it
+ // This skips "get constructors" machinery
+ return ReflectUtils.newInstance(primaryConstructor, arguments);
+ }
+ // Take a slow path if observing unexpected argument types
+ return ReflectUtils.newInstance(generatedClass, argumentTypes, arguments);
+ } finally {
+ // clear thread callbacks to allow them to be gc'd
+ setThreadCallbacks(null);
+ }
+
+ }
+
+ private void setThreadCallbacks(Callback[] callbacks) {
+ try {
+ setThreadCallbacks.invoke(generatedClass, (Object) callbacks);
+ } catch (IllegalAccessException e) {
+ throw new CodeGenerationException(e);
+ } catch (InvocationTargetException e) {
+ throw new CodeGenerationException(e.getTargetException());
+ }
+ }
+ }
+
+ private Object createHelper() {
+ preValidate();
+ Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
+ ReflectUtils.getNames(interfaces),
+ filter == ALL_ZERO ? null : new WeakCacheKey(filter),
+ callbackTypes,
+ useFactory,
+ interceptDuringConstruction,
+ serialVersionUID);
+ this.currentKey = key;
+ Object result = super.create(key);
+ return result;
+ }
+
+ @Override
+ protected Class generate(ClassLoaderData data) {
+ validate();
+ if (superclass != null) {
+ setNamePrefix(superclass.getName());
+ } else if (interfaces != null) {
+ setNamePrefix(interfaces[ReflectUtils.findPackageProtected(interfaces)].getName());
+ }
+ return super.generate(data);
+ }
+
+ protected ClassLoader getDefaultClassLoader() {
+ if (superclass != null) {
+ return superclass.getClassLoader();
+ } else if (interfaces != null) {
+ return interfaces[0].getClassLoader();
+ } else {
+ return null;
+ }
+ }
+
+ protected ProtectionDomain getProtectionDomain() {
+ if (superclass != null) {
+ return ReflectUtils.getProtectionDomain(superclass);
+ } else if (interfaces != null) {
+ return ReflectUtils.getProtectionDomain(interfaces[0]);
+ } else {
+ return null;
+ }
+ }
+
+ private Signature rename(Signature sig, int index) {
+ return new Signature("CGLIB$" + sig.getName() + "$" + index,
+ sig.getDescriptor());
+ }
+
+ /**
+ * Finds all of the methods that will be extended by an
+ * Enhancer-generated class using the specified superclass and
+ * interfaces. This can be useful in building a list of Callback
+ * objects. The methods are added to the end of the given list. Due
+ * to the subclassing nature of the classes generated by Enhancer,
+ * the methods are guaranteed to be non-static, non-final, and
+ * non-private. Each method signature will only occur once, even if
+ * it occurs in multiple classes.
+ * @param superclass the class that will be extended, or null
+ * @param interfaces the list of interfaces that will be implemented, or null
+ * @param methods the list into which to copy the applicable methods
+ */
+ public static void getMethods(Class superclass, Class[] interfaces, List methods)
+ {
+ getMethods(superclass, interfaces, methods, null, null);
+ }
+
+ private static void getMethods(Class superclass, Class[] interfaces, List methods, List interfaceMethods, Set forcePublic)
+ {
+ ReflectUtils.addAllMethods(superclass, methods);
+ List target = (interfaceMethods != null) ? interfaceMethods : methods;
+ if (interfaces != null) {
+ for (int i = 0; i < interfaces.length; i++) {
+ if (interfaces[i] != Factory.class) {
+ ReflectUtils.addAllMethods(interfaces[i], target);
+ }
+ }
+ }
+ if (interfaceMethods != null) {
+ if (forcePublic != null) {
+ forcePublic.addAll(MethodWrapper.createSet(interfaceMethods));
+ }
+ methods.addAll(interfaceMethods);
+ }
+ CollectionUtils.filter(methods, new RejectModifierPredicate(Constants.ACC_STATIC));
+ CollectionUtils.filter(methods, new VisibilityPredicate(superclass, true));
+ CollectionUtils.filter(methods, new DuplicatesPredicate());
+ CollectionUtils.filter(methods, new RejectModifierPredicate(Constants.ACC_FINAL));
+ }
+
+ public void generateClass(ClassVisitor v) throws Exception {
+ Class sc = (superclass == null) ? Object.class : superclass;
+
+ if (TypeUtils.isFinal(sc.getModifiers()))
+ throw new IllegalArgumentException("Cannot subclass final class " + sc.getName());
+ List constructors = new ArrayList(Arrays.asList(sc.getDeclaredConstructors()));
+ filterConstructors(sc, constructors);
+
+ // Order is very important: must add superclass, then
+ // its superclass chain, then each interface and
+ // its superinterfaces.
+ List actualMethods = new ArrayList();
+ List interfaceMethods = new ArrayList();
+ final Set forcePublic = new HashSet();
+ getMethods(sc, interfaces, actualMethods, interfaceMethods, forcePublic);
+
+ List methods = CollectionUtils.transform(actualMethods, new Transformer() {
+ public Object transform(Object value) {
+ Method method = (Method)value;
+ int modifiers = Constants.ACC_FINAL
+ | (method.getModifiers()
+ & ~Constants.ACC_ABSTRACT
+ & ~Constants.ACC_NATIVE
+ & ~Constants.ACC_SYNCHRONIZED);
+ if (forcePublic.contains(MethodWrapper.create(method))) {
+ modifiers = (modifiers & ~Constants.ACC_PROTECTED) | Constants.ACC_PUBLIC;
+ }
+ return ReflectUtils.getMethodInfo(method, modifiers);
+ }
+ });
+
+ ClassEmitter e = new ClassEmitter(v);
+ if (currentData == null) {
+ e.begin_class(Constants.V1_2,
+ Constants.ACC_PUBLIC,
+ getClassName(),
+ Type.getType(sc),
+ (useFactory ?
+ TypeUtils.add(TypeUtils.getTypes(interfaces), FACTORY) :
+ TypeUtils.getTypes(interfaces)),
+ Constants.SOURCE_FILE);
+ } else {
+ e.begin_class(Constants.V1_2,
+ Constants.ACC_PUBLIC,
+ getClassName(),
+ null,
+ new Type[]{FACTORY},
+ Constants.SOURCE_FILE);
+ }
+ List constructorInfo = CollectionUtils.transform(constructors, MethodInfoTransformer.getInstance());
+
+ e.declare_field(Constants.ACC_PRIVATE, BOUND_FIELD, Type.BOOLEAN_TYPE, null);
+ e.declare_field(Constants.ACC_PUBLIC | Constants.ACC_STATIC, FACTORY_DATA_FIELD, OBJECT_TYPE, null);
+ if (!interceptDuringConstruction) {
+ e.declare_field(Constants.ACC_PRIVATE, CONSTRUCTED_FIELD, Type.BOOLEAN_TYPE, null);
+ }
+ e.declare_field(Constants.PRIVATE_FINAL_STATIC, THREAD_CALLBACKS_FIELD, THREAD_LOCAL, null);
+ e.declare_field(Constants.PRIVATE_FINAL_STATIC, STATIC_CALLBACKS_FIELD, CALLBACK_ARRAY, null);
+ if (serialVersionUID != null) {
+ e.declare_field(Constants.PRIVATE_FINAL_STATIC, Constants.SUID_FIELD_NAME, Type.LONG_TYPE, serialVersionUID);
+ }
+
+ for (int i = 0; i < callbackTypes.length; i++) {
+ e.declare_field(Constants.ACC_PRIVATE, getCallbackField(i), callbackTypes[i], null);
+ }
+ // This is declared private to avoid "public field" pollution
+ e.declare_field(Constants.ACC_PRIVATE | Constants.ACC_STATIC, CALLBACK_FILTER_FIELD, OBJECT_TYPE, null);
+
+ if (currentData == null) {
+ emitMethods(e, methods, actualMethods);
+ emitConstructors(e, constructorInfo);
+ } else {
+ emitDefaultConstructor(e);
+ }
+ emitSetThreadCallbacks(e);
+ emitSetStaticCallbacks(e);
+ emitBindCallbacks(e);
+
+ if (useFactory || currentData != null) {
+ int[] keys = getCallbackKeys();
+ emitNewInstanceCallbacks(e);
+ emitNewInstanceCallback(e);
+ emitNewInstanceMultiarg(e, constructorInfo);
+ emitGetCallback(e, keys);
+ emitSetCallback(e, keys);
+ emitGetCallbacks(e);
+ emitSetCallbacks(e);
+ }
+
+ e.end_class();
+ }
+
+ /**
+ * Filter the list of constructors from the superclass. The
+ * constructors which remain will be included in the generated
+ * class. The default implementation is to filter out all private
+ * constructors, but subclasses may extend Enhancer to override this
+ * behavior.
+ * @param sc the superclass
+ * @param constructors the list of all declared constructors from the superclass
+ * @throws IllegalArgumentException if there are no non-private constructors
+ */
+ protected void filterConstructors(Class sc, List constructors) {
+ CollectionUtils.filter(constructors, new VisibilityPredicate(sc, true));
+ if (constructors.size() == 0)
+ throw new IllegalArgumentException("No visible constructors in " + sc);
+ }
+
+ /**
+ * This method should not be called in regular flow.
+ * Technically speaking {@link #wrapCachedClass(Class)} uses {@link EnhancerFactoryData} as a cache value,
+ * and the latter enables faster instantiation than plain old reflection lookup and invoke.
+ * This method is left intact for backward compatibility reasons: just in case it was ever used.
+ *
+ * @param type class to instantiate
+ * @return newly created proxy instance
+ * @throws Exception if something goes wrong
+ */
+ protected Object firstInstance(Class type) throws Exception {
+ if (classOnly) {
+ return type;
+ } else {
+ return createUsingReflection(type);
+ }
+ }
+
+ protected Object nextInstance(Object instance) {
+ EnhancerFactoryData data = (EnhancerFactoryData) instance;
+
+ if (classOnly) {
+ return data.generatedClass;
+ }
+
+ Class[] argumentTypes = this.argumentTypes;
+ Object[] arguments = this.arguments;
+ if (argumentTypes == null) {
+ argumentTypes = Constants.EMPTY_CLASS_ARRAY;
+ arguments = null;
+ }
+ return data.newInstance(argumentTypes, arguments, callbacks);
+ }
+
+ @Override
+ protected Object wrapCachedClass(Class klass) {
+ Class[] argumentTypes = this.argumentTypes;
+ if (argumentTypes == null) {
+ argumentTypes = Constants.EMPTY_CLASS_ARRAY;
+ }
+ EnhancerFactoryData factoryData = new EnhancerFactoryData(klass, argumentTypes, classOnly);
+ Field factoryDataField = null;
+ try {
+ // The subsequent dance is performed just once for each class,
+ // so it does not matter much how fast it goes
+ factoryDataField = klass.getField(FACTORY_DATA_FIELD);
+ factoryDataField.set(null, factoryData);
+ Field callbackFilterField = klass.getDeclaredField(CALLBACK_FILTER_FIELD);
+ callbackFilterField.setAccessible(true);
+ callbackFilterField.set(null, this.filter);
+ } catch (NoSuchFieldException e) {
+ throw new CodeGenerationException(e);
+ } catch (IllegalAccessException e) {
+ throw new CodeGenerationException(e);
+ }
+ return new WeakReference(factoryData);
+ }
+
+ @Override
+ protected Object unwrapCachedValue(Object cached) {
+ if (currentKey instanceof EnhancerKey) {
+ EnhancerFactoryData data = ((WeakReference) cached).get();
+ return data;
+ }
+ return super.unwrapCachedValue(cached);
+ }
+
+ /**
+ * Call this method to register the {@link Callback} array to use before
+ * creating a new instance of the generated class via reflection. If you are using
+ * an instance of Enhancer or the {@link Factory} interface to create
+ * new instances, this method is unnecessary. Its primary use is for when you want to
+ * cache and reuse a generated class yourself, and the generated class does
+ * not implement the {@link Factory} interface.
+ *
+ * Note that this method only registers the callbacks on the current thread.
+ * If you want to register callbacks for instances created by multiple threads,
+ * use {@link #registerStaticCallbacks}.
+ *
+ * The registered callbacks are overwritten and subsequently cleared
+ * when calling any of the create methods (such as
+ * {@link #create}), or any {@link Factory} newInstance method.
+ * Otherwise they are not cleared, and you should be careful to set them
+ * back to null after creating new instances via reflection if
+ * memory leakage is a concern.
+ * @param generatedClass a class previously created by {@link Enhancer}
+ * @param callbacks the array of callbacks to use when instances of the generated
+ * class are created
+ * @see #setUseFactory
+ */
+ public static void registerCallbacks(Class generatedClass, Callback[] callbacks) {
+ setThreadCallbacks(generatedClass, callbacks);
+ }
+
+ /**
+ * Similar to {@link #registerCallbacks}, but suitable for use
+ * when multiple threads will be creating instances of the generated class.
+ * The thread-level callbacks will always override the static callbacks.
+ * Static callbacks are never cleared.
+ * @param generatedClass a class previously created by {@link Enhancer}
+ * @param callbacks the array of callbacks to use when instances of the generated
+ * class are created
+ */
+ public static void registerStaticCallbacks(Class generatedClass, Callback[] callbacks) {
+ setCallbacksHelper(generatedClass, callbacks, SET_STATIC_CALLBACKS_NAME);
+ }
+
+ /**
+ * Determine if a class was generated using Enhancer.
+ * @param type any class
+ * @return whether the class was generated using Enhancer
+ */
+ public static boolean isEnhanced(Class type) {
+ try {
+ getCallbacksSetter(type, SET_THREAD_CALLBACKS_NAME);
+ return true;
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ private static void setThreadCallbacks(Class type, Callback[] callbacks) {
+ setCallbacksHelper(type, callbacks, SET_THREAD_CALLBACKS_NAME);
+ }
+
+ private static void setCallbacksHelper(Class type, Callback[] callbacks, String methodName) {
+ // TODO: optimize
+ try {
+ Method setter = getCallbacksSetter(type, methodName);
+ setter.invoke(null, new Object[]{ callbacks });
+ } catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException(type + " is not an enhanced class");
+ } catch (IllegalAccessException e) {
+ throw new CodeGenerationException(e);
+ } catch (InvocationTargetException e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+
+ private static Method getCallbacksSetter(Class type, String methodName) throws NoSuchMethodException {
+ return type.getDeclaredMethod(methodName, new Class[]{ Callback[].class });
+ }
+
+ /**
+ * Instantiates a proxy instance and assigns callback values.
+ * Implementation detail: java.lang.reflect instances are not cached, so this method should not
+ * be used on a hot path.
+ * This method is used when {@link #setUseCache(boolean)} is set to {@code false}.
+ *
+ * @param type class to instantiate
+ * @return newly created instance
+ */
+ private Object createUsingReflection(Class type) {
+ setThreadCallbacks(type, callbacks);
+ try{
+
+ if (argumentTypes != null) {
+
+ return ReflectUtils.newInstance(type, argumentTypes, arguments);
+
+ } else {
+
+ return ReflectUtils.newInstance(type);
+
+ }
+ }finally{
+ // clear thread callbacks to allow them to be gc'd
+ setThreadCallbacks(type, null);
+ }
+ }
+
+ /**
+ * Helper method to create an intercepted object.
+ * For finer control over the generated instance, use a new instance of Enhancer
+ * instead of this static method.
+ * @param type class to extend or interface to implement
+ * @param callback the callback to use for all methods
+ */
+ public static Object create(Class type, Callback callback) {
+ Enhancer e = new Enhancer();
+ e.setSuperclass(type);
+ e.setCallback(callback);
+ return e.create();
+ }
+
+ /**
+ * Helper method to create an intercepted object.
+ * For finer control over the generated instance, use a new instance of Enhancer
+ * instead of this static method.
+ * @param superclass class to extend or interface to implement
+ * @param interfaces array of interfaces to implement, or null
+ * @param callback the callback to use for all methods
+ */
+ public static Object create(Class superclass, Class interfaces[], Callback callback) {
+ Enhancer e = new Enhancer();
+ e.setSuperclass(superclass);
+ e.setInterfaces(interfaces);
+ e.setCallback(callback);
+ return e.create();
+ }
+
+ /**
+ * Helper method to create an intercepted object.
+ * For finer control over the generated instance, use a new instance of Enhancer
+ * instead of this static method.
+ * @param superclass class to extend or interface to implement
+ * @param interfaces array of interfaces to implement, or null
+ * @param filter the callback filter to use when generating a new class
+ * @param callbacks callback implementations to use for the enhanced object
+ */
+ public static Object create(Class superclass, Class[] interfaces, CallbackFilter filter, Callback[] callbacks) {
+ Enhancer e = new Enhancer();
+ e.setSuperclass(superclass);
+ e.setInterfaces(interfaces);
+ e.setCallbackFilter(filter);
+ e.setCallbacks(callbacks);
+ return e.create();
+ }
+
+ private void emitDefaultConstructor(ClassEmitter ce) {
+ Constructor