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
+     * &lt;class name&gt;+
+     *
+     * 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.