You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by aw...@apache.org on 2006/12/08 19:32:10 UTC
svn commit: r484693 [2/3] - in /incubator/openjpa/trunk:
openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/
openjpa-kernel/src/main/java/org/apache/openjpa/kernel/
openjpa-kernel/src/main/java/org/apache/openjpa/util/
openjpa-kernel/src/ma...
Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyManagerImpl.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyManagerImpl.java?view=diff&rev=484693&r1=484692&r2=484693
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyManagerImpl.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyManagerImpl.java Fri Dec 8 10:32:07 2006
@@ -15,7 +15,14 @@
*/
package org.apache.openjpa.util;
+import java.io.File;
+import java.io.IOException;
+import java.io.ObjectStreamException;
import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
@@ -24,25 +31,30 @@
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Properties;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TimeZone;
import java.util.TreeMap;
import java.util.TreeSet;
-import java.util.Vector;
-import org.apache.openjpa.conf.OpenJPAConfiguration;
-import org.apache.openjpa.lib.conf.Configurable;
-import org.apache.openjpa.lib.conf.Configuration;
+import org.apache.commons.lang.StringUtils;
+import org.apache.openjpa.kernel.OpenJPAStateManager;
+import org.apache.openjpa.lib.util.Files;
import org.apache.openjpa.lib.util.JavaVersions;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.lib.util.concurrent.ConcurrentHashMap;
+import serp.bytecode.BCClass;
+import serp.bytecode.BCClassLoader;
+import serp.bytecode.BCField;
+import serp.bytecode.BCMethod;
+import serp.bytecode.Code;
+import serp.bytecode.JumpInstruction;
+import serp.bytecode.Project;
+import serp.util.Strings;
/**
* Default implementation of the {@link ProxyManager} interface.
@@ -50,74 +62,36 @@
* @author Abe White
*/
public class ProxyManagerImpl
- implements ProxyManager, Configurable {
+ implements ProxyManager {
+
+ private static final String PROXY_SUFFIX = "$proxy";
private static final Localizer _loc = Localizer.forPackage
(ProxyManagerImpl.class);
- // date proxy cache
- private static final Map _dates = new HashMap();
-
- static {
- _dates.put(Date.class, new ProxyUtilDate());
- _dates.put(java.sql.Date.class, new ProxySQLDate());
- _dates.put(java.sql.Timestamp.class, new ProxyTimestamp());
- _dates.put(java.sql.Time.class, new ProxyTime());
- }
-
- // calendar proxy cache
- private static final Map _calendars = new HashMap();
-
- static {
- ProxyGregorianCalendar cal = new ProxyGregorianCalendar();
- try {
- cal = (ProxyGregorianCalendar) JavaVersions.
- getVersionSpecificClass(ProxyGregorianCalendar.class).
- newInstance();
- } catch (Exception e) {
- }
-
- _calendars.put(Calendar.class, cal);
- _calendars.put(GregorianCalendar.class, cal);
- }
-
- // standard collection proxy cache
+ private static long _proxyId = 0L;
private static final Map _stdCollections = new HashMap();
-
- static {
- ProxyArrayList listTempl = new ProxyArrayList();
- ProxyHashSet setTempl = new ProxyHashSet();
- ProxyTreeSet sortedSetTempl = new ProxyTreeSet();
- _stdCollections.put(Collection.class, listTempl);
- _stdCollections.put(Set.class, setTempl);
- _stdCollections.put(SortedSet.class, sortedSetTempl);
- _stdCollections.put(List.class, listTempl);
- _stdCollections.put(ArrayList.class, listTempl);
- _stdCollections.put(LinkedList.class, new ProxyLinkedList());
- _stdCollections.put(Vector.class, new ProxyVector());
- _stdCollections.put(HashSet.class, setTempl);
- _stdCollections.put(TreeSet.class, sortedSetTempl);
- }
-
- // standard map proxy cache
private static final Map _stdMaps = new HashMap();
-
static {
- ProxyHashMap mapTempl = new ProxyHashMap();
- ProxyTreeMap sortedMapTempl = new ProxyTreeMap();
- _stdMaps.put(Map.class, mapTempl);
- _stdMaps.put(SortedMap.class, sortedMapTempl);
- _stdMaps.put(HashMap.class, mapTempl);
- _stdMaps.put(TreeMap.class, sortedMapTempl);
- _stdMaps.put(Hashtable.class, new ProxyHashtable());
- _stdMaps.put(Properties.class, new ProxyProperties());
- }
+ _stdCollections.put(Collection.class, ArrayList.class);
+ _stdCollections.put(Set.class, HashSet.class);
+ _stdCollections.put(SortedSet.class, TreeSet.class);
+ _stdCollections.put(List.class, ArrayList.class);
+ if (JavaVersions.VERSION >= 5) {
+ try {
+ Class queue = Class.forName("java.util.Queue", false,
+ Collection.class.getClassLoader());
+ _stdCollections.put(queue, LinkedList.class);
+ } catch (Throwable t) {
+ // not really java 5 after all?
+ }
+ }
- // allow subclasses to manipulate collection and map templates
- private final Map _collections = new ConcurrentHashMap(_stdCollections);
- private final Map _maps = new ConcurrentHashMap(_stdMaps);
+ _stdMaps.put(Map.class, HashMap.class);
+ _stdMaps.put(SortedMap.class, TreeMap.class);
+ }
- protected OpenJPAConfiguration conf = null;
+ private final Map _proxies = new ConcurrentHashMap();
private boolean _trackChanges = true;
private boolean _assertType = false;
@@ -157,139 +131,1281 @@
_assertType = assertType;
}
- public Date copyDate(Date orig) {
- return (Date) copy(orig, _dates);
- }
+ public Object copyArray(Object orig) {
+ if (orig == null)
+ return null;
- public Calendar copyCalendar(Calendar orig) {
- return (Calendar) copy(orig, _calendars);
+ try {
+ int length = Array.getLength(orig);
+ Object array = Array.newInstance(orig.getClass().
+ getComponentType(), length);
+
+ System.arraycopy(orig, 0, array, 0, length);
+ return array;
+ } catch (Exception e) {
+ throw new UnsupportedException(_loc.get("bad-array",
+ e.getMessage()), e);
+ }
}
public Collection copyCollection(Collection orig) {
- return (Collection) copy(orig, _collections);
+ if (orig == null)
+ return null;
+ if (orig instanceof Proxy)
+ return (Collection) ((Proxy) orig).copy(orig);
+
+ ProxyCollection proxy = getFactoryProxyCollection(orig.getClass());
+ return (Collection) proxy.copy(orig);
+ }
+
+ public Proxy newCollectionProxy(Class type, Class elementType,
+ Comparator compare) {
+ type = toProxyableCollectionType(type);
+ ProxyCollection proxy = getFactoryProxyCollection(type);
+ return proxy.newInstance((_assertType) ? elementType : null, compare,
+ _trackChanges);
}
public Map copyMap(Map orig) {
- return (Map) copy(orig, _maps);
+ if (orig == null)
+ return null;
+ if (orig instanceof Proxy)
+ return (Map) ((Proxy) orig).copy(orig);
+
+ ProxyMap proxy = getFactoryProxyMap(orig.getClass());
+ return (Map) proxy.copy(orig);
}
- /**
- * Internal helper to copy value.
- */
- private static Object copy(Object orig, Map proxies) {
+ public Proxy newMapProxy(Class type, Class keyType,
+ Class elementType, Comparator compare) {
+ type = toProxyableMapType(type);
+ ProxyMap proxy = getFactoryProxyMap(type);
+ return proxy.newInstance((_assertType) ? keyType : null,
+ (_assertType) ? elementType : null, compare, _trackChanges);
+ }
+
+ public Date copyDate(Date orig) {
if (orig == null)
return null;
if (orig instanceof Proxy)
- return ((Proxy) orig).copy(orig);
+ return (Date) ((Proxy) orig).copy(orig);
- Class type = orig.getClass();
- Proxy proxy = (Proxy) proxies.get(type);
- if (proxy == null)
- throw new UnsupportedException(_loc.get("bad-proxy", type));
- return proxy.copy(orig);
+ ProxyDate proxy = getFactoryProxyDate(orig.getClass());
+ return (Date) proxy.copy(orig);
}
- public Object copyArray(Object orig) {
+ public Proxy newDateProxy(Class type) {
+ ProxyDate proxy = getFactoryProxyDate(type);
+ return proxy.newInstance();
+ }
+
+ public Calendar copyCalendar(Calendar orig) {
if (orig == null)
return null;
+ if (orig instanceof Proxy)
+ return (Calendar) ((Proxy) orig).copy(orig);
- try {
- int length = Array.getLength(orig);
- Object array = Array.newInstance(orig.getClass().
- getComponentType(), length);
+ ProxyCalendar proxy = getFactoryProxyCalendar(orig.getClass());
+ return (Calendar) proxy.copy(orig);
+ }
- System.arraycopy(orig, 0, array, 0, length);
- return array;
- } catch (Exception e) {
- throw new UnsupportedException(_loc.get("bad-array",
- e.getMessage()), e);
- }
+ public Proxy newCalendarProxy(Class type, TimeZone zone) {
+ if (type == Calendar.class)
+ type = GregorianCalendar.class;
+ ProxyCalendar proxy = getFactoryProxyCalendar(type);
+ ProxyCalendar cal = proxy.newInstance();
+ ((Calendar) cal).setTimeZone(zone);
+ return cal;
}
public Object copyCustom(Object orig) {
- if (!(orig instanceof Proxy))
+ if (orig == null)
return null;
- return ((Proxy) orig).copy(orig);
+ if (orig instanceof Proxy)
+ return ((Proxy) orig).copy(orig);
+ ProxyBean proxy = getFactoryProxyBean(orig);
+ return (proxy == null) ? null : proxy.copy(orig);
}
- public Proxy newDateProxy(Class type) {
- ProxyDate pd = (ProxyDate) findProxy(type, ProxyDate.class, _dates);
- return pd.newInstance();
+ public Proxy newCustomProxy(Object orig) {
+ if (orig == null)
+ return null;
+ if (orig instanceof Proxy)
+ return (Proxy) orig;
+
+ ProxyBean proxy = getFactoryProxyBean(orig);
+ return (proxy == null) ? null : proxy.newInstance(orig);
}
- public Proxy newCalendarProxy(Class type, TimeZone timeZone) {
- ProxyCalendar pc = (ProxyCalendar) findProxy(type,
- ProxyCalendar.class, _calendars);
- return pc.newInstance(timeZone);
+ /**
+ * Return the concrete type for proxying.
+ */
+ protected Class toProxyableCollectionType(Class type) {
+ if (type.getName().endsWith(PROXY_SUFFIX))
+ type = type.getSuperclass();
+ else if (type.isInterface()) {
+ type = toConcreteType(type, _stdCollections);
+ if (type == null)
+ throw new UnsupportedException(_loc.get("no-proxy-intf", type));
+ } else if (Modifier.isAbstract(type.getModifiers()))
+ throw new UnsupportedException(_loc.get("no-proxy-abstract", type));
+ return type;
}
- public Proxy newCollectionProxy(Class type, Class elementType,
- Comparator compare) {
- ProxyCollection pc = (ProxyCollection) findProxy(type,
- ProxyCollection.class, _collections);
- if (!_assertType)
- elementType = null;
- return pc.newInstance(elementType, compare, _trackChanges, conf);
+ /**
+ * Return the concrete type for proxying.
+ */
+ protected Class toProxyableMapType(Class type) {
+ if (type.getName().endsWith(PROXY_SUFFIX))
+ type = type.getSuperclass();
+ else if (type.isInterface()) {
+ type = toConcreteType(type, _stdMaps);
+ if (type == null)
+ throw new UnsupportedException(_loc.get("no-proxy-intf", type));
+ } else if (Modifier.isAbstract(type.getModifiers()))
+ throw new UnsupportedException(_loc.get("no-proxy-abstract", type));
+ return type;
}
- public Proxy newMapProxy(Class type, Class keyType, Class valueType,
- Comparator compare) {
- ProxyMap pm = (ProxyMap) findProxy(type, ProxyMap.class, _maps);
- if (!_assertType) {
- keyType = null;
- valueType = null;
- }
- return pm.newInstance(keyType, valueType, compare, _trackChanges, conf);
+ /**
+ * Locate a concrete type to proxy for the given collection interface.
+ */
+ private static Class toConcreteType(Class intf, Map concretes) {
+ Class concrete = (Class) concretes.get(intf);
+ if (concrete != null)
+ return concrete;
+ Class[] intfs = intf.getInterfaces();
+ for (int i = 0; i < intfs.length; i++) {
+ concrete = toConcreteType(intfs[i], concretes);
+ if (concrete != null)
+ return concrete;
+ }
+ return null;
+ }
+
+ /**
+ * Return the cached factory proxy for the given collection type.
+ */
+ private ProxyCollection getFactoryProxyCollection(Class type) {
+ // we don't lock here; ok if two proxies get generated for same type
+ ProxyCollection proxy = (ProxyCollection) _proxies.get(type);
+ if (proxy == null) {
+ proxy = (ProxyCollection) instantiateProxy
+ (generateProxyCollectionBytecode(type), type,
+ ProxyCollection.class, null, null);
+ _proxies.put(type, proxy);
+ }
+ return proxy;
+ }
+
+ /**
+ * Return the cached factory proxy for the given map type.
+ */
+ private ProxyMap getFactoryProxyMap(Class type) {
+ // we don't lock here; ok if two proxies get generated for same type
+ ProxyMap proxy = (ProxyMap) _proxies.get(type);
+ if (proxy == null) {
+ proxy = (ProxyMap) instantiateProxy(generateProxyMapBytecode(type),
+ type, ProxyMap.class, null, null);
+ _proxies.put(type, proxy);
+ }
+ return proxy;
+ }
+
+ /**
+ * Return the cached factory proxy for the given date type.
+ */
+ private ProxyDate getFactoryProxyDate(Class type) {
+ // we don't lock here; ok if two proxies get generated for same type
+ ProxyDate proxy = (ProxyDate) _proxies.get(type);
+ if (proxy == null) {
+ proxy = (ProxyDate) instantiateProxy
+ (generateProxyDateBytecode(type), type, ProxyDate.class, null,
+ null);
+ _proxies.put(type, proxy);
+ }
+ return proxy;
+ }
+
+ /**
+ * Return the cached factory proxy for the given calendar type.
+ */
+ private ProxyCalendar getFactoryProxyCalendar(Class type) {
+ // we don't lock here; ok if two proxies get generated for same type
+ ProxyCalendar proxy = (ProxyCalendar) _proxies.get(type);
+ if (proxy == null) {
+ proxy = (ProxyCalendar) instantiateProxy
+ (generateProxyCalendarBytecode(type), type, ProxyCalendar.class,
+ null, null);
+ _proxies.put(type, proxy);
+ }
+ return proxy;
+ }
+
+ /**
+ * Return the cached factory proxy for the given bean type.
+ */
+ private ProxyBean getFactoryProxyBean(Object orig) {
+ // we don't lock here; ok if two proxies get generated for same type
+ ProxyBean proxy = (ProxyBean) _proxies.get(orig.getClass());
+ if (proxy == null && !_proxies.containsKey(orig.getClass())) {
+ BCClass bc = generateProxyBeanBytecode(orig.getClass());
+ if (bc == null)
+ _proxies.put(orig.getClass(), null);
+ else {
+ BCMethod m = bc.getDeclaredMethod("<init>", (Class[]) null);
+ proxy = (ProxyBean) instantiateProxy(bc, orig.getClass(),
+ ProxyBean.class, findCopyConstructor(orig.getClass()),
+ new Object[] {orig});
+ _proxies.put(orig.getClass(), proxy);
+ }
+ }
+ return proxy;
+ }
+
+ /**
+ * Instantiate the given proxy bytecode.
+ */
+ private Proxy instantiateProxy(BCClass bc, Class type, Class proxy,
+ Constructor cons, Object[] args) {
+ BCClassLoader loader = new BCClassLoader(bc.getProject(),
+ getMostDerivedClassLoader(type, proxy));
+ try {
+ Class cls = Class.forName(bc.getName(), true, loader);
+ if (cons != null)
+ return (Proxy) cls.getConstructor(cons.getParameterTypes()).
+ newInstance(args);
+ return (Proxy) cls.newInstance();
+ } catch (InstantiationException ie) {
+ throw new UnsupportedException(_loc.get("cant-classforname",
+ bc.getSuperclassName()));
+ } catch (Throwable t) {
+ throw new GeneralException(t);
+ }
+ }
+
+ /**
+ * Return the more derived loader of the class laoders for the given
+ * classes.
+ */
+ private static ClassLoader getMostDerivedClassLoader(Class c1, Class c2) {
+ ClassLoader l1 = c1.getClassLoader();
+ ClassLoader l2 = c2.getClassLoader();
+ if (l1 == l2)
+ return l1;
+ if (l1 == null)
+ return l2;
+ if (l2 == null)
+ return l1;
+
+ for (ClassLoader p = l1.getParent(); p != null; p = p.getParent())
+ if (p == l2)
+ return l1;
+ return l2;
+ }
+
+ /**
+ * Generate the bytecode for a collection proxy for the given type.
+ */
+ protected BCClass generateProxyCollectionBytecode(Class type) {
+ Project project = new Project();
+ BCClass bc = project.loadClass(Strings.getPackageName
+ (ProxyManagerImpl.class) + "." + type.getName().replace('.', '$')
+ + "$" + nextProxyId() + PROXY_SUFFIX);
+ bc.setSuperclass(type);
+ bc.declareInterface(ProxyCollection.class);
+
+ delegateConstructors(bc, type);
+ addProxyMethods(bc, type, false);
+ addProxyCollectionMethods(bc, type);
+ proxyRecognizedMethods(bc, type, ProxyCollections.class,
+ ProxyCollection.class);
+ proxySetters(bc, type);
+ addWriteReplaceMethod(bc);
+ return bc;
+ }
+
+ /**
+ * Generate the bytecode for a map proxy for the given type.
+ */
+ protected BCClass generateProxyMapBytecode(Class type) {
+ Project project = new Project();
+ BCClass bc = project.loadClass(Strings.getPackageName
+ (ProxyManagerImpl.class) + "." + type.getName().replace('.', '$')
+ + "$" + nextProxyId() + PROXY_SUFFIX);
+ bc.setSuperclass(type);
+ bc.declareInterface(ProxyMap.class);
+
+ delegateConstructors(bc, type);
+ addProxyMethods(bc, type, false);
+ addProxyMapMethods(bc, type);
+ proxyRecognizedMethods(bc, type, ProxyMaps.class, ProxyMap.class);
+ proxySetters(bc, type);
+ addWriteReplaceMethod(bc);
+ return bc;
+ }
+
+ /**
+ * Generate the bytecode for a date proxy for the given type.
+ */
+ protected BCClass generateProxyDateBytecode(Class type) {
+ Project project = new Project();
+ BCClass bc = project.loadClass(Strings.getPackageName
+ (ProxyManagerImpl.class) + "." + type.getName().replace('.', '$')
+ + "$" + nextProxyId() + PROXY_SUFFIX);
+ bc.setSuperclass(type);
+ bc.declareInterface(ProxyDate.class);
+
+ delegateConstructors(bc, type);
+ addProxyMethods(bc, type, true);
+ addProxyDateMethods(bc, type);
+ proxySetters(bc, type);
+ addWriteReplaceMethod(bc);
+ return bc;
+ }
+
+ /**
+ * Generate the bytecode for a calendar proxy for the given type.
+ */
+ protected BCClass generateProxyCalendarBytecode(Class type) {
+ Project project = new Project();
+ BCClass bc = project.loadClass(Strings.getPackageName
+ (ProxyManagerImpl.class) + "." + type.getName().replace('.', '$')
+ + "$" + nextProxyId() + PROXY_SUFFIX);
+ bc.setSuperclass(type);
+ bc.declareInterface(ProxyCalendar.class);
+
+ delegateConstructors(bc, type);
+ addProxyMethods(bc, type, true);
+ addProxyCalendarMethods(bc, type);
+ proxySetters(bc, type);
+ addWriteReplaceMethod(bc);
+ return bc;
+ }
+
+ /**
+ * Generate the bytecode for a bean proxy for the given type.
+ */
+ protected BCClass generateProxyBeanBytecode(Class type) {
+ // we can only generate a valid proxy if there is a copy constructor
+ // or a default constructor
+ Constructor cons = findCopyConstructor(type);
+ if (cons == null) {
+ Constructor[] cs = type.getConstructors();
+ for (int i = 0; cons == null && i < cs.length; i++)
+ if (cs[i].getParameterTypes().length == 0)
+ cons = cs[i];
+ if (cons == null)
+ return null;
+ }
+
+ Project project = new Project();
+ BCClass bc = project.loadClass(Strings.getPackageName
+ (ProxyManagerImpl.class) + "." + type.getName().replace('.', '$')
+ + "$" + nextProxyId() + PROXY_SUFFIX);
+ bc.setSuperclass(type);
+ bc.declareInterface(ProxyBean.class);
+
+ delegateConstructors(bc, type);
+ addProxyMethods(bc, type, true);
+ addProxyBeanMethods(bc, type, cons);
+ proxySetters(bc, type);
+ addWriteReplaceMethod(bc);
+ return bc;
}
/**
- * Helper method to find an existing proxy for the given type.
- */
- private static Proxy findProxy(Class type, Class proxyType, Map proxies) {
- Proxy p = (Proxy) proxies.get(type);
-
- if (p == null) {
- // check for custom proxy
- if (proxyType.isAssignableFrom(type)) {
- try {
- p = (Proxy) type.newInstance();
- proxies.put(type, p);
- } catch (Exception e) {
- throw new UnsupportedException(_loc.get("no-proxy-cons",
- type), e);
- }
+ * Create pass-through constructors to base type.
+ */
+ private void delegateConstructors(BCClass bc, Class type) {
+ Constructor[] cons = type.getConstructors();
+ Class[] params;
+ BCMethod m;
+ Code code;
+ for (int i = 0; i < cons.length; i++) {
+ params = cons[i].getParameterTypes();
+ m = bc.declareMethod("<init>", void.class, params);
+ m.makePublic();
+
+ code = m.getCode(true);
+ code.aload().setThis();
+ for (int j = 0; j < params.length; j++)
+ code.xload().setParam(j).setType(params[j]);
+ code.invokespecial().setMethod(cons[i]);
+ code.vreturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+ }
+ }
+
+ /**
+ * Implement the methods in the {@link Proxy} interface, with the exception
+ * of {@link Proxy#copy}.
+ *
+ * @param changeTracker whether to implement a null change tracker; if false
+ * the change tracker method is left unimplemented
+ */
+ private void addProxyMethods(BCClass bc, Class type,
+ boolean changeTracker) {
+ BCField sm = bc.declareField("sm", OpenJPAStateManager.class);
+ BCField field = bc.declareField("field", int.class);
+
+ BCMethod m = bc.declareMethod("setOwner", void.class, new Class[] {
+ OpenJPAStateManager.class, int.class });
+ m.makePublic();
+ Code code = m.getCode(true);
+ code.aload().setThis();
+ code.aload().setParam(0);
+ code.putfield().setField(sm);
+ code.aload().setThis();
+ code.iload().setParam(1);
+ code.putfield().setField(field);
+ code.vreturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+
+ m = bc.declareMethod("getOwner", OpenJPAStateManager.class, null);
+ m.makePublic();
+ code = m.getCode(true);
+ code.aload().setThis();
+ code.getfield().setField(sm);
+ code.areturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+
+ m = bc.declareMethod("getOwnerField", int.class, null);
+ m.makePublic();
+ code = m.getCode(true);
+ code.aload().setThis();
+ code.getfield().setField(field);
+ code.ireturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+
+ if (changeTracker) {
+ m = bc.declareMethod("getChangeTracker", ChangeTracker.class, null);
+ m.makePublic();
+ code = m.getCode(true);
+ code.constant().setNull();
+ code.areturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+ }
+ }
+
+ /**
+ * Implement the methods in the {@link ProxyCollection} interface.
+ */
+ private void addProxyCollectionMethods(BCClass bc, Class type) {
+ // change tracker
+ BCField changeTracker = bc.declareField("changeTracker",
+ CollectionChangeTracker.class);
+ BCMethod m = bc.declareMethod("getChangeTracker", ChangeTracker.class,
+ null);
+ m.makePublic();
+ Code code = m.getCode(true);
+ code.aload().setThis();
+ code.getfield().setField(changeTracker);
+ code.areturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+
+ // collection copy
+ Constructor cons = findCopyConstructor(type);
+ if (cons == null && SortedSet.class.isAssignableFrom(type))
+ cons = findComparatorConstructor(type);
+ Class[] params = (cons == null) ? new Class[0]
+ : cons.getParameterTypes();
+
+ m = bc.declareMethod("copy", Object.class, new Class[] {Object.class});
+ m.makePublic();
+ code = m.getCode(true);
+
+ code.anew().setType(type);
+ code.dup();
+ if (params.length == 1) {
+ code.aload().setParam(0);
+ if (params[0] == Comparator.class) {
+ code.checkcast().setType(SortedSet.class);
+ code.invokeinterface().setMethod(SortedSet.class, "comparator",
+ Comparator.class, null);
} else
- throw new UnsupportedException(_loc.get("bad-proxy", type));
+ code.checkcast().setType(params[0]);
+ }
+ code.invokespecial().setMethod(type, "<init>", void.class, params);
+ if (params.length == 0 || params[0] == Comparator.class) {
+ code.dup();
+ code.aload().setParam(0);
+ code.checkcast().setType(Collection.class);
+ code.invokevirtual().setMethod(type, "addAll", boolean.class,
+ new Class[] { Collection.class });
+ code.pop();
+ }
+ code.areturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+
+ // element type
+ BCField elementType = bc.declareField("elementType", Class.class);
+ m = bc.declareMethod("getElementType", Class.class, null);
+ m.makePublic();
+ code = m.getCode(true);
+ code.aload().setThis();
+ code.getfield().setField(elementType);
+ code.areturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+
+ // new instance factory
+ m = bc.declareMethod("newInstance", ProxyCollection.class,
+ new Class[] { Class.class, Comparator.class, boolean.class });
+ m.makePublic();
+ code = m.getCode(true);
+
+ code.anew().setType(bc);
+ code.dup();
+ cons = findComparatorConstructor(type);
+ params = (cons == null) ? new Class[0] : cons.getParameterTypes();
+ if (params.length == 1)
+ code.aload().setParam(1);
+ code.invokespecial().setMethod("<init>", void.class, params);
+ int ret = code.getNextLocalsIndex();
+ code.astore().setLocal(ret);
+
+ // set element type
+ code.aload().setLocal(ret);
+ code.aload().setParam(0);
+ code.putfield().setField(elementType);
+
+ // create change tracker and set it
+ code.iload().setParam(2);
+ JumpInstruction ifins = code.ifeq();
+ code.aload().setLocal(ret);
+ code.anew().setType(CollectionChangeTrackerImpl.class);
+ code.dup();
+ code.aload().setLocal(ret);
+ code.constant().setValue(allowsDuplicates(type));
+ code.constant().setValue(isOrdered(type));
+ code.invokespecial().setMethod(CollectionChangeTrackerImpl.class,
+ "<init>", void.class, new Class[] { Collection.class,
+ boolean.class, boolean.class });
+ code.putfield().setField(changeTracker);
+
+ ifins.setTarget(code.aload().setLocal(ret));
+ code.areturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+ }
+
+ /**
+ * Return whether the given collection type allows duplicates.
+ */
+ protected boolean allowsDuplicates(Class type) {
+ return !Set.class.isAssignableFrom(type);
+ }
+
+ /**
+ * Return whether the given collection type maintains an artificial
+ * ordering.
+ */
+ protected boolean isOrdered(Class type) {
+ return List.class.isAssignableFrom(type)
+ || "java.util.LinkedHashSet".equals(type.getName());
+ }
+
+ /**
+ * Implement the methods in the {@link ProxyMap} interface.
+ */
+ private void addProxyMapMethods(BCClass bc, Class type) {
+ // change tracker
+ BCField changeTracker = bc.declareField("changeTracker",
+ MapChangeTracker.class);
+ BCMethod m = bc.declareMethod("getChangeTracker", ChangeTracker.class,
+ null);
+ m.makePublic();
+ Code code = m.getCode(true);
+ code.aload().setThis();
+ code.getfield().setField(changeTracker);
+ code.areturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+
+ // map copy
+ Constructor cons = findCopyConstructor(type);
+ if (cons == null && SortedMap.class.isAssignableFrom(type))
+ cons = findComparatorConstructor(type);
+ Class[] params = (cons == null) ? new Class[0]
+ : cons.getParameterTypes();
+
+ m = bc.declareMethod("copy", Object.class, new Class[] {Object.class});
+ m.makePublic();
+ code = m.getCode(true);
+
+ code.anew().setType(type);
+ code.dup();
+ if (params.length == 1) {
+ code.aload().setParam(0);
+ if (params[0] == Comparator.class) {
+ code.checkcast().setType(SortedMap.class);
+ code.invokeinterface().setMethod(SortedMap.class, "comparator",
+ Comparator.class, null);
+ } else
+ code.checkcast().setType(params[0]);
}
- return p;
+ code.invokespecial().setMethod(type, "<init>", void.class, params);
+ if (params.length == 0 || params[0] == Comparator.class) {
+ code.dup();
+ code.aload().setParam(0);
+ code.checkcast().setType(Map.class);
+ code.invokevirtual().setMethod(type, "putAll", void.class,
+ new Class[] { Map.class });
+ }
+ code.areturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+
+ // key type
+ BCField keyType = bc.declareField("keyType", Class.class);
+ m = bc.declareMethod("getKeyType", Class.class, null);
+ m.makePublic();
+ code = m.getCode(true);
+ code.aload().setThis();
+ code.getfield().setField(keyType);
+ code.areturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+
+ // value type
+ BCField valueType = bc.declareField("valueType", Class.class);
+ m = bc.declareMethod("getValueType", Class.class, null);
+ m.makePublic();
+ code = m.getCode(true);
+ code.aload().setThis();
+ code.getfield().setField(valueType);
+ code.areturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+
+ // new instance factory
+ m = bc.declareMethod("newInstance", ProxyMap.class,
+ new Class[] { Class.class, Class.class, Comparator.class,
+ boolean.class });
+ m.makePublic();
+ code = m.getCode(true);
+
+ code.anew().setType(bc);
+ code.dup();
+ cons = findComparatorConstructor(type);
+ params = (cons == null) ? new Class[0] : cons.getParameterTypes();
+ if (params.length == 1)
+ code.aload().setParam(2);
+ code.invokespecial().setMethod("<init>", void.class, params);
+ int ret = code.getNextLocalsIndex();
+ code.astore().setLocal(ret);
+
+ // set key and value types
+ code.aload().setLocal(ret);
+ code.aload().setParam(0);
+ code.putfield().setField(keyType);
+ code.aload().setLocal(ret);
+ code.aload().setParam(1);
+ code.putfield().setField(valueType);
+
+ // create change tracker and set it
+ code.iload().setParam(3);
+ JumpInstruction ifins = code.ifeq();
+ code.aload().setLocal(ret);
+ code.anew().setType(MapChangeTrackerImpl.class);
+ code.dup();
+ code.aload().setLocal(ret);
+ code.invokespecial().setMethod(MapChangeTrackerImpl.class,
+ "<init>", void.class, new Class[] { Map.class });
+ code.putfield().setField(changeTracker);
+
+ ifins.setTarget(code.aload().setLocal(ret));
+ code.areturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
}
- public Proxy newCustomProxy(Object obj) {
- return (obj instanceof Proxy) ? (Proxy) obj : null;
+ /**
+ * Implement the methods in the {@link ProxyDate} interface.
+ */
+ private void addProxyDateMethods(BCClass bc, Class type) {
+ boolean hasDefaultCons = bc.getDeclaredMethod("<init>",
+ (Class[]) null) != null;
+ boolean hasMillisCons = bc.getDeclaredMethod("<init>",
+ new Class[] { long.class }) != null;
+ if (!hasDefaultCons && !hasMillisCons)
+ throw new UnsupportedException(_loc.get("no-date-cons", type));
+
+ // add a default constructor that delegates to the millis constructor
+ BCMethod m;
+ Code code;
+ if (!hasDefaultCons) {
+ m = bc.declareMethod("<init>", void.class, null);
+ m.makePublic();
+ code = m.getCode(true);
+ code.aload().setThis();
+ code.invokestatic().setMethod(System.class, "currentTimeMillis",
+ long.class, null);
+ code.invokespecial().setMethod(type, "<init>", void.class,
+ new Class[] { long.class });
+ code.vreturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+ }
+
+ // date copy
+ Constructor cons = findCopyConstructor(type);
+ Class[] params;
+ if (cons != null)
+ params = cons.getParameterTypes();
+ else if (hasMillisCons)
+ params = new Class[] { long.class };
+ else
+ params = new Class[0];
+
+ m = bc.declareMethod("copy", Object.class, new Class[] {Object.class});
+ m.makePublic();
+ code = m.getCode(true);
+
+ code.anew().setType(type);
+ code.dup();
+ if (params.length == 1) {
+ if (params[0] == long.class) {
+ code.aload().setParam(0);
+ code.checkcast().setType(Date.class);
+ code.invokevirtual().setMethod(Date.class, "getTime",
+ long.class, null);
+ } else {
+ code.aload().setParam(0);
+ code.checkcast().setType(params[0]);
+ }
+ }
+ code.invokespecial().setMethod(type, "<init>", void.class, params);
+ if (params.length == 0) {
+ code.dup();
+ code.aload().setParam(0);
+ code.checkcast().setType(Date.class);
+ code.invokevirtual().setMethod(Date.class, "getTime", long.class,
+ null);
+ code.invokevirtual().setMethod(type, "setTime", void.class,
+ new Class[] { long.class });
+ }
+ if ((params.length == 0 || params[0] == long.class)
+ && Timestamp.class.isAssignableFrom(type)) {
+ code.dup();
+ code.aload().setParam(0);
+ code.checkcast().setType(Timestamp.class);
+ code.invokevirtual().setMethod(Timestamp.class, "getNanos",
+ int.class, null);
+ code.invokevirtual().setMethod(type, "setNanos", void.class,
+ new Class[] { int.class });
+ }
+ code.areturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+
+ // new instance factory
+ m = bc.declareMethod("newInstance", ProxyDate.class, null);
+ m.makePublic();
+ code = m.getCode(true);
+ code.anew().setType(bc);
+ code.dup();
+ code.invokespecial().setMethod("<init>", void.class, null);
+ code.areturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
}
/**
- * Add a supported proxy collection type.
+ * Implement the methods in the {@link ProxyCalendar} interface.
*/
- protected void setProxyTemplate(Class collType, ProxyCollection proxy) {
- _collections.put(collType, proxy);
+ private void addProxyCalendarMethods(BCClass bc, Class type) {
+ // calendar copy
+ Constructor cons = findCopyConstructor(type);
+ Class[] params = (cons == null) ? new Class[0]
+ : cons.getParameterTypes();
+
+ BCMethod m = bc.declareMethod("copy", Object.class,
+ new Class[] {Object.class});
+ m.makePublic();
+ Code code = m.getCode(true);
+
+ code.anew().setType(type);
+ code.dup();
+ if (params.length == 1) {
+ code.aload().setParam(0);
+ code.checkcast().setType(params[0]);
+ }
+ code.invokespecial().setMethod(type, "<init>", void.class, params);
+ if (params.length == 0) {
+ code.dup();
+ code.aload().setParam(0);
+ code.checkcast().setType(Calendar.class);
+ code.invokevirtual().setMethod(Calendar.class, "getTimeInMillis",
+ long.class, null);
+ code.invokevirtual().setMethod(type, "setTimeInMillis", void.class,
+ new Class[] { long.class });
+
+ code.dup();
+ code.aload().setParam(0);
+ code.checkcast().setType(Calendar.class);
+ code.invokevirtual().setMethod(Calendar.class, "getTimeZone",
+ TimeZone.class, null);
+ code.invokevirtual().setMethod(type, "setTimeZone", void.class,
+ new Class[] { TimeZone.class });
+ }
+ code.areturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+
+ // new instance factory
+ m = bc.declareMethod("newInstance", ProxyCalendar.class, null);
+ m.makePublic();
+ code = m.getCode(true);
+ code.anew().setType(bc);
+ code.dup();
+ code.invokespecial().setMethod("<init>", void.class, null);
+ code.areturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+
+ // proxy the protected computeFields method b/c it is called on
+ // mutate, and some setters are final and therefore not proxyable
+ m = bc.declareMethod("computeFields", void.class, null);
+ m.makeProtected();
+ code = m.getCode(true);
+ code.aload().setThis();
+ code.constant().setValue(true);
+ code.invokestatic().setMethod(Proxies.class, "dirty", void.class,
+ new Class[] { Proxy.class, boolean.class });
+ code.aload().setThis();
+ code.invokespecial().setMethod(type, "computeFields", void.class, null);
+ code.vreturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+ }
+
+ /**
+ * Implement the methods in the {@link ProxyBean} interface.
+ */
+ private void addProxyBeanMethods(BCClass bc, Class type, Constructor cons) {
+ // bean copy
+ BCMethod m = bc.declareMethod("copy", Object.class,
+ new Class[] { Object.class });
+ m.makePublic();
+ Code code = m.getCode(true);
+
+ code.anew().setType(type);
+ code.dup();
+ Class[] params = cons.getParameterTypes();
+ if (params.length == 1) {
+ code.aload().setParam(0);
+ code.checkcast().setType(params[0]);
+ }
+ code.invokespecial().setMethod(cons);
+ if (params.length == 0)
+ copyProperties(type, code);
+ code.areturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+
+ // new instance factory
+ m = bc.declareMethod("newInstance", ProxyBean.class,
+ new Class[] { Object.class });
+ m.makePublic();
+ code = m.getCode(true);
+ code.anew().setType(bc);
+ code.dup();
+ if (params.length == 1) {
+ code.aload().setParam(0);
+ code.checkcast().setType(params[0]);
+ }
+ code.invokespecial().setMethod("<init>", void.class, params);
+ if (params.length == 0)
+ copyProperties(type, code);
+ code.areturn();
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
}
/**
- * Add a supported proxy map type.
+ * Copy bean properties. Called with the copy object on the stack. Must
+ * return with the copy object on the stack.
*/
- protected void setProxyTemplate(Class mapType, ProxyMap proxy) {
- _maps.put(mapType, proxy);
+ private void copyProperties(Class type, Code code) {
+ int copy = code.getNextLocalsIndex();
+ code.astore().setLocal(copy);
+
+ Method[] meths = type.getMethods();
+ Method getter;
+ for (int i = 0; i < meths.length; i++) {
+ if (!startsWith(meths[i].getName(), "set")
+ || meths[i].getParameterTypes().length != 1)
+ continue;
+ getter = findGetter(type, meths[i]);
+ if (getter == null)
+ continue;
+
+ // copy.setXXX(orig.getXXX());
+ code.aload().setLocal(copy);
+ code.aload().setParam(0);
+ code.checkcast().setType(type);
+ code.invokevirtual().setMethod(getter);
+ code.invokevirtual().setMethod(meths[i]);
+ }
+ code.aload().setLocal(copy);
}
- public void setConfiguration(Configuration conf) {
- this.conf = (OpenJPAConfiguration) conf;
+ /**
+ * Proxy recognized methods to invoke helpers in given helper class.
+ */
+ private void proxyRecognizedMethods(BCClass bc, Class type, Class helper,
+ Class proxyType) {
+ Method[] meths = type.getMethods();
+ Class[] params;
+ Class[] afterParams;
+ Method match;
+ Method after;
+ for (int i = 0; i < meths.length; i++) {
+ params = toHelperParameters(meths[i].getParameterTypes(),
+ proxyType);
+
+ // first check for overriding method
+ try {
+ match = helper.getMethod(meths[i].getName(), params);
+ proxyOverrideMethod(bc, meths[i], match, params);
+ continue;
+ } catch (NoSuchMethodException nsme) {
+ } catch (Exception e) {
+ throw new GeneralException(e);
+ }
+
+ // check for before and after methods, either of which may not
+ // exist
+ match = null;
+ try {
+ match = helper.getMethod("before"
+ + StringUtils.capitalize(meths[i].getName()), params);
+ } catch (NoSuchMethodException nsme) {
+ } catch (Exception e) {
+ throw new GeneralException(e);
+ }
+ after = null;
+ afterParams = null;
+ try {
+ afterParams = toHelperAfterParameters(params,
+ meths[i].getReturnType(), (match == null)
+ ? void.class : match.getReturnType());
+ after = helper.getMethod("after"
+ + StringUtils.capitalize(meths[i].getName()), afterParams);
+ } catch (NoSuchMethodException nsme) {
+ } catch (Exception e) {
+ throw new GeneralException(e);
+ }
+ if (match != null || after != null)
+ proxyBeforeAfterMethod(bc, meths[i], match, params, after,
+ afterParams);
+ }
}
- public void startConfiguration() {
+ /**
+ * Return the parameter types to the corresponding helper class method.
+ */
+ private static Class[] toHelperParameters(Class[] cls, Class helper) {
+ Class[] params = new Class[cls.length + 1];
+ params[0] = helper;
+ System.arraycopy(cls, 0, params, 1, cls.length);
+ return params;
}
- public void endConfiguration ()
- {
- }
+ /**
+ * Return the parameter types to the corresponding helper class "after"
+ * method.
+ */
+ private static Class[] toHelperAfterParameters(Class[] cls, Class ret,
+ Class beforeRet) {
+ if (ret == void.class && beforeRet == void.class)
+ return cls;
+ int len = cls.length;
+ if (ret != void.class)
+ len++;
+ if (beforeRet != void.class)
+ len++;
+ Class[] params = new Class[len];
+ System.arraycopy(cls, 0, params, 0, cls.length);
+ int pos = cls.length;
+ if (ret != void.class)
+ params[pos++] = ret;
+ if (beforeRet != void.class)
+ params[pos++] = beforeRet;
+ return params;
+ }
+
+ /**
+ * Proxy setter methods of the given type.
+ */
+ private void proxySetters(BCClass bc, Class type) {
+ Method[] meths = type.getMethods();
+ for (int i = 0; i < meths.length; i++) {
+ if (isSetter(meths[i]) && !Modifier.isFinal(meths[i].getModifiers())
+ && bc.getDeclaredMethod(meths[i].getName(),
+ meths[i].getParameterTypes()) == null) {
+ proxySetter(bc, meths[i]);
+ }
+ }
+ }
+
+ /**
+ * Proxy the given method with one that overrides it by calling into the
+ * given helper.
+ */
+ private void proxyOverrideMethod(BCClass bc, Method meth,
+ Method helper, Class[] params) {
+ BCMethod m = bc.declareMethod(meth.getName(), meth.getReturnType(),
+ meth.getParameterTypes());
+ m.makePublic();
+ Code code = m.getCode(true);
+
+ code.aload().setThis();
+ for (int i = 1; i < params.length; i++)
+ code.xload().setParam(i - 1).setType(params[i]);
+ code.invokestatic().setMethod(helper);
+ code.xreturn().setType(meth.getReturnType());
+
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+ }
+
+ /**
+ * Proxy the given method with one that overrides it by calling into the
+ * given helper.
+ */
+ private void proxyBeforeAfterMethod(BCClass bc, Method meth,
+ Method before, Class[] params, Method after, Class[] afterParams) {
+ BCMethod m = bc.declareMethod(meth.getName(), meth.getReturnType(),
+ meth.getParameterTypes());
+ m.makePublic();
+ Code code = m.getCode(true);
+
+ // invoke before
+ int beforeRet = -1;
+ if (before != null) {
+ code.aload().setThis();
+ for (int i = 1; i < params.length; i++)
+ code.xload().setParam(i - 1).setType(params[i]);
+ code.invokestatic().setMethod(before);
+ if (after != null && before.getReturnType() != void.class) {
+ beforeRet = code.getNextLocalsIndex();
+ code.xstore().setLocal(beforeRet).
+ setType(before.getReturnType());
+ }
+ }
+
+ // invoke super
+ code.aload().setThis();
+ for (int i = 1; i < params.length; i++)
+ code.xload().setParam(i - 1).setType(params[i]);
+ code.invokespecial().setMethod(meth);
+
+ // invoke after
+ if (after != null) {
+ int ret = -1;
+ if (meth.getReturnType() != void.class) {
+ ret = code.getNextLocalsIndex();
+ code.xstore().setLocal(ret).setType(meth.getReturnType());
+ }
+ code.aload().setThis();
+ for (int i = 1; i < params.length; i++)
+ code.xload().setParam(i - 1).setType(params[i]);
+ if (ret != -1)
+ code.xload().setLocal(ret).setType(meth.getReturnType());
+ if (beforeRet != -1)
+ code.xload().setLocal(beforeRet).
+ setType(before.getReturnType());
+ code.invokestatic().setMethod(after);
+ }
+ code.xreturn().setType(meth.getReturnType());
+
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+ }
+
+ /**
+ * Return whether the given method is a setter.
+ */
+ protected boolean isSetter(Method meth) {
+ return startsWith(meth.getName(), "set")
+ || startsWith(meth.getName(), "add")
+ || startsWith(meth.getName(), "remove")
+ || startsWith(meth.getName(), "insert")
+ || startsWith(meth.getName(), "clear")
+ || startsWith(meth.getName(), "roll"); // used by Calendar
+ }
+
+ /**
+ * Return the getter corresponding to the given setter, or null.
+ */
+ protected Method findGetter(Class type, Method setter) {
+ String name = setter.getName().substring(3);
+ Class param = setter.getParameterTypes()[0];
+ Method getter;
+ try {
+ getter = type.getMethod("get" + name, (Class[]) null);
+ if (getter.getReturnType().isAssignableFrom(param)
+ || param.isAssignableFrom(getter.getReturnType()))
+ return getter;
+ } catch (NoSuchMethodException nsme) {
+ } catch (Exception e) {
+ throw new GeneralException(e);
+ }
+
+ if (param == boolean.class || param == Boolean.class) {
+ try {
+ getter = type.getMethod("is" + name, (Class[]) null);
+ if (getter.getReturnType().isAssignableFrom(param)
+ || param.isAssignableFrom(getter.getReturnType()))
+ return getter;
+ } catch (NoSuchMethodException nsme) {
+ } catch (Exception e) {
+ throw new GeneralException(e);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return whether the target string stars with the given token.
+ */
+ private static boolean startsWith(String str, String token) {
+ return str.startsWith(token)
+ && (str.length() == token.length()
+ || Character.isUpperCase(str.charAt(token.length())));
+ }
+
+ /**
+ * Proxy the given setter method to dirty the proxy owner.
+ */
+ private void proxySetter(BCClass bc, Method meth) {
+ BCMethod m = bc.declareMethod(meth.getName(), meth.getReturnType(),
+ meth.getParameterTypes());
+ m.makePublic();
+ Code code = m.getCode(true);
+ code.aload().setThis();
+ code.constant().setValue(true);
+ code.invokestatic().setMethod(Proxies.class, "dirty", void.class,
+ new Class[] { Proxy.class, boolean.class });
+ code.aload().setThis();
+ Class[] params = meth.getParameterTypes();
+ for (int i = 0; i < params.length; i++)
+ code.xload().setParam(i).setType(params[i]);
+ code.invokespecial().setMethod(meth);
+ code.xreturn().setType(meth.getReturnType());
+ code.calculateMaxStack();
+ code.calculateMaxLocals();
+ }
+
+ /**
+ * Add a writeReplace implementation that serializes to a non-proxy type
+ * unless detached.
+ */
+ private void addWriteReplaceMethod(BCClass bc) {
+ BCMethod m = bc.declareMethod("writeReplace", Object.class, null);
+ m.makeProtected();
+ m.getExceptions(true).addException(ObjectStreamException.class);
+ Code code = m.getCode(true);
+ code.aload().setThis();
+ code.invokestatic().setMethod(Proxies.class, "writeReplace",
+ Object.class, new Class[] { Proxy.class });
+ code.areturn();
+ code.calculateMaxLocals();
+ code.calculateMaxStack();
+ }
+
+ /**
+ * Create a unique id to avoid proxy class name conflicts.
+ */
+ private static synchronized long nextProxyId() {
+ return _proxyId++;
+ }
+
+ /**
+ * Find an appropriate copy constructor for the given type, or return null
+ * if none.
+ */
+ protected Constructor findCopyConstructor(Class cls) {
+ Constructor[] cons = cls.getConstructors();
+ Constructor match = null;
+ Class matchParam = null;
+ Class[] params;
+ for (int i = 0; i < cons.length; i++) {
+ params = cons[i].getParameterTypes();
+ if (params.length != 1)
+ continue;
+
+ // quit immediately on exact match
+ if (params[0] == cls)
+ return cons[i];
+
+ if (params[0].isAssignableFrom(cls) && (matchParam == null
+ || matchParam.isAssignableFrom(params[0]))) {
+ // track most derived collection constructor
+ match = cons[i];
+ matchParam = params[0];
+ }
+ }
+ return match;
+ }
+
+ /**
+ * Return the constructor that takes a comparator for the given type, or
+ * null if none.
+ */
+ private static Constructor findComparatorConstructor(Class cls) {
+ try {
+ return cls.getConstructor(new Class[] { Comparator.class });
+ } catch (NoSuchMethodException nsme) {
+ return null;
+ } catch (Exception e) {
+ throw new GeneralException(e);
+ }
+ }
+
+ /**
+ * Usage: java org.apache.openjpa.util.proxy.ProxyManagerImpl
+ * <class name>+
+ *
+ * The main method generates .class files for the proxies to the classes
+ * given on the command line. The .class files are placed in the same
+ * package as this class, or the current directory if the directory for this
+ * class cannot be accessed.
+ */
+ public static void main(String[] args)
+ throws ClassNotFoundException, IOException {
+ File dir = Files.getClassFile(ProxyManagerImpl.class);
+ dir = (dir == null) ? new File(System.getProperty("user.dir"))
+ : dir.getParentFile();
+
+ ProxyManagerImpl mgr = new ProxyManagerImpl();
+ Class cls;
+ BCClass bc;
+ for (int i = 0; i < args.length; i++) {
+ cls = Class.forName(args[i]);
+ if (Collection.class.isAssignableFrom(cls))
+ bc = mgr.generateProxyCollectionBytecode(cls);
+ else if (Map.class.isAssignableFrom(cls))
+ bc = mgr.generateProxyMapBytecode(cls);
+ else if (Date.class.isAssignableFrom(cls))
+ bc = mgr.generateProxyDateBytecode(cls);
+ else if (Calendar.class.isAssignableFrom(cls))
+ bc = mgr.generateProxyCalendarBytecode(cls);
+ else
+ bc = mgr.generateProxyBeanBytecode(cls);
+
+ System.out.println(bc.getName());
+ bc.write(new File(dir, bc.getClassName() + ".class"));
+ }
+ }
}
Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyMap.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyMap.java?view=diff&rev=484693&r1=484692&r2=484693
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyMap.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyMap.java Fri Dec 8 10:32:07 2006
@@ -18,23 +18,27 @@
import java.util.Comparator;
import java.util.Map;
-import org.apache.openjpa.conf.OpenJPAConfiguration;
-
/**
- * Interface implemented by all built-in proxies on {@link Map} types.
+ * Interface implemented by proxies on {@link Map} types.
*
* @author Abe White
*/
public interface ProxyMap
- extends Proxy {
+ extends Proxy, Map {
+
+ /**
+ * The map key type.
+ */
+ public Class getKeyType();
+
+ /**
+ * The map value type.
+ */
+ public Class getValueType();
/**
- * This method should return a new proxy of the same concrete type as the
- * implementing class. Used by the {@link ProxyManager} factories: one
- * template instance of each type is created for the purpose of producing
- * new instances via this method. Overcomes the performance
- * penalties of reflection.
+ * Create a new instance of this proxy type.
*/
public ProxyMap newInstance(Class keyType, Class valueType,
- Comparator compare, boolean trackChanges, OpenJPAConfiguration conf);
+ Comparator compare, boolean trackChanges);
}
Added: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyMaps.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyMaps.java?view=auto&rev=484693
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyMaps.java (added)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/ProxyMaps.java Fri Dec 8 10:32:07 2006
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openjpa.util;
+
+import java.io.InputStream;
+import java.io.ObjectStreamException;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Utility methods used by map proxies.
+ *
+ * @author Abe White
+ * @nojavadoc
+ */
+public class ProxyMaps
+ extends Proxies {
+
+ /**
+ * Call before invoking {@link Map#clear} on super.
+ */
+ public static void beforeClear(ProxyMap map) {
+ dirty(map, true);
+ Map.Entry entry;
+ for (Iterator itr = map.entrySet().iterator(); itr.hasNext();) {
+ entry = (Map.Entry) itr.next();
+ removed(map, entry.getKey(), true);
+ removed(map, entry.getValue(), false);
+ }
+ }
+
+ /**
+ * Override for {@link Map#keySet}.
+ */
+ public static Set keySet(ProxyMap map) {
+ ProxyEntrySet entries = (ProxyEntrySet) map.entrySet();
+ entries.setView(ProxyEntrySet.VIEW_KEYS);
+ return entries;
+ }
+
+ /**
+ * Override for {@link Map#values}.
+ */
+ public static Collection values(ProxyMap map) {
+ ProxyEntrySet entries = (ProxyEntrySet) map.entrySet();
+ entries.setView(ProxyEntrySet.VIEW_VALUES);
+ return entries;
+ }
+
+ /**
+ * Wrap the given entry set in a proxy.
+ */
+ public static Set afterEntrySet(ProxyMap map, Set entries) {
+ return new ProxyEntrySetImpl(map, entries);
+ }
+
+ /**
+ * Call before invoking {@link Map#put} on super.
+ */
+ public static boolean beforePut(ProxyMap map, Object key, Object value) {
+ assertAllowedType(key, map.getKeyType());
+ assertAllowedType(value, map.getValueType());
+ dirty(map, false);
+ return map.containsKey(key);
+ }
+
+ /**
+ * Call after invoking {@link Map#put} on super.
+ *
+ * @param ret the return value from the super's method
+ * @param before the return value from {@link #beforePut}
+ * @return the value to return from {@link Map#put}
+ */
+ public static Object afterPut(ProxyMap map, Object key, Object value,
+ Object ret, boolean before) {
+ if (before) {
+ if (map.getChangeTracker() != null)
+ ((MapChangeTracker) map.getChangeTracker()).changed(key, ret,
+ value);
+ removed(map, ret, false);
+ } else if (map.getChangeTracker() != null)
+ ((MapChangeTracker) map.getChangeTracker()).added(key, value);
+ return ret;
+ }
+
+ /**
+ * Call before invoking {@link Properties#setProperty} on super.
+ */
+ public static boolean beforeSetProperty(ProxyMap map, String key,
+ String value) {
+ return beforePut(map, key, value);
+ }
+
+ /**
+ * Call after invoking {@link Properties#setProperty} on super.
+ *
+ * @param ret the return value from the super's method
+ * @param before the return value from {@link #beforeSetProperty}
+ * @return the value to return from {@link Properties#setProperty}
+ */
+ public static Object afterSetProperty(ProxyMap map, String key,
+ String value, Object ret, boolean before) {
+ return afterPut(map, key, value, ret, before);
+ }
+
+ /**
+ * Call before invoking {@link Properties#load} on super.
+ */
+ public static void beforeLoad(ProxyMap map, InputStream in) {
+ dirty(map, true);
+ }
+
+ /**
+ * Call before invoking {@link Properties#loadXML} on super.
+ */
+ public static void beforeLoadFromXML(ProxyMap map, InputStream in) {
+ dirty(map, true);
+ }
+
+ /**
+ * Overload for {@link Map#putAll}.
+ */
+ public static void putAll(ProxyMap map, Map values) {
+ Map.Entry entry;
+ for (Iterator itr = values.entrySet().iterator(); itr.hasNext();) {
+ entry = (Map.Entry) itr.next();
+ map.put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ /**
+ * Call before invoking {@link Map#remove} on super.
+ */
+ public static boolean beforeRemove(ProxyMap map, Object key) {
+ dirty(map, false);
+ return map.containsKey(key);
+ }
+
+ /**
+ * Call after invoking {@link Map#remove} on super.
+ *
+ * @param ret the return value from the super's method
+ * @param before the return value from {@link #beforeRemove}
+ * @return the value to return from {@link Map#remove}
+ */
+ public static Object afterRemove(ProxyMap map, Object key, Object ret,
+ boolean before) {
+ if (before) {
+ if (map.getChangeTracker() != null)
+ ((MapChangeTracker) map.getChangeTracker()).removed(key, ret);
+ removed(map, key, true);
+ removed(map, ret, false);
+ }
+ return ret;
+ }
+
+ /**
+ * Marker interface for a proxy entry set.
+ */
+ public static interface ProxyEntrySet
+ extends Set {
+
+ public static final int VIEW_KEYS = 0;
+ public static final int VIEW_VALUES = 1;
+ public static final int VIEW_ENTRIES = 2;
+
+ /**
+ * Set what entry view this set exposes.
+ */
+ public void setView(int view);
+ }
+
+ /**
+ * Dirtying proxy for map entry set.
+ */
+ private static class ProxyEntrySetImpl
+ extends AbstractSet
+ implements ProxyEntrySet {
+
+ private final ProxyMap _map;
+ private final Set _entries;
+ private int _view = VIEW_ENTRIES;
+
+ /**
+ * Supply owning map and delegate entry set on construction.
+ */
+ public ProxyEntrySetImpl(ProxyMap map, Set entries) {
+ _map = map;
+ _entries = entries;
+ }
+
+ /**
+ * View mode.
+ */
+ public int getView() {
+ return _view;
+ }
+
+ /**
+ * View mode.
+ */
+ public void setView(int view) {
+ _view = view;
+ }
+
+ public int size() {
+ return _entries.size();
+ }
+
+ public boolean remove(Object o) {
+ if (_view != VIEW_KEYS)
+ throw new UnsupportedOperationException();
+
+ if (!_map.containsKey(o))
+ return false;
+ _map.remove(o);
+ return true;
+ }
+
+ public Iterator iterator() {
+ final Iterator itr = _entries.iterator();
+ return new Iterator() {
+ private Map.Entry _last = null;
+
+ public boolean hasNext() {
+ return itr.hasNext();
+ }
+
+ public Object next() {
+ _last = (Map.Entry) itr.next();
+ switch (_view) {
+ case VIEW_KEYS:
+ return _last.getKey();
+ case VIEW_VALUES:
+ return _last.getValue();
+ default:
+ return _last;
+ }
+ }
+
+ public void remove() {
+ dirty(_map, false);
+ itr.remove();
+ if (_map.getChangeTracker() != null)
+ ((MapChangeTracker) _map.getChangeTracker()).
+ removed(_last.getKey(), _last.getValue());
+ Proxies.removed(_map, _last.getKey(), true);
+ Proxies.removed(_map, _last.getValue(), false);
+ }
+ };
+ }
+
+ protected Object writeReplace()
+ throws ObjectStreamException {
+ switch (_view) {
+ case VIEW_KEYS:
+ return ((Map) _map).keySet();
+ case VIEW_VALUES:
+ return ((Map) _map).values();
+ default:
+ return ((Map) _map).entrySet();
+ }
+ }
+ }
+}
Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/util/localizer.properties
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/util/localizer.properties?view=diff&rev=484693&r1=484692&r2=484693
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/util/localizer.properties (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/resources/org/apache/openjpa/util/localizer.properties Fri Dec 8 10:32:07 2006
@@ -42,16 +42,20 @@
concurrently modified in another transaction. Each exception in \
the nested throwables array contains a failed object representing \
a concurrently modified object.
-bad-proxy: Unable to create second class object proxy for type: "{0}".
bad-array: Unable to copy array: {0}.
+no-proxy-intf: Unable to create a second class object proxy for interface \
+ "{0}". No corresponding concrete types are known.
+no-proxy-abstract: Unable to create a second class object proxy for abstract \
+ type "{0}". You must use a concrete type or a recognized interface.
+cant-classforname: Unable to instantiate proxy for type "{0}". Make sure the \
+ class has a default constructor.
+no-date-cons: Custom date type "{0}" needs a default constructor or a \
+ millisecond constructor.
bad-elem-type: The given element does not meet the requirements for this \
field. The container requires that all non-null objects are of the type \
declared in the XML metadata for this container. \
"{1}(loader={0}).isAssignableFrom ({3}(loader={2}))" failed.
string-id: Unable to create a valid id from string "{0}".
-no-proxy-cons: An exception was thrown while creating a new instance of \
- custom proxy collection type "{0}". This could mean that there is no \
- public no-args constructor for this type.
bad-single-id: Invalid single identity declaration for type "{0}". Only \
a single field can be declared for single field identity.
not-single: The given type "{0}" does not use single field identity.