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/07/29 01:48:18 UTC

svn commit: r426710 [5/6] - in /incubator/openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ openjpa-jdbc/sr...

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCDataGenerator.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCDataGenerator.java?rev=426710&r1=426709&r2=426710&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCDataGenerator.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCDataGenerator.java Fri Jul 28 16:48:13 2006
@@ -26,7 +26,6 @@
 import org.apache.openjpa.conf.OpenJPAConfiguration;
 import org.apache.openjpa.kernel.AbstractPCData;
 import org.apache.openjpa.kernel.FetchConfiguration;
-import org.apache.openjpa.kernel.FetchState;
 import org.apache.openjpa.kernel.OpenJPAStateManager;
 import org.apache.openjpa.kernel.PCData;
 import org.apache.openjpa.kernel.StoreContext;
@@ -523,18 +522,12 @@
     }
 
     private void addLoadMethod(BCClass bc, ClassMetaData meta) {
-        // public void load(OpenJPAStateManager sm, FetchState fetchState,
+        // public void load(OpenJPAStateManager sm, FetchConfiguration fetch,
         // 		Object context)
         Code code = addLoadMethod(bc, false);
         FieldMetaData[] fmds = meta.getFields();
         Collection jumps = new LinkedList();
         Collection jumps2;
-		// FetchConfiguration fetch = fetchState.getFetchConfiguration();
-		int fetch = code.getNextLocalsIndex();
-		code.aload().setParam(1);
-		code.invokeinterface().setMethod(FetchState.class, 
-			"getFetchConfiguration", FetchConfiguration.class, null);
-		code.astore().setLocal(fetch);
 		
         int local = code.getNextLocalsIndex();
         code.constant().setNull();
@@ -558,38 +551,22 @@
 
             if (intermediate)
                 addLoadIntermediate(code, i, objectCount, jumps2, inter);
-
             jumps2.add(code.go2());
 
-            // if in DFG, no if statement.
-            // else if (fetch.hasFetchGroup(sm.getMetaData().getField(i).
-			//     getFetchGroups()) || fetch.hasField(fmds[i].getFullName()))
-            if (!fmds[i].isInDefaultFetchGroup()) {
-                setTarget(code.aload().setLocal(fetch), jumps);
-                code.aload().setParam(0);
-                code.invokeinterface().setMethod(OpenJPAStateManager.class,
-                    "getMetaData", ClassMetaData.class, null);
-				code.constant().setValue(fmds[i].getIndex());
-                code.invokevirtual().setMethod(ClassMetaData.class,
-                    "getField", FieldMetaData.class, new Class[]{int.class});
-                code.invokevirtual().setMethod(FieldMetaData.class,
-                    "getFetchGroups", Set.class, null);
-                code.invokeinterface().setMethod
-                    (FetchConfiguration.class, "hasAnyFetchGroup",
-                        boolean.class, new Class[]{ Set.class });
-                JumpInstruction ifins = code.ifne();
-                code.aload().setLocal(fetch);
-                code.constant().setValue(fmds[i].getFullName());
-                code.invokeinterface().setMethod
-                    (FetchConfiguration.class, "hasField", boolean.class,
-                        new Class[]{ String.class });
-                jumps2.add(code.ifeq());
-                ifins.setTarget(addLoad(bc, code, fmds[i], objectCount,
-                    local, false));
-            } else {
-                setTarget(addLoad(bc, code, fmds[i], objectCount,
-                    local, false), jumps);
-            }
+            // if (fetch.requiresFetch(fmds[i]))
+            setTarget(code.aload().setParam(1), jumps);
+            code.aload().setParam(0);
+            code.invokeinterface().setMethod(OpenJPAStateManager.class,
+                "getMetaData", ClassMetaData.class, null);
+            code.constant().setValue(fmds[i].getIndex());
+            code.invokevirtual().setMethod(ClassMetaData.class,
+                "getField", FieldMetaData.class, new Class[]{int.class});
+            code.invokeinterface().setMethod (FetchConfiguration.class, 
+                "requiresFetch", boolean.class, 
+                new Class[]{FieldMetaData.class});
+            jumps2.add(code.ifeq());
+            addLoad(bc, code, fmds[i], objectCount, local, false);
+
             jumps = jumps2;
             if (replaceType(fmds[i]) >= JavaTypes.OBJECT)
                 objectCount++;
@@ -601,7 +578,7 @@
 
     private void addLoadWithFieldsMethod(BCClass bc, ClassMetaData meta) {
         Code code = addLoadMethod(bc, true);
-        // public void load(OpenJPAStateManager sm, FetchState fs,
+        // public void load(OpenJPAStateManager sm, FetchConfiguration fetch,
         // 		BitSet fields, Object conn)
         FieldMetaData[] fmds = meta.getFields();
         Collection jumps = new LinkedList();
@@ -667,10 +644,10 @@
         Class[] args = null;
         if (fields)
             args = new Class[]{ OpenJPAStateManager.class, BitSet.class,
-                FetchState.class, Object.class };
+                FetchConfiguration.class, Object.class };
         else
             args = new Class[]{ OpenJPAStateManager.class,
-                FetchState.class, Object.class };
+                FetchConfiguration.class, Object.class };
         BCMethod load = bc.declareMethod("load", void.class, args);
         Code code = load.getCode(true);
 
@@ -730,9 +707,9 @@
             code.aload().setParam(1 + offset);
             code.aload().setParam(2 + offset);
             code.invokevirtual().setMethod(bc.getName(), "toField",
-                Object.class.getName(), toStrings
-                (new Class[]{ OpenJPAStateManager.class, FieldMetaData.class,
-                    Object.class, FetchState.class, Object.class }));
+                Object.class.getName(), toStrings(new Class[]{ 
+                OpenJPAStateManager.class, FieldMetaData.class,
+                Object.class, FetchConfiguration.class, Object.class }));
             code.invokeinterface().setMethod(OpenJPAStateManager.class,
                 "storeField", void.class,
                 new Class[]{ int.class, Object.class });

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractPCData.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractPCData.java?rev=426710&r1=426709&r2=426710&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractPCData.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractPCData.java Fri Jul 28 16:48:13 2006
@@ -64,7 +64,7 @@
      * Transform the given data value into its field value.
      */
     protected Object toField(OpenJPAStateManager sm, FieldMetaData fmd,
-        Object data, FetchState fetchState, Object context) {
+        Object data, FetchConfiguration fetch, Object context) {
         if (data == null)
             return null;
 
@@ -74,7 +74,7 @@
                 Collection c2 = (Collection) sm.newFieldProxy(fmd.getIndex());
                 for (int i = 0; i < c.size(); i++)
                     c2.add(toNestedField(sm, fmd.getElement(), c.get(i),
-                        fetchState, context));
+                        fetch, context));
                 if (c2 instanceof Proxy) {
                     ChangeTracker ct = ((Proxy) c2).getChangeTracker();
                     if (ct != null)
@@ -90,9 +90,9 @@
                 for (Iterator mi = m.entrySet().iterator(); mi.hasNext();) {
                     e = (Map.Entry) mi.next();
                     key = toNestedField(sm, fmd.getKey(), e.getKey(),
-                        fetchState, context);
+                        fetch, context);
                     value = toNestedField(sm, fmd.getElement(), e.getValue(),
-                        fetchState, context);
+                        fetch, context);
                     m2.put(key, value);
                 }
                 return m2;
@@ -102,11 +102,11 @@
                     l.size());
                 for (int i = 0; i < l.size(); i++) {
                     Array.set(a, i, toNestedField(sm, fmd.getElement(),
-                        l.get(i), fetchState, context));
+                        l.get(i), fetch, context));
                 }
                 return a;
             default:
-                return toNestedField(sm, fmd, data, fetchState, context);
+                return toNestedField(sm, fmd, data, fetch, context);
         }
     }
 
@@ -115,7 +115,7 @@
      * may be a key, value, or element of a map or collection.
      */
     protected Object toNestedField(OpenJPAStateManager sm, ValueMetaData vmd,
-        Object data, FetchState fetchState, Object context) {
+        Object data, FetchConfiguration fetch, Object context) {
         if (data == null)
             return null;
 
@@ -126,11 +126,11 @@
                 return (Locale) data;
             case JavaTypes.PC:
                 if (vmd.isEmbedded())
-                    return toEmbeddedField(sm, vmd, data, fetchState, context);
+                    return toEmbeddedField(sm, vmd, data, fetch, context);
                 // no break
             case JavaTypes.PC_UNTYPED:
                 Object ret =
-                    toRelationField(sm, vmd, data, fetchState, context);
+                    toRelationField(sm, vmd, data, fetch, context);
                 if (ret != null)
                     return ret;
                 OrphanedKeyAction action = sm.getContext().getConfiguration().
@@ -146,8 +146,8 @@
      * implementation assumes the data is an oid.
      */
     protected Object toRelationField(OpenJPAStateManager sm, ValueMetaData vmd,
-        Object data, FetchState fetchState, Object context) {
-        return sm.getContext().find(data, fetchState, null, null, 0);
+        Object data, FetchConfiguration fetch, Object context) {
+        return sm.getContext().find(data, fetch, null, null, 0);
     }
 
     /**
@@ -155,12 +155,12 @@
      * implementation assumes the data is an {@link AbstractPCData}.
      */
     protected Object toEmbeddedField(OpenJPAStateManager sm, ValueMetaData vmd,
-        Object data, FetchState fetchState, Object context) {
+        Object data, FetchConfiguration fetch, Object context) {
         AbstractPCData pcdata = (AbstractPCData) data;
         OpenJPAStateManager embedded = sm.getContext().embed(null,
             pcdata.getId(), sm, vmd);
         pcdata.load(embedded, (BitSet) pcdata.getLoaded().clone(),
-            fetchState, context);
+            fetch, context);
         return embedded.getManagedInstance();
     }
 

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractPCResultObjectProvider.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractPCResultObjectProvider.java?rev=426710&r1=426709&r2=426710&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractPCResultObjectProvider.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractPCResultObjectProvider.java Fri Jul 28 16:48:13 2006
@@ -53,10 +53,10 @@
     }
 
     public void initialize(OpenJPAStateManager sm, PCState state,
-        FetchState fetchState)
+        FetchConfiguration fetch)
         throws Exception {
         sm.initialize(getPCType(), state);
-        load(sm, fetchState);
+        load(sm, fetch);
     }
 
     public Object getResultObject()
@@ -93,7 +93,8 @@
      * manager. Remember to call {@link OpenJPAStateManager#setVersion} to set
      * the optimistic versioning information, if it has any.
      */
-    protected abstract void load(OpenJPAStateManager sm, FetchState fetch)
+    protected abstract void load(OpenJPAStateManager sm, 
+        FetchConfiguration fetch)
         throws Exception;
 
     /**

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java?rev=426710&r1=426709&r2=426710&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java Fri Jul 28 16:48:13 2006
@@ -694,20 +694,18 @@
         int flags = OID_COPY | OID_ALLOW_NEW | OID_NODELETED;
         if (!validate)
             flags |= OID_NOVALIDATE;
-        return find(oid, _fc.newFetchState (), null, null, flags, call);
+        return find(oid, _fc, null, null, flags, call);
     }
 
-    public Object find(Object oid, FetchState fetchState, BitSet exclude,
+    public Object find(Object oid, FetchConfiguration fetch, BitSet exclude,
         Object edata, int flags) {
-    	if (fetchState == null)
-    		fetchState = _fc.newFetchState ();
-        return find(oid, fetchState, exclude, edata, flags, null);
+        return find(oid, fetch, exclude, edata, flags, null);
     }
 
     /**
      * Internal finder.
      */
-    protected Object find(Object oid, FetchState fetchState, BitSet exclude,
+    protected Object find(Object oid, FetchConfiguration fetch, BitSet exclude,
         Object edata, int flags, FindCallbacks call) {
         if (call == null)
             call = this;
@@ -717,6 +715,8 @@
                 throw new ObjectNotFoundException(_loc.get("null-oid"));
             return call.processReturn(oid, null);
         }
+        if (fetch == null)
+            fetch = _fc;
 
         beginOperation(true);
         try {
@@ -732,16 +732,13 @@
                 if (!sm.isLoading()) {
                     // make sure all the configured fields are loaded; do this
                     // after making instance transactional for locking
-                    if (!sm.isTransactional()
-                        && useTransactionalState(fetchState.
-                        getFetchConfiguration()))
+                    if (!sm.isTransactional() && useTransactionalState(fetch))
                         sm.transactional();
                     boolean loaded = sm.isLoading();
                     if (!loaded) {
                         try {
-                            loaded = sm.load(fetchState,
-                                StateManagerImpl.LOAD_FGS, exclude, edata,
-                                false);
+                            loaded = sm.load(fetch, StateManagerImpl.LOAD_FGS, 
+                                exclude, edata, false);
                         } catch (ObjectNotFoundException onfe) {
                             if ((flags & OID_NODELETED) != 0
                                 || (flags & OID_NOVALIDATE) != 0)
@@ -767,8 +764,6 @@
                 // since the object was cached, we may need to upgrade lock
                 // if current level is higher than level of initial load
                 if ((_flags & FLAG_ACTIVE) != 0) {
-                    FetchConfiguration fetch =
-                        fetchState.getFetchConfiguration();
                     int level = fetch.getReadLockLevel();
                     _lm.lock(sm, level, fetch.getLockTimeout(), edata);
                     sm.readLocked(level, fetch.getWriteLockLevel());
@@ -784,7 +779,7 @@
             // initialize a new state manager for the datastore instance
             sm = newStateManagerImpl(oid, (flags & OID_COPY) != 0);
             boolean load = requiresLoad(sm, false, edata, flags);
-            sm = initialize(sm, load, fetchState, edata);
+            sm = initialize(sm, load, fetch, edata);
             if (sm == null) {
                 if ((flags & OID_NOVALIDATE) != 0)
                     throw new ObjectNotFoundException(oid);
@@ -794,7 +789,7 @@
             // make sure all configured fields were loaded
             if (load) {
                 try {
-                    sm.load(fetchState, StateManagerImpl.LOAD_FGS, exclude,
+                    sm.load(fetch, StateManagerImpl.LOAD_FGS, exclude,
                         edata, false);
                 } catch (ObjectNotFoundException onfe) {
                     if ((flags & OID_NODELETED) != 0
@@ -817,18 +812,16 @@
      * Initialize a newly-constructed state manager.
      */
     protected StateManagerImpl initialize(StateManagerImpl sm, boolean load,
-        FetchState fetchState, Object edata) {
+        FetchConfiguration fetch, Object edata) {
         if (!load) {
             sm.initialize(sm.getMetaData().getDescribedType(),
                 PCState.HOLLOW);
         } else {
-            FetchConfiguration fetch = (fetchState == null)
-                ? _fc : fetchState.getFetchConfiguration();
             PCState state = (useTransactionalState(fetch))
                 ? PCState.PCLEAN : PCState.PNONTRANS;
             sm.setLoading(true);
             try {
-                if (!_store.initialize(sm, state, fetchState, edata))
+                if (!_store.initialize(sm, state, fetch, edata))
                     return null;
             } finally {
                 sm.setLoading(false);
@@ -867,6 +860,9 @@
         _loading = new HashMap((int) (oids.size() * 1.33 + 1));
         if (call == null)
             call = this;
+        if (fetch == null)
+            fetch = _fc;
+
         beginOperation(true);
         try {
             assertNontransactionalRead();
@@ -912,7 +908,7 @@
                 PCState state = (transState) ? PCState.PCLEAN
                     : PCState.PNONTRANS;
                 Collection failed = _store.loadAll(load, state,
-                    StoreManager.FORCE_LOAD_NONE, _fc, edata);
+                    StoreManager.FORCE_LOAD_NONE, fetch, edata);
 
                 // set failed instances to null
                 if (failed != null && !failed.isEmpty()) {
@@ -934,7 +930,7 @@
                 sm = (StateManagerImpl) _loading.get(oid);
                 if (sm != null && requiresLoad(sm, true, edata, flags)) {
                     try {
-                        sm.load(fetch.newFetchState(), StateManagerImpl.LOAD_FGS, 
+                        sm.load(fetch, StateManagerImpl.LOAD_FGS,
                         	exclude, edata, false);
                         if (active) {
                             _lm.lock(sm, level, fetch.getLockTimeout(), edata);
@@ -2482,8 +2478,7 @@
 
                 // otherwise make sure pc is fully loaded for when we copy its
                 // data below
-                orig.load(_fc.newFetchState(), StateManagerImpl.LOAD_ALL,
-                    null, null, false);
+                orig.load(_fc, StateManagerImpl.LOAD_ALL, null, null, false);
             }
 
             // create new state manager with embedded metadata
@@ -2698,8 +2693,8 @@
 
                     try {
                         sm.afterRefresh();
-                        sm.load(_fc.newFetchState(),
-                            StateManagerImpl.LOAD_FGS, null, null, false);
+                        sm.load(_fc, StateManagerImpl.LOAD_FGS, null, null, 
+                            false);
                     } catch (OpenJPAException ke) {
                         exceps = add(exceps, ke);
                     }
@@ -2739,8 +2734,7 @@
                 if (sm.isDetached())
                     throw newDetachedException(obj, "refresh");
                 else if (sm.beforeRefresh(false)) {
-                    sm.load(_fc.newFetchState(), StateManagerImpl.LOAD_FGS,
-                        null, null, false);
+                    sm.load(_fc, StateManagerImpl.LOAD_FGS, null, null, false);
                     sm.afterRefresh();
                 }
                 fireLifecycleEvent(sm.getManagedInstance(), null,
@@ -2825,7 +2819,7 @@
                     : StateManagerImpl.LOAD_ALL;
                 try {
                     sm.beforeRead(-1);
-                    sm.load(_fc.newFetchState(), mode, null, null, false);
+                    sm.load(_fc, mode, null, null, false);
                 } catch (OpenJPAException ke) {
                     exceps = add(exceps, ke);
                 }
@@ -2861,7 +2855,7 @@
                     int mode = (dfgOnly) ? StateManagerImpl.LOAD_FGS
                         : StateManagerImpl.LOAD_ALL;
                     sm.beforeRead(-1);
-                    sm.load(_fc.newFetchState(), mode, null, null, false);
+                    sm.load(_fc, mode, null, null, false);
                 }
             } else if (assertPersistenceCapable(obj).pcIsDetached()
                 == Boolean.TRUE)
@@ -3227,8 +3221,7 @@
             if (sm != null && sm.isPersistent()) {
                 assertActiveTransaction();
                 sm.transactional();
-                sm.load(_fc.newFetchState(), StateManagerImpl.LOAD_FGS, null,
-                    null, false);
+                sm.load(_fc, StateManagerImpl.LOAD_FGS, null, null, false);
                 sm.setCheckVersion(true);
                 if (updateVersion)
                     sm.setUpdateVersion(true);
@@ -3266,8 +3259,7 @@
 
             try {
                 sm.transactional();
-                sm.load(_fc.newFetchState(), StateManagerImpl.LOAD_FGS, null,
-                    null, false);
+                sm.load(_fc, StateManagerImpl.LOAD_FGS, null, null, false);
             } catch (OpenJPAException ke) {
                 exceps = add(exceps, ke);
             }

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java?rev=426710&r1=426709&r2=426710&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java Fri Jul 28 16:48:13 2006
@@ -189,10 +189,10 @@
         }
     }
 
-    public Object find(Object oid, FetchState fetchState, BitSet exclude,
+    public Object find(Object oid, FetchConfiguration fetch, BitSet exclude,
         Object edata, int flags) {
         try {
-            return _broker.find(oid, fetchState, exclude, edata, flags);
+            return _broker.find(oid, fetch, exclude, edata, flags);
         } catch (RuntimeException re) {
             throw translate(re);
         }

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingFetchConfiguration.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingFetchConfiguration.java?rev=426710&r1=426709&r2=426710&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingFetchConfiguration.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingFetchConfiguration.java Fri Jul 28 16:48:13 2006
@@ -20,6 +20,7 @@
 
 import org.apache.openjpa.lib.rop.ResultList;
 import org.apache.openjpa.lib.rop.ResultObjectProvider;
+import org.apache.openjpa.meta.FieldMetaData;
 import org.apache.openjpa.util.RuntimeExceptionTranslator;
 
 ///////////////////////////////////////////////////////////////
@@ -229,14 +230,6 @@
         }
     }
 
-    public boolean hasAnyFetchGroup(Set groups) {
-        try {
-            return _fetch.hasAnyFetchGroup(groups);
-        } catch (RuntimeException re) {
-            throw translate(re);
-        }
-    }
-
     public FetchConfiguration addFetchGroup(String group) {
         try {
             _fetch.addFetchGroup(group);
@@ -411,14 +404,6 @@
         }
     }
 
-    public FetchState newFetchState() {
-        try {
-            return _fetch.newFetchState();
-        } catch (RuntimeException re) {
-            throw translate(re);
-        }
-    }
-
     public void copy(FetchConfiguration fetch) {
         try {
             _fetch.copy(fetch);
@@ -450,6 +435,22 @@
             throw translate(re);
 		}
 	}
+
+    public boolean requiresFetch(FieldMetaData fmd) {
+        try {
+            return _fetch.requiresFetch(fmd);
+        } catch (RuntimeException re) {
+            throw translate(re);
+        }
+    }
+
+    public FetchConfiguration traverse(FieldMetaData fmd) {
+        try {
+            return _fetch.traverse(fmd);
+        } catch (RuntimeException re) {
+            throw translate(re);
+        } 
+    }
 
     public void lock() {
         try {

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingStoreManager.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingStoreManager.java?rev=426710&r1=426709&r2=426710&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingStoreManager.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingStoreManager.java Fri Jul 28 16:48:13 2006
@@ -104,13 +104,13 @@
     }
 
     public boolean initialize(OpenJPAStateManager sm, PCState state,
-        FetchState fetchState, Object context) {
-        return _store.initialize(sm, state, fetchState, context);
+        FetchConfiguration fetch, Object context) {
+        return _store.initialize(sm, state, fetch, context);
     }
 
     public boolean load(OpenJPAStateManager sm, BitSet fields,
-        FetchState fetchState, int lockLevel, Object context) {
-        return _store.load(sm, fields, fetchState, lockLevel, context);
+        FetchConfiguration fetch, int lockLevel, Object context) {
+        return _store.load(sm, fields, fetch, lockLevel, context);
     }
 
     public Collection loadAll(Collection sms, PCState state, int load,

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachManager.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachManager.java?rev=426710&r1=426709&r2=426710&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachManager.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachManager.java Fri Jul 28 16:48:13 2006
@@ -148,9 +148,7 @@
             exclude = StoreContext.EXCLUDE_ALL;
         else if (detachMode == DETACH_ALL)
             loadMode = StateManagerImpl.LOAD_ALL;
-        FetchState fetchState = broker.getFetchConfiguration().
-            newFetchState();
-        sm.load(fetchState, loadMode, exclude, null, false);
+        sm.load(broker.getFetchConfiguration(), loadMode, exclude, null, false);
 
         // create bitset of fields to detach; if mode is all we can use
         // currently loaded bitset clone, since we know all fields are loaded
@@ -221,12 +219,9 @@
         StateManagerImpl sm, BitSet idxs) {
         FetchConfiguration fetch = broker.getFetchConfiguration();
         FieldMetaData[] fmds = sm.getMetaData().getFields();
-        for (int i = 0; i < fmds.length; i++) {
-            if (fmds[i].isPrimaryKey() || fmds[i].isInDefaultFetchGroup()
-                || fetch.hasAnyFetchGroup(fmds[i].getFetchGroups())
-                || fetch.hasField(fmds[i].getFullName()))
+        for (int i = 0; i < fmds.length; i++)
+            if (fmds[i].isPrimaryKey() || fetch.requiresFetch(fmds[i]))
                 idxs.set(i);
-        }
     }
 
     /**

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateManager.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateManager.java?rev=426710&r1=426709&r2=426710&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateManager.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedStateManager.java Fri Jul 28 16:48:13 2006
@@ -137,9 +137,7 @@
                 }
             }
             FetchConfiguration fc = broker.getFetchConfiguration();
-            FetchState fetchState = fc.newFetchState();
-            sm.loadFields(load, fetchState, fc.getWriteLockLevel(), null, true);
-                
+            sm.loadFields(load, fc, fc.getWriteLockLevel(), null, true);
         }
         sm.setVersion(_version);
 
@@ -646,15 +644,15 @@
         return ret;
     }
 
-    ///////////////////////////////////
+    //////////////////////////////////////
     // OpenJPAStateManager implementation
-    ///////////////////////////////////
+    //////////////////////////////////////
 
     public void initialize(Class forType, PCState state) {
         throw new UnsupportedOperationException();
     }
 
-    public void load(FetchState fetchState) {
+    public void load(FetchConfiguration fetch) {
         throw new UnsupportedOperationException();
     }
 
@@ -702,7 +700,7 @@
         throw new UnsupportedOperationException();
     }
 
-    public BitSet getUnloaded(FetchState fetchState) {
+    public BitSet getUnloaded(FetchConfiguration fetch) {
         throw new UnsupportedOperationException();
     }
 

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedValueStateManager.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedValueStateManager.java?rev=426710&r1=426709&r2=426710&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedValueStateManager.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DetachedValueStateManager.java Fri Jul 28 16:48:13 2006
@@ -65,7 +65,7 @@
         throw new UnsupportedOperationException();
     }
 
-    public void load(FetchState fetchState) {
+    public void load(FetchConfiguration fetch) {
         throw new UnsupportedOperationException();
     }
 
@@ -113,7 +113,7 @@
         throw new UnsupportedOperationException();
     }
 
-    public BitSet getUnloaded(FetchState fetchState) {
+    public BitSet getUnloaded(FetchConfiguration fetch) {
         throw new UnsupportedOperationException();
     }
 

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfiguration.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfiguration.java?rev=426710&r1=426709&r2=426710&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfiguration.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfiguration.java Fri Jul 28 16:48:13 2006
@@ -21,6 +21,7 @@
 
 import org.apache.openjpa.lib.rop.ResultList;
 import org.apache.openjpa.lib.rop.ResultObjectProvider;
+import org.apache.openjpa.meta.FieldMetaData;
 
 /**
  * Allows configuration and optimization of how objects are loaded from
@@ -28,7 +29,7 @@
  *
  * @since 3.0
  * @author Abe White
- * @author Patrick Linskey
+ * @author Pinaki Poddar
  */
 public interface FetchConfiguration
     extends Serializable, Cloneable, LockLevels, QueryFlushModes {
@@ -39,18 +40,6 @@
     public static final int DEFAULT = -99;
 
     /**
-     * Special fetch group name that is used by OpenJPA to indicate that all
-     * fetch groups should be loaded by this configuration.
-     */
-    public static final String FETCH_GROUP_ALL = "openjpa.FetchGroupAll";
-
-    /**
-     * Special fetch group name that is used by OpenJPA to denote the default
-     * fetch group.
-     */
-    public static final String FETCH_GROUP_DEFAULT = "default";
-
-    /**
      * Return the context assiciated with this configuration;
      * may be null if it has not been set or this object has been serialized.
      */
@@ -143,13 +132,6 @@
     public boolean hasFetchGroup(String group);
 
     /**
-     * Return true if any of the given fetch groups has been added.
-     *
-     * @since 4.1
-     */
-    public boolean hasAnyFetchGroup(Set groups);
-
-    /**
      * Adds <code>group</code> to the set of fetch group names to
      * use when loading objects.
      */
@@ -228,26 +210,6 @@
     public FetchConfiguration clearFields();
 
     /**
-     * Root classes for recursive operations. This set is not thread safe.
-     */
-    public Set getRootClasses();
-
-    /**
-     * Root classes for recursive operations.
-     */
-    public FetchConfiguration setRootClasses(Collection classes);
-
-    /**
-     * Root instances for recursive operations. This set is not thread safe.
-     */
-    public Set getRootInstances();
-
-    /**
-     * Root instances for recursive operations.
-     */
-    public FetchConfiguration setRootInstances(Collection roots);
-
-    /**
      * The number of milliseconds to wait for an object lock, or -1 for no
      * limit.
      *
@@ -297,13 +259,6 @@
     public ResultList newResultList(ResultObjectProvider rop);
 
     /**
-     * Create a new fecth state for the current fetch configuration.
-     *
-     * @since 4.1
-     */
-    public FetchState newFetchState();
-
-    /**
      * Sets an arbitrary query hint that may be utilized during
      * execution. The hint may be datastore-specific.
      *
@@ -318,12 +273,31 @@
      * is not specified.
      *
 	 * @param name the hint name
-	 *
-	 * @since	4.0
+	 * @since 4.0
 	 */
 	public Object getHint (String name);
 
     /**
+     * Root classes for recursive operations. This set is not thread safe.
+     */
+    public Set getRootClasses();
+
+    /**
+     * Root classes for recursive operations.
+     */
+    public FetchConfiguration setRootClasses(Collection classes);
+
+    /**
+     * Root instances for recursive operations. This set is not thread safe.
+     */
+    public Set getRootInstances();
+
+    /**
+     * Root instances for recursive operations.
+     */
+    public FetchConfiguration setRootInstances(Collection roots);
+
+    /**
      * Synchronize on internal lock if multithreaded is true.
      */
     public void lock();
@@ -332,4 +306,21 @@
      * Release internal lock if multithreaded is true.
      */
     public void unlock();
+
+    /**
+     * Affirms if the given field requires to be fetched in the context
+     * of current fetch operation.
+     *
+     * @since 4.1
+     */
+    public boolean requiresFetch(FieldMetaData fm);
+    
+    /**
+     * Traverse the given field to generate (possibly) a new configuration 
+     * state.
+     * 
+     * @return a new configuration state resulting out of traversal
+     * @since 4.1
+     */
+    public FetchConfiguration traverse(FieldMetaData fm);
 }

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java?rev=426710&r1=426709&r2=426710&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/FetchConfigurationImpl.java Fri Jul 28 16:48:13 2006
@@ -15,12 +15,15 @@
  */
 package org.apache.openjpa.kernel;
 
+import java.io.Serializable;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -33,6 +36,9 @@
 import org.apache.openjpa.lib.rop.SimpleResultList;
 import org.apache.openjpa.lib.rop.WindowResultList;
 import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.FetchGroup;
+import org.apache.openjpa.meta.FieldMetaData;
 import org.apache.openjpa.util.InternalException;
 import org.apache.openjpa.util.NoTransactionException;
 import org.apache.openjpa.util.UserException;
@@ -43,6 +49,7 @@
  *
  * @since 3.0
  * @author Abe White
+ * @author Pinaki Poddar
  * @nojavadoc
  */
 public class FetchConfigurationImpl
@@ -51,31 +58,52 @@
     private static final Localizer _loc = Localizer.forPackage
         (FetchConfigurationImpl.class);
 
-    // transient state
-    private transient StoreContext _ctx = null;
-
-    private int _fetchBatchSize = 0;
-    private int _maxFetchDepth = 1;
-    private boolean _queryCache = true;
-    private int _flushQuery = 0;
-    private int _lockTimeout = -1;
-    private int _readLockLevel = LOCK_NONE;
-    private int _writeLockLevel = LOCK_NONE;
-    private Set _fetchGroups = null;
-    private Set _fields = null;
-    private Set _rootClasses;
-    private Set _rootInstances;
-    private Map _hints = null;
+    /**
+     * Configurable state shared throughout a traversal chain.
+     */
+    protected static class ConfigurationState
+        implements Serializable
+    {
+        public transient StoreContext ctx = null;
+        public int fetchBatchSize = 0;
+        public int maxFetchDepth = 1;
+        public boolean queryCache = true;
+        public int flushQuery = 0;
+        public int lockTimeout = -1;
+        public int readLockLevel = LOCK_NONE;
+        public int writeLockLevel = LOCK_NONE;
+        public Set fetchGroups = null;
+        public Set fields = null;
+        public Set rootClasses;
+        public Set rootInstances;
+        public Map hints = null;
+    }
+
+    private final ConfigurationState _state;
+    private FetchConfigurationImpl _parent;
+    private String _fromField;
+    private Class _fromType;
+    private int _availableRecursion;
+    private int _availableDepth;
+
+    public FetchConfigurationImpl() {
+        this(null);
+    }
+
+    protected FetchConfigurationImpl(ConfigurationState state) {
+        _state = (state == null) ? new ConfigurationState() : state;
+        _availableDepth = _state.maxFetchDepth;
+    } 
 
     public StoreContext getContext() {
-        return _ctx;
+        return _state.ctx;
     }
 
     public void setContext(StoreContext ctx) {
         // can't reset non-null context to another context
-        if (ctx != null && _ctx != null && ctx != _ctx)
+        if (ctx != null && _state.ctx != null && ctx != _state.ctx)
             throw new InternalException();
-        _ctx = ctx;
+        _state.ctx = ctx;
         if (ctx == null)
             return;
 
@@ -91,18 +119,22 @@
      * Clone this instance.
      */
     public Object clone() {
-        FetchConfigurationImpl clone = newInstance();
-        clone._ctx = _ctx;
+        FetchConfigurationImpl clone = newInstance(null);
+        clone._state.ctx = _state.ctx;
+        clone._parent = _parent;
+        clone._fromField = _fromField;
+        clone._fromType = _fromType;
+        clone._availableRecursion = _availableRecursion;
+        clone._availableDepth = _availableDepth;
         clone.copy(this);
         return clone;
     }
 
     /**
-     * Return a new hollow instance. Subclasses should override to return
-     * a new instance of their type, with cached permissions set appropriately.
+     * Return a new hollow instance.
      */
-    protected FetchConfigurationImpl newInstance() {
-        return new FetchConfigurationImpl();
+    protected FetchConfigurationImpl newInstance(ConfigurationState state) {
+        return new FetchConfigurationImpl(state);
     }
 
     public void copy(FetchConfiguration fetch) {
@@ -117,86 +149,75 @@
         addFields(fetch.getFields());
 
         // don't use setters because require active transaction
-        _readLockLevel = fetch.getReadLockLevel();
-        _writeLockLevel = fetch.getWriteLockLevel();
+        _state.readLockLevel = fetch.getReadLockLevel();
+        _state.writeLockLevel = fetch.getWriteLockLevel();
     }
 
     public int getFetchBatchSize() {
-        return _fetchBatchSize;
+        return _state.fetchBatchSize;
     }
 
     public FetchConfiguration setFetchBatchSize(int fetchBatchSize) {
-        if (fetchBatchSize == DEFAULT && _ctx != null)
-            fetchBatchSize = _ctx.getConfiguration().getFetchBatchSize();
+        if (fetchBatchSize == DEFAULT && _state.ctx != null)
+            fetchBatchSize = _state.ctx.getConfiguration().getFetchBatchSize();
         if (fetchBatchSize != DEFAULT)
-            _fetchBatchSize = fetchBatchSize;
+            _state.fetchBatchSize = fetchBatchSize;
         return this;
     }
 
     public int getMaxFetchDepth() {
-        return _maxFetchDepth;
+        return _state.maxFetchDepth;
     }
 
     public FetchConfiguration setMaxFetchDepth(int depth) {
-        _maxFetchDepth = depth;
+        _state.maxFetchDepth = depth;
+        if (_parent == null)
+            _availableDepth = depth;
         return this;
     }
 
     public boolean getQueryCache() {
-        return _queryCache;
+        return _state.queryCache;
     }
 
     public FetchConfiguration setQueryCache(boolean cache) {
-        _queryCache = cache;
+        _state.queryCache = cache;
         return this;
     }
 
     public int getFlushBeforeQueries() {
-        return _flushQuery;
+        return _state.flushQuery;
     }
 
     public FetchConfiguration setFlushBeforeQueries(int flush) {
-        if (flush == DEFAULT && _ctx != null)
-            _flushQuery = _ctx.getConfiguration().
+        if (flush == DEFAULT && _state.ctx != null)
+            _state.flushQuery = _state.ctx.getConfiguration().
                 getFlushBeforeQueriesConstant();
         else if (flush != DEFAULT)
-            _flushQuery = flush;
+            _state.flushQuery = flush;
         return this;
     }
 
     public Set getFetchGroups() {
-        return (_fetchGroups == null) ? Collections.EMPTY_SET : _fetchGroups;
+        return (_state.fetchGroups == null) ? Collections.EMPTY_SET 
+            : _state.fetchGroups;
     }
 
     public boolean hasFetchGroup(String group) {
-        return _fetchGroups != null
-            && (_fetchGroups.contains(group)
-            || _fetchGroups.contains(FETCH_GROUP_ALL));
-    }
-
-    public boolean hasAnyFetchGroup(Set groups) {
-        if (groups == null || groups.isEmpty())
-            return false;
-        for (Iterator itr = groups.iterator(); itr.hasNext();)
-            if (hasFetchGroup((String) itr.next()))
-                return true;
-        return false;
+        return _state.fetchGroups != null
+            && (_state.fetchGroups.contains(group)
+            || _state.fetchGroups.contains(FetchGroup.NAME_ALL));
     }
 
-    /**
-     * Adds a fetch group of the given name to this receiver.
-     *
-     * @param name must not be null or empty.
-     */
     public FetchConfiguration addFetchGroup(String name) {
         if (StringUtils.isEmpty(name))
             throw new UserException(_loc.get("null-fg"));
 
         lock();
         try {
-            if (_fetchGroups == null)
-                _fetchGroups = new HashSet();
-            _fetchGroups.add(name);
+            if (_state.fetchGroups == null)
+                _state.fetchGroups = new HashSet();
+            _state.fetchGroups.add(name);
         } finally {
             unlock();
         }
@@ -214,8 +235,8 @@
     public FetchConfiguration removeFetchGroup(String group) {
         lock();
         try {
-            if (_fetchGroups != null)
-                _fetchGroups.remove(group);
+            if (_state.fetchGroups != null)
+                _state.fetchGroups.remove(group);
         } finally {
             unlock();
         }
@@ -225,8 +246,8 @@
     public FetchConfiguration removeFetchGroups(Collection groups) {
         lock();
         try {
-            if (_fetchGroups != null)
-                _fetchGroups.removeAll(groups);
+            if (_state.fetchGroups != null)
+                _state.fetchGroups.removeAll(groups);
         } finally {
             unlock();
         }
@@ -236,8 +257,8 @@
     public FetchConfiguration clearFetchGroups() {
         lock();
         try {
-            if (_fetchGroups != null)
-                _fetchGroups.clear();
+            if (_state.fetchGroups != null)
+                _state.fetchGroups.clear();
         } finally {
             unlock();
         }
@@ -246,18 +267,18 @@
 
     public FetchConfiguration resetFetchGroups() {
         clearFetchGroups();
-        if (_ctx != null)
-            addFetchGroups(Arrays.asList(_ctx.getConfiguration().
+        if (_state.ctx != null)
+            addFetchGroups(Arrays.asList(_state.ctx.getConfiguration().
                 getFetchGroupsList()));
         return this;
     }
 
     public Set getFields() {
-        return (_fields == null) ? Collections.EMPTY_SET : _fields;
+        return (_state.fields == null) ? Collections.EMPTY_SET : _state.fields;
     }
 
     public boolean hasField(String field) {
-        return _fields != null && _fields.contains(field);
+        return _state.fields != null && _state.fields.contains(field);
     }
 
     public FetchConfiguration addField(String field) {
@@ -266,9 +287,9 @@
 
         lock();
         try {
-            if (_fields == null)
-                _fields = new HashSet();
-            _fields.add(field);
+            if (_state.fields == null)
+                _state.fields = new HashSet();
+            _state.fields.add(field);
         } finally {
             unlock();
         }
@@ -281,9 +302,9 @@
 
         lock();
         try {
-            if (_fields == null)
-                _fields = new HashSet();
-            _fields.addAll(fields);
+            if (_state.fields == null)
+                _state.fields = new HashSet();
+            _state.fields.addAll(fields);
         } finally {
             unlock();
         }
@@ -293,8 +314,8 @@
     public FetchConfiguration removeField(String field) {
         lock();
         try {
-            if (_fields != null)
-                _fields.remove(field);
+            if (_state.fields != null)
+                _state.fields.remove(field);
         } finally {
             unlock();
         }
@@ -304,8 +325,8 @@
     public FetchConfiguration removeFields(Collection fields) {
         lock();
         try {
-            if (_fields != null)
-                _fields.removeAll(fields);
+            if (_state.fields != null)
+                _state.fields.removeAll(fields);
         } finally {
             unlock();
         }
@@ -315,8 +336,8 @@
     public FetchConfiguration clearFields() {
         lock();
         try {
-            if (_fields != null)
-                _fields.clear();
+            if (_state.fields != null)
+                _state.fields.clear();
         } finally {
             unlock();
         }
@@ -324,33 +345,33 @@
     }
 
     public int getLockTimeout() {
-        return _lockTimeout;
+        return _state.lockTimeout;
     }
 
     public FetchConfiguration setLockTimeout(int timeout) {
-        if (timeout == DEFAULT && _ctx != null)
-            _lockTimeout = _ctx.getConfiguration().getLockTimeout();
+        if (timeout == DEFAULT && _state.ctx != null)
+            _state.lockTimeout = _state.ctx.getConfiguration().getLockTimeout();
         else if (timeout != DEFAULT)
-            _lockTimeout = timeout;
+            _state.lockTimeout = timeout;
         return this;
     }
 
     public int getReadLockLevel() {
-        return _readLockLevel;
+        return _state.readLockLevel;
     }
 
     public FetchConfiguration setReadLockLevel(int level) {
-        if (_ctx == null)
+        if (_state.ctx == null)
             return this;
 
         lock();
         try {
             assertActiveTransaction();
             if (level == DEFAULT)
-                _readLockLevel = _ctx.getConfiguration().
+                _state.readLockLevel = _state.ctx.getConfiguration().
                     getReadLockLevelConstant();
             else
-                _readLockLevel = level;
+                _state.readLockLevel = level;
         } finally {
             unlock();
         }
@@ -358,21 +379,21 @@
     }
 
     public int getWriteLockLevel() {
-        return _writeLockLevel;
+        return _state.writeLockLevel;
     }
 
     public FetchConfiguration setWriteLockLevel(int level) {
-        if (_ctx == null)
+        if (_state.ctx == null)
             return this;
 
         lock();
         try {
             assertActiveTransaction();
             if (level == DEFAULT)
-                _writeLockLevel = _ctx.getConfiguration().
+                _state.writeLockLevel = _state.ctx.getConfiguration().
                     getWriteLockLevelConstant();
             else
-                _writeLockLevel = level;
+                _state.writeLockLevel = level;
         } finally {
             unlock();
         }
@@ -382,82 +403,51 @@
     public ResultList newResultList(ResultObjectProvider rop) {
         if (rop instanceof ListResultObjectProvider)
             return new SimpleResultList(rop);
-        if (_fetchBatchSize < 0)
+        if (_state.fetchBatchSize < 0)
             return new EagerResultList(rop);
         if (rop.supportsRandomAccess())
             return new SimpleResultList(rop);
         return new WindowResultList(rop);
     }
 
-    public FetchState newFetchState() {
-        return new FetchStateImpl(this);
-    }
-
     /**
      * Throw an exception if no transaction is active.
      */
     private void assertActiveTransaction() {
-        if (_ctx != null && !_ctx.isActive())
+        if (_state.ctx != null && !_state.ctx.isActive())
             throw new NoTransactionException(_loc.get("not-active"));
     }
 
-    public String toString() {
-        if ((_fetchGroups == null || _fetchGroups.isEmpty())
-            && (_fields == null || _fields.isEmpty()))
-            return "Default";
-
-        StringBuffer buf = new StringBuffer();
-        lock();
-        try {
-            if (_fetchGroups != null && !_fetchGroups.isEmpty()) {
-                for (Iterator itr = _fetchGroups.iterator(); itr.hasNext();) {
-                    if (buf.length() > 0)
-                        buf.append(", ");
-                    buf.append(itr.next());
-                }
-            }
-            if (_fields != null && !_fields.isEmpty()) {
-                for (Iterator itr = _fields.iterator(); itr.hasNext();) {
-                    if (buf.length() > 0)
-                        buf.append(", ");
-                    buf.append(itr.next());
-                }
-            }
-        } finally {
-            unlock();
-        }
-        return buf.toString();
-    }
-
     public void setHint(String name, Object value) {
         lock();
         try {
-            if (_hints == null)
-                _hints = new HashMap();
-            _hints.put(name, value);
+            if (_state.hints == null)
+                _state.hints = new HashMap();
+            _state.hints.put(name, value);
         } finally {
             unlock();
         }
     }
 
     public Object getHint(String name) {
-        return (_hints == null) ? null : _hints.get(name);
+        return (_state.hints == null) ? null : _state.hints.get(name);
     }
 
     public Set getRootClasses() {
-        return (_rootClasses == null) ? Collections.EMPTY_SET : _rootClasses;
+        return (_state.rootClasses == null) ? Collections.EMPTY_SET 
+            : _state.rootClasses;
     }
 
     public FetchConfiguration setRootClasses(Collection classes) {
         lock();
         try {
-            if (_rootClasses != null)
-                _rootClasses.clear();
+            if (_state.rootClasses != null)
+                _state.rootClasses.clear();
             if (classes != null && !classes.isEmpty()) {
-                if (_rootClasses == null)
-                    _rootClasses = new HashSet(classes);
+                if (_state.rootClasses == null)
+                    _state.rootClasses = new HashSet(classes);
                 else 
-                    _rootClasses.addAll(classes);
+                    _state.rootClasses.addAll(classes);
             }
         } finally {
             unlock();
@@ -466,20 +456,20 @@
     }
 
     public Set getRootInstances() {
-        return (_rootInstances == null) ? Collections.EMPTY_SET 
-            : _rootInstances;
+        return (_state.rootInstances == null) ? Collections.EMPTY_SET 
+            : _state.rootInstances;
     }
 
     public FetchConfiguration setRootInstances(Collection instances) {
         lock();
         try {
-            if (_rootInstances != null)
-                _rootInstances.clear();
+            if (_state.rootInstances != null)
+                _state.rootInstances.clear();
             if (instances != null && !instances.isEmpty()) {
-                if (_rootInstances == null)
-                    _rootInstances = new HashSet(instances);
+                if (_state.rootInstances == null)
+                    _state.rootInstances = new HashSet(instances);
                 else 
-                    _rootInstances.addAll(instances);
+                    _state.rootInstances.addAll(instances);
             }
         } finally {
             unlock();
@@ -488,12 +478,215 @@
     }
 
     public void lock() {
-        if (_ctx != null)
-            _ctx.lock();
+        if (_state.ctx != null)
+            _state.ctx.lock();
     }
 
     public void unlock() {
-        if (_ctx != null)
-            _ctx.unlock();
+        if (_state.ctx != null)
+            _state.ctx.unlock();
+    }
+
+    /////////////
+    // Traversal
+    /////////////
+    
+    public boolean requiresFetch(FieldMetaData fm) {
+        if (!includes(fm))
+            return false;
+        
+        Class type = getRelationType(fm);
+        if (type == null)
+            return true;
+        if (_availableDepth == 0)
+            return false;
+
+        // we can skip calculating recursion depth if this is a top-level conf:
+        // the field is in our fetch groups, so can't possibly not select
+        if (_parent == null) 
+            return true;
+
+        int rdepth = getAvailableRecursionDepth(fm, type, false);
+        return rdepth == FetchGroup.DEPTH_INFINITE || rdepth > 0;
+    }
+
+    public FetchConfiguration traverse(FieldMetaData fm) {
+        Class type = getRelationType(fm);
+        if (type == null)
+            return this;
+
+        FetchConfigurationImpl clone = newInstance(_state);
+        clone._parent = this;
+        clone._availableDepth = reduce(_availableDepth);
+        clone._fromField = fm.getFullName();
+        clone._fromType = type;
+        clone._availableRecursion = getAvailableRecursionDepth(fm, type, true);
+        return clone;
+    }
+
+    /**
+     * Whether our configuration state includes the given field.
+     */
+    private boolean includes(FieldMetaData fmd) {
+        if ((fmd.isInDefaultFetchGroup() 
+            && hasFetchGroup(FetchGroup.NAME_DEFAULT))
+            || hasFetchGroup(FetchGroup.NAME_ALL)
+            || hasField(fmd.getFullName()))
+            return true;
+        String[] fgs = fmd.getCustomFetchGroups();
+        for (int i = 0; i < fgs.length; i++)
+            if (hasFetchGroup(fgs[i]))
+                return true;
+        return false; 
+    }
+
+    /**
+     * Return the available recursion depth via the given field for the
+     * given type.
+     *
+     * @param traverse whether we're traversing the field
+     */
+    private int getAvailableRecursionDepth(FieldMetaData fm, Class type, 
+        boolean traverse) {
+        // see if there's a previous limit
+        int avail = Integer.MIN_VALUE;
+        for (FetchConfigurationImpl f = this; f != null; f = f._parent) {
+            if (isAssignable(type, f._fromType)) {
+                avail = f._availableRecursion;
+                if (traverse)
+                    avail = reduce(avail);
+                break;
+            }
+        }
+        if (avail == 0)
+            return 0;
+        
+        // calculate fetch groups max
+        ClassMetaData meta = fm.getDefiningMetaData();
+        int max = Integer.MIN_VALUE;
+        if (fm.isInDefaultFetchGroup())
+            max = meta.getFetchGroup(FetchGroup.NAME_DEFAULT).
+                getRecursionDepth(fm);
+        String[] groups = fm.getCustomFetchGroups();
+        int cur;
+        for (int i = 0; max != FetchGroup.DEPTH_INFINITE 
+            && i < groups.length; i++) {
+            cur = meta.getFetchGroup(groups[i]).getRecursionDepth(fm);
+            if (cur == FetchGroup.DEPTH_INFINITE || cur > max) 
+                max = cur;
+        }
+        // reduce max if we're traversing a self-type relation
+        if (traverse && max != Integer.MIN_VALUE 
+            && isAssignable(meta.getDescribedType(), type))
+            max = reduce(max);
+
+        // take min/defined of previous avail and fetch group max
+        if (avail == Integer.MIN_VALUE && max == Integer.MIN_VALUE) {
+            int def = FetchGroup.RECURSION_DEPTH_DEFAULT;
+            return (traverse && isAssignable(meta.getDescribedType(), type))
+                ? def - 1 : def;
+        }
+        if (avail == Integer.MIN_VALUE || avail == FetchGroup.DEPTH_INFINITE)
+            return max;
+        if (max == Integer.MIN_VALUE || max == FetchGroup.DEPTH_INFINITE)
+            return avail;
+        return Math.min(max, avail);
+    }
+ 
+    /**
+     * Return the relation type of the given field.
+     */
+    private static Class getRelationType(FieldMetaData fm) {
+        if (fm.isDeclaredTypePC())
+            return fm.getDeclaredType();
+        if (fm.getElement().isDeclaredTypePC())
+            return fm.getElement().getDeclaredType();
+        if (fm.getKey().isDeclaredTypePC())
+            return fm.getKey().getDeclaredType();
+        return null;
+    }
+
+    /**
+     * Whether either of the two types is assignable from the other.
+     */
+    private static boolean isAssignable(Class c1, Class c2) {
+        return c1 != null && c2 != null 
+            && (c1.isAssignableFrom(c2) || c2.isAssignableFrom(c1));
+    }
+
+    /**
+     * Reduce the given logical depth by 1.
+     */
+    private static int reduce(int d) {
+        if (d == 0)
+            return 0;
+        if (d != FetchGroup.DEPTH_INFINITE)
+            d--;
+        return d;
+    }
+
+    /////////////////
+    // Debug methods
+    /////////////////
+
+    FetchConfiguration getParent() {
+        return _parent;
+    }
+    
+    boolean isRoot() {
+        return _parent == null;
+    }
+    
+    FetchConfiguration getRoot() {
+        return (isRoot()) ? this : _parent.getRoot();
+    }
+
+    int getAvailableFetchDepth() {
+        return _availableDepth;
+    }
+
+    int getAvailableRecursionDepth() {
+        return _availableRecursion;
+    }
+
+    String getTraversedFromField() {
+        return _fromField;
+    }
+
+    Class getTraversedFromType() {
+        return _fromType;
+    }
+
+    List getPath() {
+        if (isRoot())
+            return Collections.EMPTY_LIST;
+        return trackPath(new ArrayList());
+    }
+    
+    List trackPath(List path) {
+        if (_parent != null)
+            _parent.trackPath(path);
+        path.add(this);
+        return path;
+    }
+       
+    public String toString() {
+        return "FetchConfiguration@" + System.identityHashCode(this) 
+            + " (" + _availableDepth + ")" + getPathString();
+    }
+    
+    private String getPathString()
+    {
+        List path = getPath();
+        if (path.isEmpty())
+            return "";
+        StringBuffer buf = new StringBuffer().append (": ");
+        for (Iterator itr = path.iterator(); itr.hasNext();) {
+            buf.append(((FetchConfigurationImpl) itr.next()).
+                getTraversedFromField());
+            if (itr.hasNext())
+                buf.append("->");
+        }
+        return buf.toString();
     }
 }

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ObjectIdStateManager.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ObjectIdStateManager.java?rev=426710&r1=426709&r2=426710&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ObjectIdStateManager.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ObjectIdStateManager.java Fri Jul 28 16:48:13 2006
@@ -281,7 +281,7 @@
         throw new UnsupportedOperationException();
     }
 
-    public void load(FetchState fetchState) {
+    public void load(FetchConfiguration fetch) {
         throw new UnsupportedOperationException();
     }
 
@@ -329,7 +329,7 @@
         throw new UnsupportedOperationException();
     }
 
-    public BitSet getUnloaded(FetchState fetchState) {
+    public BitSet getUnloaded(FetchConfiguration fetch) {
         throw new UnsupportedOperationException();
     }
 

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/OpenJPAStateManager.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/OpenJPAStateManager.java?rev=426710&r1=426709&r2=426710&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/OpenJPAStateManager.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/OpenJPAStateManager.java Fri Jul 28 16:48:13 2006
@@ -74,7 +74,7 @@
     /**
      * Load fetch group fields.
      */
-    public void load(FetchState fetchState);
+    public void load(FetchConfiguration fetch);
 
     /**
      * Return the managed instance.
@@ -141,13 +141,14 @@
      * on the given fetch configuration. Pass in null to retrieve all
      * unloaded fields.
      */
-    public BitSet getUnloaded(FetchState fetchState);
+    public BitSet getUnloaded(FetchConfiguration fetch);
 
     /**
      * Create a new hollow proxy instance for the given field. In cases where
      * the field externalizes to an SCO but is declared something else, the
      * returned object may not implement {@link Proxy}. In all other cases,
-     * this method delegates to the system {@link org.apache.openjpa.util.ProxyManager}
+     * this method delegates to the system 
+     * {@link org.apache.openjpa.util.ProxyManager}
      * with the correct field information. The returned proxy's owner is
      * unset so that modifications to the proxy will not be tracked while its
      * state is initialized. Calling {@link #storeField} or {@link #store}

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PCData.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PCData.java?rev=426710&r1=426709&r2=426710&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PCData.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PCData.java Fri Jul 28 16:48:13 2006
@@ -63,11 +63,10 @@
      * into the given state manager.
      *
      * @param sm the state manager to load
-     * @param fetchState the fetch configuration to use for loading related
-     * objects
+     * @param fetch the fetch configuration to use for loading related objects
      * @param context current context information
      */
-    public void load(OpenJPAStateManager sm, FetchState fetchState,
+    public void load(OpenJPAStateManager sm, FetchConfiguration fetch,
         Object context);
 
     /**
@@ -77,12 +76,11 @@
      * @param sm the state manager to load
      * @param fields the fields to load; clear the bits for the fields
      * that are successfully loaded
-     * @param fetchState the fetch configuration to use for loading related
-     * objects
+     * @param fetch the fetch configuration to use for loading related objects
      * @param context current context information
      */
     public void load(OpenJPAStateManager sm, BitSet fields,
-        FetchState fetchState, Object context);
+        FetchConfiguration fetch, Object context);
 
     /**
      * Store all loaded fields of the state manager.

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PCDataImpl.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PCDataImpl.java?rev=426710&r1=426709&r2=426710&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PCDataImpl.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PCDataImpl.java Fri Jul 28 16:48:13 2006
@@ -127,7 +127,7 @@
         _version = version;
     }
 
-    public void load(OpenJPAStateManager sm, FetchState fetchState,
+    public void load(OpenJPAStateManager sm, FetchConfiguration fetch,
         Object context) {
         loadVersion(sm);
         loadImplData(sm);
@@ -138,13 +138,13 @@
             // fields in configured fetch groups
             if (!isLoaded(i))
                 loadIntermediate(sm, fmds[i]);
-            else if (fetchState.requiresLoad(sm, fmds[i]))
-                loadField(sm, fmds[i], fetchState, context);
+            else if (!sm.getLoaded().get(i) && fetch.requiresFetch(fmds[i]))
+                loadField(sm, fmds[i], fetch, context);
         }
     }
 
     public void load(OpenJPAStateManager sm, BitSet fields,
-        FetchState fetchState, Object context) {
+        FetchConfiguration fetch, Object context) {
         loadVersion(sm);
         loadImplData(sm);
 
@@ -159,7 +159,7 @@
             if (!isLoaded(i))
                 loadIntermediate(sm, fmd);
             else {
-                loadField(sm, fmd, fetchState, context);
+                loadField(sm, fmd, fetch, context);
                 loadImplData(sm, fmd);
                 fields.clear(i);
             }
@@ -187,9 +187,9 @@
      * Set field-level information into the given state manager.
      */
     protected void loadField(OpenJPAStateManager sm, FieldMetaData fmd,
-        FetchState fetchState, Object context) {
+        FetchConfiguration fetch, Object context) {
         int index = fmd.getIndex();
-        Object val = toField(sm, fmd, getData(index), fetchState, context);
+        Object val = toField(sm, fmd, getData(index), fetch, context);
         sm.storeField(index, val);
     }
 

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PCResultObjectProvider.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PCResultObjectProvider.java?rev=426710&r1=426709&r2=426710&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PCResultObjectProvider.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/PCResultObjectProvider.java Fri Jul 28 16:48:13 2006
@@ -34,6 +34,6 @@
      * @see StoreManager#initialize
      */
     public void initialize(OpenJPAStateManager sm, PCState state,
-        FetchState fetchState)
+        FetchConfiguration fetch)
         throws Exception;
 }

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ROPStoreManager.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ROPStoreManager.java?rev=426710&r1=426709&r2=426710&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ROPStoreManager.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ROPStoreManager.java Fri Jul 28 16:48:13 2006
@@ -40,11 +40,10 @@
     }
 
     public boolean initialize(OpenJPAStateManager sm, PCState state,
-        FetchState fetchState, Object context) {
+        FetchConfiguration fetch, Object context) {
         if (context instanceof PCResultObjectProvider) {
             try {
-                ((PCResultObjectProvider) context).initialize
-                    (sm, state, fetchState);
+                ((PCResultObjectProvider) context).initialize(sm, state, fetch);
             } catch (OpenJPAException ke) {
                 throw ke;
             } catch (Exception e) {
@@ -52,7 +51,7 @@
             }
             return true;
         }
-        return super.initialize(sm, state, fetchState, context);
+        return super.initialize(sm, state, fetch, context);
     }
 
     public boolean syncVersion(OpenJPAStateManager sm, Object context) {
@@ -66,13 +65,13 @@
     }
 
     public boolean load(OpenJPAStateManager sm, BitSet fields,
-        FetchState fetchState, int lockLevel, Object context) {
+        FetchConfiguration fetch, int lockLevel, Object context) {
         // the only way this gets called with a rop context is if the
         // rop didn't load the field on initialize, so just null
         // it out so we don't get unexpected results when our delegate
         // expectes a different context type
         if (context instanceof PCResultObjectProvider)
             context = null;
-        return super.load(sm, fields, fetchState, lockLevel, context);
+        return super.load(sm, fields, fetch, lockLevel, context);
     }
 }

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java?rev=426710&r1=426709&r2=426710&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StateManagerImpl.java Fri Jul 28 16:48:13 2006
@@ -30,6 +30,7 @@
 import org.apache.openjpa.event.LifecycleEventManager;
 import org.apache.openjpa.lib.util.Localizer;
 import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.FetchGroup;
 import org.apache.openjpa.meta.FieldMetaData;
 import org.apache.openjpa.meta.JavaTypes;
 import org.apache.openjpa.meta.UpdateStrategies;
@@ -69,7 +70,7 @@
 
     private static final int FLAG_SAVE = 2 << 0;
     private static final int FLAG_DEREF = 2 << 1;
-    private static final int FLAG_DFG = 2 << 2;
+    private static final int FLAG_LOADED = 2 << 2;
     private static final int FLAG_READ_LOCKED = 2 << 3;
     private static final int FLAG_WRITE_LOCKED = 2 << 4;
     private static final int FLAG_OID_ASSIGNED = 2 << 5;
@@ -131,7 +132,8 @@
     /**
      * Constructor; supply id, type metadata, and owning persistence manager.
      */
-    protected StateManagerImpl(Object id, ClassMetaData meta, BrokerImpl broker) {
+    protected StateManagerImpl(Object id, ClassMetaData meta, 
+        BrokerImpl broker) {
         _id = id;
         _meta = meta;
         _broker = broker;
@@ -304,23 +306,21 @@
             _meta, type);
     }
 
-    public void load(FetchState fetchState) {
-        load(fetchState, LOAD_FGS, null, null, false);
+    public void load(FetchConfiguration fetch) {
+        load(fetch, LOAD_FGS, null, null, false);
     }
 
     /**
      * Load the state of this instance based on the given fetch configuration
      * and load mode. Return true if any data was loaded, false otherwise.
      */
-    protected boolean load(FetchState fetchState, int loadMode,
+    protected boolean load(FetchConfiguration fetch, int loadMode,
         BitSet exclude, Object sdata, boolean forWrite) {
         if (!forWrite && (!isPersistent() || isNew() || isDeleted()))
             return false;
 
-        if (fetchState==null)
-        	fetchState = _broker.getFetchConfiguration().newFetchState();
         // if any fields being loaded, do state transitions for read
-        BitSet fields = getUnloadedInternal(fetchState, loadMode, exclude);
+        BitSet fields = getUnloadedInternal(fetch, loadMode, exclude);
         boolean active = _broker.isActive();
         if (!forWrite && fields != null)
             beforeRead(-1);
@@ -328,10 +328,8 @@
         // call load even if no fields are being loaded, because it takes
         // care of checking if the DFG is loaded, making sure version info
         // is loaded, etc
-        FetchConfiguration fetch = fetchState.getFetchConfiguration();
         int lockLevel = calculateLockLevel(active, forWrite, fetch);
-        boolean ret = loadFields(fields, fetchState, lockLevel, sdata,
-            forWrite);
+        boolean ret = loadFields(fields, fetch, lockLevel, sdata, forWrite);
         obtainLocks(active, forWrite, lockLevel, fetch, sdata);
         return ret;
     }
@@ -380,9 +378,9 @@
         return _dirty;
     }
 
-    public BitSet getUnloaded(FetchState fetchState) {
+    public BitSet getUnloaded(FetchConfiguration fetch) {
         // collect fields to load from data store based on fetch configuration
-        BitSet fields = getUnloadedInternal(fetchState, LOAD_FGS, null);
+        BitSet fields = getUnloadedInternal(fetch, LOAD_FGS, null);
         return (fields == null) ? new BitSet(0) : fields;
     }
 
@@ -391,7 +389,7 @@
      * creating an empty bit set by returning null when there are no unloaded
      * fields.
      */
-    private BitSet getUnloadedInternal(FetchState fetchState, int mode,
+    private BitSet getUnloadedInternal(FetchConfiguration fetch, int mode,
         BitSet exclude) {
         if (exclude == StoreContext.EXCLUDE_ALL)
             return null;
@@ -408,8 +406,7 @@
                     load = !fmds[i].isTransient();
                     break;
                 case LOAD_FGS:
-                    load = fetchState == null ||
-                        fetchState.requiresLoad(this, fmds[i]);
+                    load = fetch == null || fetch.requiresFetch(fmds[i]);
                     break;
                 default: // LOAD_ALL
                     load = true;
@@ -1204,8 +1201,8 @@
             if (_meta.isDetachable())
                 return DetachManager.preSerialize(this);
 
-            load(_broker.getFetchConfiguration().newFetchState(),
-                LOAD_SERIALIZE, null, null, false);
+            load(_broker.getFetchConfiguration(), LOAD_SERIALIZE, null, null, 
+                false);
             return false;
         } catch (RuntimeException re) {
             throw translate(re);
@@ -2412,10 +2409,10 @@
                 setLoaded(i, val);
         }
         if (!val) {
-            _flags &= ~FLAG_DFG;
+            _flags &= ~FLAG_LOADED;
             setDirty(false);
         } else
-            _flags |= FLAG_DFG;
+            _flags |= FLAG_LOADED;
     }
 
     /**
@@ -2438,7 +2435,7 @@
         }
 
         if (val)
-            _flags |= FLAG_DFG;
+            _flags |= FLAG_LOADED;
     }
 
     /**
@@ -2706,7 +2703,7 @@
      * Load the given field set from the data store into the instance.
      * Return true if any data is loaded, false otherwise.
      */
-    boolean loadFields(BitSet fields, FetchState fetchState, int lockLevel,
+    boolean loadFields(BitSet fields, FetchConfiguration fetch, int lockLevel,
         Object sdata, boolean forWrite) {
         // can't load version field from store
         if (fields != null) {
@@ -2721,7 +2718,7 @@
             // if any fields given, load them
             int len = (fields == null) ? 0 : fields.length();
             if (len > 0) {
-                if (!_broker.getStoreManager().load(this, fields, fetchState,
+                if (!_broker.getStoreManager().load(this, fields, fetch,
                     lockLevel, sdata)) {
                     throw new ObjectNotFoundException(_loc.get("del-instance",
                         _meta.getDescribedType(), _oid)).
@@ -2748,36 +2745,33 @@
         // see if the dfg is now loaded; do this regardless of whether we
         // loaded any fields, cause may already have been loaded by
         // StoreManager during initialization
-        postLoad(-1, fetchState);
+        postLoad(-1, fetch);
         return ret;
     }
 
-    protected void loadField(int field, int lockLevel, boolean forWrite,
-        boolean fgs) {
-    	FetchConfiguration fc = _broker.getFetchConfiguration();
-        loadField(field, fc.newFetchState(),lockLevel, forWrite, fgs);
-     }
-
     /**
      * Load the given field's fetch group; the field itself may already be
      * loaded if it is being set by the user.
      */
-    protected void loadField(int field, FetchState fetchState, int lockLevel,
-        boolean forWrite, boolean fgs) {
+    protected void loadField(int field, int lockLevel, boolean forWrite,
+        boolean fgs) {
+    	FetchConfiguration fetch = _broker.getFetchConfiguration();
         FieldMetaData fmd = _meta.getField(field);
         BitSet fields = null;
         // if this is a dfg field or we need to load our dfg, do so
         if (fmd.isInDefaultFetchGroup()
-            || (fgs && (_flags & FLAG_DFG) == 0)) {
-            fields = getUnloadedInternal(fetchState, LOAD_FGS, null);
-        }
-
+            || (fgs && (_flags & FLAG_LOADED) == 0)) 
+            fields = getUnloadedInternal(fetch, LOAD_FGS, null);
+        
+        // if not a dfg field, use first custom fetch group as load group
+        //### need to use metadata load-fetch-group
         if (!fmd.isInDefaultFetchGroup()) {
-            if (fmd.getFetchGroups() != null) {
+            if (fmd.getCustomFetchGroups().length > 0) {  
+                String fg = fmd.getCustomFetchGroups()[0];
                 FieldMetaData[] fmds = _meta.getFields();
                 for (int i = 0; i < fmds.length; i++) {
                     if (!_loaded.get(i) && (i == field
-                        || fmd.fetchGroupOverlapsWith(fmds[i]))) {
+                        || fmds[i].isInFetchGroup(fg))) {
                         if (fields == null)
                             fields = new BitSet(fmds.length);
                         fields.set(i);
@@ -2785,7 +2779,7 @@
                 }
             } else if (!_loaded.get(fmd.getIndex())) {
                 if (fields == null)
-                    fields = new BitSet(fmd.getIndex());
+                    fields = new BitSet();
                 fields.set(fmd.getIndex());
             }
         }
@@ -2793,7 +2787,20 @@
         // call this method even if there are no unloaded fields; loadFields
         // takes care of things like loading version info and setting PC
         // flags
-        loadFields(fields, fetchState, lockLevel, null, forWrite);
+        loadFields(fields, fetch, lockLevel, null, forWrite);
+    }
+
+    /**
+     * Return whether the second field has any custom fetch groups in common 
+     * with the first.
+     */
+    private static boolean sharesCustomFetchGroups(FieldMetaData fmd1, 
+        FieldMetaData fmd2) {
+        String[] fgs = fmd1.getCustomFetchGroups();
+        for (int i = 0; i < fgs.length; i++)
+            if (fmd2.isInFetchGroup(fgs[i]))
+                return true;
+        return false;
     }
 
     /**
@@ -2848,12 +2855,9 @@
      * @param field the field index that was loaded, or -1 to indicate
      * that a group of possibly unknown fields was loaded
      */
-    private void postLoad(int field, FetchState fetchState) {
+    private void postLoad(int field, FetchConfiguration fetch) {
         // no need for postLoad callback?
-        if ((_flags & FLAG_DFG) > 0)
-            return;
-        LifecycleEventManager mgr = _broker.getLifecycleEventManager();
-        if (mgr == null || !mgr.hasLoadListeners(getManagedInstance(), _meta))
+        if ((_flags & FLAG_LOADED) != 0)
             return;
 
         // in the middle of a group load, after which this method will be
@@ -2861,33 +2865,46 @@
         if (field != -1 && isLoading())
             return;
 
-        // is this field in the dfg?
-        FieldMetaData[] fmds = _meta.getDefaultFetchGroupFields();
+        // no listeners?
+        LifecycleEventManager mgr = _broker.getLifecycleEventManager();
+        if (mgr == null || !mgr.hasLoadListeners(getManagedInstance(), _meta))
+            return;
 
-        // see if any fetch group with postLoad=true is fully loaded
-        boolean isLoaded = true;
-        for (int i = 0; isLoaded && i < fmds.length; i++)
-            if (!_loaded.get(fmds[i].getIndex())
-                && requiresPostLoadCallabck(fmds[i], fetchState))
-                isLoaded = false;
-        if (isLoaded) {
-            _flags |= FLAG_DFG;
-            fireLifecycleEvent(LifecycleEvent.AFTER_LOAD);
+        // is this field a post-load field?
+        if (field != -1) {
+            FieldMetaData fmd = _meta.getField (field);
+            if (fmd.isInDefaultFetchGroup() 
+                && postLoad(_meta.getFetchGroup(FetchGroup.NAME_DEFAULT)))
+                return;
+            String[] fgs = fmd.getCustomFetchGroups();
+            for (int i = 0; i < fgs.length; i++)
+                if (postLoad(_meta.getFetchGroup(fgs[i])))
+                    return;
+        } else {
+            if (postLoad(_meta.getFetchGroup(FetchGroup.NAME_DEFAULT)))
+                return;
+            FetchGroup[] fgs = _meta.getCustomFetchGroups();
+            for (int i = 0; i < fgs.length; i++)
+                if (postLoad(fgs[i]))
+                    return; 
         }
     }
 
-    private boolean requiresPostLoadCallabck(FieldMetaData fm, FetchState fetchState) {
-        if (fm == null)
+    /**
+     * Perform post-load actions if the given fetch group is a post-load group
+     * and is fully loaded.
+     */
+    private boolean postLoad(FetchGroup fg) {
+        if (!fg.isPostLoad())
             return false;
-        Set fetchGroups = fm.getFetchGroups();
-        for (Iterator i = fetchGroups.iterator(); i.hasNext();) 
-        {
-        	String fg = i.next().toString();
-        	if (_broker.getFetchConfiguration().hasFetchGroup(fg)
-        		&& fm.getDeclaringMetaData().getFetchGroup(fg).isPostLoad())
-        		return true;
-        }
-        return false;
+        FieldMetaData[] fmds = _meta.getFields();
+        for (int i = 0; i < fmds.length; i++)
+            if (!_loaded.get(i) && fmds[i].isInFetchGroup(fg.getName()))
+                return false;
+
+        _flags |= FLAG_LOADED;
+        fireLifecycleEvent(LifecycleEvent.AFTER_LOAD);
+        return true;
     }
 
     /**

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreContext.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreContext.java?rev=426710&r1=426709&r2=426710&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreContext.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreContext.java Fri Jul 28 16:48:13 2006
@@ -135,13 +135,13 @@
      * if a cached instance has been deleted concurrently. These options
      * are controllable through the given <code>OID_XXX</code> flags.
      */
-    public Object find(Object oid, FetchState fetchState, BitSet exclude,
+    public Object find(Object oid, FetchConfiguration fetch, BitSet exclude,
         Object edata, int flags);
 
     /**
      * Return the objects with the given oids.
      *
-     * @see #find(Object,FetchState,BitSet,Object,int)
+     * @see #find(Object,FetchConfiguration,BitSet,Object,int)
      */
     public Object[] findAll(Collection oids, FetchConfiguration fetch,
         BitSet exclude, Object edata, int flags);

Modified: incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreManager.java
URL: http://svn.apache.org/viewvc/incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreManager.java?rev=426710&r1=426709&r2=426710&view=diff
==============================================================================
--- incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreManager.java (original)
+++ incubator/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreManager.java Fri Jul 28 16:48:13 2006
@@ -129,14 +129,14 @@
      *
      * @param sm the instance to initialize
      * @param state the lifecycle state to initialize the state manager with
-     * @param fetchState configuration for how to load the instance
+     * @param fetch configuration for how to load the instance
      * @param edata the current execution data, or null if not
      * given to the calling method of the broker
      * @return true if the matching instance exists in the data
      * store, false otherwise
      */
     public boolean initialize(OpenJPAStateManager sm, PCState state,
-        FetchState fetchState, Object edata);
+        FetchConfiguration fetch, Object edata);
 
     /**
      * Load the given state manager.
@@ -153,7 +153,7 @@
      * @param sm the instance to load
      * @param fields set of fields to load; all field indexes in this
      * set must be loaded; this set is mutable
-     * @param fetchState the fetch configuration to use when loading
+     * @param fetch the fetch configuration to use when loading
      * related objects
      * @param lockLevel attempt to load simple fields at this lock level;
      * relations should be loaded at the read lock level
@@ -164,7 +164,7 @@
      * database, true otherwise
      */
     public boolean load(OpenJPAStateManager sm, BitSet fields,
-        FetchState fetchState, int lockLevel, Object edata);
+        FetchConfiguration fetch, int lockLevel, Object edata);
 
     /**
      * Initialize, load, or validate the existance of all of the given
@@ -192,7 +192,8 @@
      * be included outside of refresh invocations if a user calls
      * <code>findAll</code> with the <code>validate</code>
      * parameter set to <code>true</code>.</li>
-     * </ul> Store managers that cannot efficiently batch load can simply test
+     * </ul> 
+     * Store managers that cannot efficiently batch load can simply test
      * for these conditions and delegate to the proper methods.
      *
      * @param sms the state manager instances to load
@@ -201,7 +202,7 @@
      * instances are included in <code>sms</code>
      * @param load one of the FORCE_LOAD_* constants describing the
      * fields to force-load if this is a refresh or retrieve action
-     * @param fetchState the current fetch configuration to use when loading
+     * @param fetch the current fetch configuration to use when loading
      * related objects
      * @param edata the current execution data, or null if not
      * given to the calling method of the broker