KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > triactive > jdo > state > StateManagerImpl


1 /*
2  * Copyright 2004 (C) TJDO.
3  * All rights reserved.
4  *
5  * This software is distributed under the terms of the TJDO License version 1.0.
6  * See the terms of the TJDO License in the documentation provided with this software.
7  *
8  * $Id: StateManagerImpl.java,v 1.19 2004/01/25 22:31:16 jackknifebarber Exp $
9  */

10
11 package com.triactive.jdo.state;
12
13 import com.triactive.jdo.AbstractFieldManager;
14 import com.triactive.jdo.FieldManager;
15 import com.triactive.jdo.GenericFieldManager;
16 import com.triactive.jdo.PersistenceManager;
17 import com.triactive.jdo.SCO;
18 import com.triactive.jdo.model.ClassMetaData;
19 import com.triactive.jdo.model.FieldMetaData;
20 import com.triactive.jdo.sco.SCOProcessor;
21 import com.triactive.jdo.store.StoreManager;
22 import java.io.PrintWriter JavaDoc;
23 import java.lang.reflect.Method JavaDoc;
24 import java.util.Arrays JavaDoc;
25 import java.util.Collection JavaDoc;
26 import java.util.Map JavaDoc;
27 import javax.jdo.InstanceCallbacks;
28 import javax.jdo.JDOFatalInternalException;
29 import javax.jdo.JDOFatalUserException;
30 import javax.jdo.JDOUnsupportedOptionException;
31 import javax.jdo.JDOUserException;
32 import javax.jdo.Transaction;
33 import javax.jdo.spi.JDOImplHelper;
34 import javax.jdo.spi.PersistenceCapable;
35 import javax.jdo.spi.StateManager;
36 import org.apache.log4j.Category;
37
38
39 /**
40  * This class implements the StateManager.
41  *
42  * @author <a HREF="mailto:mmartin5@austin.rr.com">Mike Martin</a>
43  * @version $Revision: 1.19 $
44  */

45
46 public class StateManagerImpl implements com.triactive.jdo.StateManager
47 {
48     private static final Category LOG = Category.getInstance(StateManagerImpl.class);
49     private static final JDOImplHelper helper;
50     private static final StateFieldManager hollowFieldManager = new StateFieldManager();
51
52     private PersistenceManager myPM;
53     private PersistenceCapable myPC;
54     private Transaction myTX;
55     private Object JavaDoc myID;
56     private LifeCycleState myLC;
57     private byte jdoFlags;
58     private PersistenceCapable savedImage = null;
59     private byte savedFlags;
60     private boolean[] savedLoadedFields = null;
61
62     private ClassMetaData cmd;
63     private StoreManager srm;
64     private int fieldCount;
65
66     private boolean inserting = false;
67     private boolean dirty = false;
68     private boolean deleting = false;
69     private boolean flushing = false;
70     private boolean changingState = false;
71     private boolean postLoadPending = false;
72     private boolean disconnecting = false;
73     private boolean[] dirtyFields;
74     private boolean[] loadedFields;
75     private boolean[] defaultFetchGroupFields;
76     private boolean[] secondClassMutableFields;
77     private int[] allFieldNumbers;
78     private int[] persistentFieldNumbers;
79     private int[] defaultFetchGroupFieldNumbers;
80     private int[] secondClassMutableFieldNumbers;
81
82     private FieldManager currFM = null;
83
84     static
85     {
86         try
87         {
88             helper = JDOImplHelper.getInstance();
89         }
90         catch (SecurityException JavaDoc e)
91         {
92             throw new JDOFatalUserException("Insufficient access granted to com.triactive.jdo.*", e);
93         }
94     }
95
96
97     /**
98      * Constructs a state manager to manage an existing persistence-capable
99      * instance that is not persistent.
100      * All mutable SCO fields are wrapped in suitable wrapper objects and the
101      * PC instance transitions to a TransientClean state.
102      *
103      * @param pm
104      * The persistence manager controlling this state manager.
105      * @param pc
106      * The instance to be managed.
107      */

108
109     public StateManagerImpl(PersistenceManager pm, PersistenceCapable pc)
110     {
111         myPM = pm;
112         myTX = pm.currentTransaction();
113         srm = myPM.getStoreManager();
114         myPC = pc;
115         myID = null;
116         myLC = LifeCycleState.getLifeCycleState(LifeCycleState.T_CLEAN);
117         jdoFlags = PersistenceCapable.READ_OK;
118         cmd = ClassMetaData.forClass(pc.getClass());
119
120         initialize();
121
122         for (int i = 0; i < fieldCount; ++i)
123             loadedFields[i] = true;
124
125         myPC.jdoReplaceStateManager(this);
126         myPC.jdoReplaceFlags();
127
128         replaceSCOFields();
129     }
130
131
132     /**
133      * Constructs a state manager to manage a new, hollow instance having the
134      * given object ID.
135      * This constructor is used for creating new instances of existing
136      * persistent objects (i.e. via pm.getObjectById()).
137      *
138      * @param pm
139      * The persistence manager controlling this state manager.
140      * @param pcClass
141      * The class of the new instance to be created.
142      * @param id
143      * The JDO identity of the object.
144      */

145
146     public StateManagerImpl(PersistenceManager pm, Class JavaDoc pcClass, Object JavaDoc id)
147     {
148         myPM = pm;
149         myTX = pm.currentTransaction();
150         srm = myPM.getStoreManager();
151         myID = id;
152         myLC = LifeCycleState.getLifeCycleState(LifeCycleState.HOLLOW);
153         jdoFlags = PersistenceCapable.LOAD_REQUIRED;
154         cmd = ClassMetaData.forClass(pcClass);
155
156         initialize();
157
158         myPC = helper.newInstance(pcClass, this);
159     }
160
161
162     private void initialize()
163     {
164         fieldCount = cmd.getInheritedFieldCount() + cmd.getFieldCount();
165
166         dirtyFields = new boolean[fieldCount];
167         loadedFields = (boolean[])cmd.getTransactionalFieldFlags().clone();
168         defaultFetchGroupFields = cmd.getDefaultFetchGroupFieldFlags();
169         secondClassMutableFields = cmd.getSecondClassMutableFieldFlags();
170
171         allFieldNumbers = cmd.getAllFieldNumbers();
172         persistentFieldNumbers = cmd.getPersistentFieldNumbers();
173         defaultFetchGroupFieldNumbers = cmd.getDefaultFetchGroupFieldNumbers();
174         secondClassMutableFieldNumbers = cmd.getSecondClassMutableFieldNumbers();
175     }
176
177
178     /**
179      * Passes all SCO field values through replaceFields(), which will wrap any
180      * unwrapped values with an appropriate SCO wrapper.
181      */

182     void replaceSCOFields()
183     {
184         StateFieldManager scoFieldValues = new StateFieldManager();
185
186         provideFields(secondClassMutableFieldNumbers, scoFieldValues);
187         replaceFields(secondClassMutableFieldNumbers, scoFieldValues);
188     }
189
190
191     Object JavaDoc wrapSCOInstance(int field, Object JavaDoc value)
192     {
193         if (value == null || (value instanceof SCO && ((SCO)value).getOwner() == myPC))
194             return value;
195         else
196         {
197             FieldMetaData fmd = cmd.getFieldAbsolute(field);
198             Class JavaDoc fieldType = fmd.getType();
199
200             SCOProcessor scoProc = SCOProcessor.forFieldType(fieldType);
201
202             if (scoProc == null)
203                 throw new JDOUserException("Class not supported as a second-class object: " + fieldType.getName());
204
205             return scoProc.newSCOInstance(myPC, fmd.getName(), value);
206         }
207     }
208
209
210     /**
211      * Calls unsetOwner() on all SCO fields.
212      */

213     private void disownSCOFields()
214     {
215         provideFields(secondClassMutableFieldNumbers, new AbstractFieldManager()
216         {
217             public void storeObjectField(int fieldNumber, Object JavaDoc value)
218             {
219                 if (value instanceof SCO)
220                     ((SCO)value).unsetOwner();
221             }
222         });
223     }
224
225
226     private boolean isDFGLoaded()
227     {
228         for (int i = 0; i < defaultFetchGroupFieldNumbers.length; ++i)
229         {
230             if (!loadedFields[defaultFetchGroupFieldNumbers[i]])
231                 return false;
232         }
233
234         return true;
235     }
236
237
238     void enlistInTransaction()
239     {
240         myPM.enlistInTransaction(this);
241
242         if (jdoFlags == PersistenceCapable.LOAD_REQUIRED && isDFGLoaded())
243         {
244             /*
245              * A transactional object whose DFG fields are loaded does not need
246              * to contact us in order to read those fields.
247              */

248             jdoFlags = PersistenceCapable.READ_OK;
249             myPC.jdoReplaceFlags();
250         }
251     }
252
253
254     void evictFromTransaction()
255     {
256         myPM.evictFromTransaction(this);
257
258         if (jdoFlags == PersistenceCapable.READ_OK && myLC.isPersistent())
259         {
260             /*
261              * A non-transactional object needs to contact us on any field read no
262              * matter what fields are loaded.
263              */

264             jdoFlags = PersistenceCapable.LOAD_REQUIRED;
265             myPC.jdoReplaceFlags();
266         }
267     }
268
269
270     void saveFields()
271     {
272         savedFlags = jdoFlags;
273         savedLoadedFields = (boolean[])loadedFields.clone();
274         savedImage = myPC.jdoNewInstance(this);
275         savedImage.jdoCopyFields(myPC, allFieldNumbers);
276
277         for (int i = 0; i < secondClassMutableFieldNumbers.length; ++i)
278         {
279             int field = secondClassMutableFieldNumbers[i];
280
281             /*
282              * Note: SCO fields in the saved image are clones and therefore
283              * unowned. If/when they are later restored the individual life-
284              * cycle states decide whether or not they must be rewrapped to get
285              * reowned (if going Transient they don't, otherwise they do).
286              */

287             Object JavaDoc value = provideField(savedImage, field);
288
289             if (value != null)
290             {
291                 if (value instanceof SCO)
292                     value = ((SCO)value).clone();
293                 else
294                 {
295                     Class JavaDoc c = value.getClass();
296
297                     try
298                     {
299                         Method JavaDoc m = c.getMethod("clone", null);
300
301                         if (m != null)
302                             value = m.invoke(value, null);
303                     }
304                     catch (Exception JavaDoc e)
305                     {
306                         throw new JDOFatalInternalException("SCO class not cloneable: " + c.getName());
307                     }
308                 }
309
310                 replaceField(savedImage, field, value);
311             }
312         }
313     }
314
315
316     void restoreFields()
317     {
318         if (savedImage != null)
319         {
320             loadedFields = savedLoadedFields;
321             jdoFlags = savedFlags;
322             myPC.jdoReplaceFlags();
323             myPC.jdoCopyFields(savedImage, allFieldNumbers);
324
325             clearDirtyFlags();
326             discardSavedFields();
327         }
328     }
329
330
331     void discardSavedFields()
332     {
333         savedFlags = 0;
334         savedLoadedFields = null;
335         savedImage = null;
336     }
337
338
339     void clearPersistentFields()
340     {
341         try
342         {
343             if (myPC instanceof InstanceCallbacks)
344                 ((InstanceCallbacks)myPC).jdoPreClear();
345         }
346         finally
347         {
348             replaceFieldsInternal(persistentFieldNumbers, hollowFieldManager);
349             clearLoadedFlags();
350             clearDirtyFlags();
351         }
352     }
353
354
355     private void clearLoadedFlags()
356     {
357         jdoFlags = PersistenceCapable.LOAD_REQUIRED;
358         myPC.jdoReplaceFlags();
359         System.arraycopy(cmd.getTransactionalFieldFlags(), 0, loadedFields, 0, loadedFields.length);
360     }
361
362
363     private void clearDirtyFlags()
364     {
365         dirty = false;
366         clearFlags(dirtyFields);
367     }
368
369
370     /**
371      * Returns an array of integers containing the indices of all elements in
372      * <tt>flags</tt> that are set to <tt>state</tt>.
373      */

374
375     private static int[] getFlagsSetTo(boolean[] flags, boolean state)
376     {
377         int[] temp = new int[flags.length];
378         int j = 0;
379
380         for (int i = 0; i < flags.length; i++)
381         {
382             if (flags[i] == state)
383                 temp[j++] = i;
384         }
385
386         if (j != 0)
387         {
388             int[] fieldNumbers = new int[j];
389             System.arraycopy(temp, 0, fieldNumbers, 0, j);
390
391             return fieldNumbers;
392         }
393         else
394             return null;
395     }
396
397
398     /**
399      * Returns an array of integers containing the indices of all elements in
400      * <tt>flags</tt> whose index occurs in <tt>indices</tt> and whose value is
401      * <tt>state</tt>.
402      */

403
404     private static int[] getFlagsSetTo(boolean[] flags, int[] indices, boolean state)
405     {
406         int[] temp = new int[indices.length];
407         int j = 0;
408
409         for (int i = 0; i < indices.length; i++)
410         {
411             if (flags[indices[i]] == state)
412                 temp[j++] = indices[i];
413         }
414
415         if (j != 0)
416         {
417             int[] fieldNumbers = new int[j];
418             System.arraycopy(temp, 0, fieldNumbers, 0, j);
419
420             return fieldNumbers;
421         }
422         else
423             return null;
424     }
425
426
427     private static void clearFlags(boolean[] flags)
428     {
429         for (int i = 0; i < flags.length; i++)
430             flags[i] = false;
431     }
432
433
434     private boolean disconnectClone(PersistenceCapable pc)
435     {
436         if (pc != myPC)
437         {
438             if (LOG.isDebugEnabled())
439                 LOG.debug("Disconnecting clone " + toJVMIDString(pc) + " from " + this);
440
441             /*
442              * Reset jdoFlags in the clone to PersistenceCapable.READ_WRITE_OK
443              * and clear its state manager.
444              */

445             byte myJdoFlags = jdoFlags;
446             jdoFlags = PersistenceCapable.READ_WRITE_OK;
447
448             try
449             {
450                 pc.jdoReplaceFlags();
451             }
452             finally
453             {
454                 jdoFlags = myJdoFlags;
455             }
456
457             pc.jdoReplaceStateManager(null);
458             return true;
459         }
460         else
461             return false;
462     }
463
464
465     /**
466      * The StateManager uses this method to supply the value of jdoFlags to the
467      * associated PersistenceCapable instance.
468      *
469      * @param pc
470      * the calling PersistenceCapable instance
471      * @return
472      * the value of jdoFlags to be stored in the PersistenceCapable instance
473      */

474
475     public byte replacingFlags(PersistenceCapable pc)
476     {
477         // If this is a clone, return READ_WRITE_OK.
478
if (pc != myPC)
479             return PersistenceCapable.READ_WRITE_OK;
480         else
481             return jdoFlags;
482     }
483
484
485     /**
486      * Return the PersistenceManager that owns this instance.
487      *
488      * @param pc
489      * the calling PersistenceCapable instance
490      * @return
491      * the PersistenceManager that owns this instance
492      */

493
494     public javax.jdo.PersistenceManager getPersistenceManager(PersistenceCapable pc)
495     {
496         if (disconnectClone(pc))
497             return null;
498         else
499         {
500             this.myPM.hereIsStateManager(this, myPC);
501             return myPM;
502         }
503     }
504
505
506     private void postWriteField(int field)
507     {
508         /*
509          * If we've written a field in the middle of inserting or flushing it
510          * must be due to jdoPreStore(). If inserting, we haven't actually done
511          * the INSERT yet so we don't want to mark anything as dirty, which
512          * would make us want to do an UPDATE later.
513          */

514         if (myLC.isPersistent() && !inserting)
515         {
516             dirty = true;
517             dirtyFields[field] = true;
518             loadedFields[field] = true;
519
520             /*
521              * If flushing, to avoid an infinite recursion we don't notify the
522              * PM or call flush().
523              */

524             if (!flushing)
525             {
526                 if (myTX.isActive())
527                     myPM.markDirty(this);
528                 else
529                     flush();
530             }
531         }
532     }
533
534
535     /**
536      * Marks the given field dirty.
537      */

538
539     public void makeDirty(int field)
540     {
541         transitionWriteField();
542         postWriteField(field);
543     }
544
545
546     /**
547      * Mark the associated PersistenceCapable field dirty.
548      *
549      * @param pc the calling PersistenceCapable instance
550      * @param fieldName the name of the field
551      */

552
553     public void makeDirty(PersistenceCapable pc, String JavaDoc fieldName)
554     {
555         if (!disconnectClone(pc))
556         {
557             int fieldNumber = cmd.getAbsoluteFieldNumber(fieldName);
558
559             if (fieldNumber == -1)
560                 throw new JDOUserException("No such field '" + fieldName + "' in class " + cmd.getPCClass().getName());
561
562             makeDirty(fieldNumber);
563         }
564     }
565
566
567     public Object JavaDoc getObjectId()
568     {
569         return myID;
570     }
571
572
573     public StoreManager getStoreManager()
574     {
575         return srm;
576     }
577
578
579     public PersistenceManager getPersistenceManager()
580     {
581         return myPM;
582     }
583
584
585     /**
586      * Return the object representing the JDO identity of the calling instance.
587      *
588      * According to the JDO specification, if the JDO identity is being changed
589      * in the current transaction, this method returns the JDO identify as of the
590      * beginning of the transaction.
591      *
592      * @param pc the calling PersistenceCapable instance
593      * @return the object representing the JDO identity of the calling instance
594      */

595
596     public Object JavaDoc getObjectId(PersistenceCapable pc)
597     {
598         if (disconnectClone(pc))
599             return null;
600         else
601             return myID;
602     }
603
604
605     /**
606      * Replace the current value of jdoStateManager.
607      *
608      * <P>This method is called by the PersistenceCapable whenever
609      * jdoReplaceStateManager is called and there is already
610      * an owning StateManager. This is a security precaution
611      * to ensure that the owning StateManager is the only
612      * source of any change to its reference in the PersistenceCapable.</p>
613      *
614      * @return the new value for the jdoStateManager
615      * @param pc the calling PersistenceCapable instance
616      * @param sm the proposed new value for the jdoStateManager
617      */

618
619     public StateManager replacingStateManager(PersistenceCapable pc, StateManager sm)
620     {
621         if (myLC == null)
622             throw new JDOFatalInternalException("Null LifeCycleState");
623
624         if (pc == myPC)
625         {
626             if (sm != null)
627                 throw new JDOFatalInternalException("Attempted to replace with a different state manager");
628             if (!disconnecting)
629                 throw new JDOFatalInternalException("Attempted to clear state manager from other than disconnect()");
630
631             if (LOG.isDebugEnabled())
632                 LOG.debug("Clearing state manager for " + toJVMIDString(pc));
633
634             return null;
635         }
636         else if (pc == savedImage)
637             return null;
638         else
639             return sm;
640     }
641
642
643     /**
644      * Return the object representing the JDO identity
645      * of the calling instance. If the JDO identity is being changed in
646      * the current transaction, this method returns the current identity as
647      * changed in the transaction.
648      *
649      * @param pc the calling PersistenceCapable instance
650      * @return the object representing the JDO identity of the calling instance
651      */

652
653     public Object JavaDoc getTransactionalObjectId(PersistenceCapable pc)
654     {
655         return getObjectId(pc);
656     }
657
658
659     /**
660      * Tests whether this object is dirty.
661      *
662      * Instances that have been modified, deleted, or newly
663      * made persistent in the current transaction return true.
664      *
665      * <P>Transient nontransactional instances return false (JDO spec), but the
666      * TriActive implementation does not currently support the transient
667      * transactional state.
668      *
669      * @see PersistenceCapable#jdoMakeDirty(String fieldName)
670      * @param pc the calling PersistenceCapable instance
671      * @return true if this instance has been modified in the current transaction.
672      */

673
674     public boolean isDirty(PersistenceCapable pc)
675     {
676         if (disconnectClone(pc))
677             return false;
678         else
679             return myLC.isDirty();
680     }
681
682
683     /**
684      * Tests whether this object is transactional.
685      *
686      * Instances that respect transaction boundaries return true. These instances
687      * include transient instances made transactional as a result of being the
688      * target of a makeTransactional method call; newly made persistent or deleted
689      * persistent instances; persistent instances read in data store
690      * transactions; and persistent instances modified in optimistic transactions.
691      *
692      * <P>Transient nontransactional instances return false.
693      *
694      * @param pc the calling PersistenceCapable instance
695      * @return true if this instance is transactional.
696      */

697
698     public boolean isTransactional(PersistenceCapable pc)
699     {
700         if (disconnectClone(pc))
701             return false;
702         else
703             return myLC.isTransactional();
704     }
705
706
707     /**
708      * Tests whether this object is persistent.
709      *
710      * Instances whose state is stored in the data store return true.
711      *
712      * <P>Transient instances return false.
713      *
714      * @see PersistenceManager#makePersistent(Object pc)
715      * @param pc the calling PersistenceCapable instance
716      * @return true if this instance is persistent.
717      */

718
719     public boolean isPersistent(PersistenceCapable pc)
720     {
721         if (disconnectClone(pc))
722             return false;
723         else
724             return myLC.isPersistent();
725     }
726
727
728     /**
729      * Tests whether this object has been newly made persistent.
730      *
731      * Instances that have been made persistent in the current transaction
732      * return true.
733      *
734      * <P>Transient instances return false.
735      *
736      * @see PersistenceManager#makePersistent(Object pc)
737      * @param pc the calling PersistenceCapable instance
738      * @return true if this instance was made persistent
739      * in the current transaction.
740      */

741
742     public boolean isNew(PersistenceCapable pc)
743     {
744         if (disconnectClone(pc))
745             return false;
746         else
747             return myLC.isNew();
748     }
749
750
751     /**
752      * Tests whether this object has been deleted.
753      *
754      * Instances that have been deleted in the current transaction return true.
755      *
756      * <P>Transient instances return false.
757      *
758      * @see PersistenceManager#deletePersistent(Object pc)
759      * @param pc the calling PersistenceCapable instance
760      * @return true if this instance was deleted
761      * in the current transaction.
762      */

763
764     public boolean isDeleted(PersistenceCapable pc)
765     {
766         if (disconnectClone(pc))
767             return false;
768         else
769             return myLC.isDeleted();
770     }
771
772
773     /**
774      * Called whenever the default fetch group fields have all been loaded.
775      * Updates jdoFlags and calls jdoPostLoad() as appropriate.
776      * <p>
777      * If it's called in the midst of a life-cycle transition both actions will
778      * be deferred until the transition is complete.
779      * <em>This deferral is important</em>.
780      * Without it, we could enter user code (jdoPostLoad()) while still making
781      * a state transition, and that way lies madness.
782      * <p>
783      * As an example, consider a jdoPostLoad() that calls other enhanced methods
784      * that read fields (jdoPostLoad() itself is not enhanced). A P_NONTRANS
785      * object accessed within a transaction would produce the following infinite
786      * loop:
787      * <p>
788      * <blockquote><pre>
789      * isLoaded()
790      * transitionReadField()
791      * refreshLoadedFields()
792      * jdoPostLoad()
793      * isLoaded()
794      * ...
795      * </pre></blockquote>
796      * <p>
797      * because the transition from P_NONTRANS to P_CLEAN can never be completed.
798      */

799
800     private void postLoad()
801     {
802         if (changingState)
803             postLoadPending = true;
804         else
805         {
806             /*
807              * A transactional object whose DFG fields are loaded does not need
808              * to contact us in order to read those fields, so we can safely set
809              * READ_OK.
810              *
811              * A non-transactional object needs to notify us on all field reads
812              * so that we can decide whether or not any transition should occur,
813              * so we leave the flags at LOAD_REQUIRED.
814              */

815             if (jdoFlags == PersistenceCapable.LOAD_REQUIRED && myLC.isTransactional())
816             {
817                 jdoFlags = PersistenceCapable.READ_OK;
818                 myPC.jdoReplaceFlags();
819             }
820
821             if (myPC instanceof InstanceCallbacks)
822                 ((InstanceCallbacks)myPC).jdoPostLoad();
823         }
824     }
825
826
827     /**
828      * Validates that the instance exists in the data store.
829      * Called by pm.getObjectById() when validate == true.
830      */

831
832     public void validate()
833     {
834         if (!myLC.isTransactional())
835         {
836             /*
837              * We're either Hollow or PNonTrans. If a TX is active and there
838              * are any DFG fields then a retrieve() does what we want, i.e. it
839              * validates existence and makes us PClean. Otherwise we do the
840              * minimum lookup operation and leave our state as it is.
841              */

842             if (myTX.isActive() && defaultFetchGroupFieldNumbers.length != 0)
843                 retrieve(true);
844             else
845                 srm.lookup(this);
846         }
847     }
848
849
850     /**
851      * Offers the specified pre-fetched fields to the state manager.
852      * If the instance is in a state that can benefit from newly available field
853      * values, the fields are replaced in the instance and a state change occurs
854      * as though the instance itself had read a field.
855      * Called by pm.getObjectById() when given prefetched fields.
856      */

857
858     public void offerPrefetchedFields(int[] fieldNumbers, FieldManager fieldManager)
859     {
860         if (!myLC.isTransactional())
861         {
862             /* We're either Hollow or PNonTrans. Accept the fields. */
863             transitionReadField();
864
865             boolean dfgWasAlreadyLoaded = isDFGLoaded();
866
867             replaceFields(fieldNumbers, fieldManager);
868
869             if (!dfgWasAlreadyLoaded && isDFGLoaded())
870                 postLoad();
871         }
872     }
873
874
875     /**
876      * Fetchs from the database all fields in the default fetch group not
877      * already loaded.
878      * Called by, or immediately after, life-cycle transitions.
879      */

880
881     void loadDFGFields()
882     {
883         int[] fieldNumbers = getFlagsSetTo(loadedFields, defaultFetchGroupFieldNumbers, false);
884
885         if (fieldNumbers != null)
886         {
887             srm.fetch(this, fieldNumbers);
888             postLoad();
889         }
890     }
891
892
893     /**
894      * Fetch a given field from the database. Do NOT call with a default fetch
895      * group field.
896      *
897      * @param fieldNumber the field number of the field to fetch.
898      */

899
900     private void loadNonDFGField(int fieldNumber)
901     {
902         srm.fetch(this, new int[] { fieldNumber });
903     }
904
905
906     /**
907      * Fetchs from the database all fields not currently loaded.
908      * Called by life-cycle transitions.
909      */

910
911     void loadUnloadedFields()
912     {
913         int[] fieldNumbers = getFlagsSetTo(loadedFields, false);
914
915         if (fieldNumbers != null)
916         {
917             boolean dfgWasAlreadyLoaded = isDFGLoaded();
918
919             srm.fetch(this, fieldNumbers);
920
921             /* If the DFG was not already loaded, it is now. */
922             if (!dfgWasAlreadyLoaded)
923                 postLoad();
924         }
925     }
926
927
928     /**
929      * Refreshes from the database all fields currently loaded.
930      * Called by life-cycle transitions.
931      */

932
933     void refreshLoadedFields()
934     {
935         int[] fieldNumbers = getFlagsSetTo(loadedFields, true);
936
937         if (fieldNumbers != null)
938         {
939             clearDirtyFlags();
940             clearLoadedFlags();
941
942             srm.fetch(this, fieldNumbers);
943
944             if (isDFGLoaded())
945                 postLoad();
946         }
947     }
948
949
950     /**
951      * Guarantee that the serializable transactional and persistent fields
952      * are loaded into the instance. This method is called by the generated
953      * jdoPreSerialize method prior to serialization of the instance.
954      *
955      * @param pc the calling PersistenceCapable instance
956      */

957
958     public void preSerialize(PersistenceCapable pc)
959     {
960         if (disconnectClone(pc))
961             return;
962
963         retrieve(false);
964     }
965
966
967     /**
968      * Return true if the field is cached in the calling instance.
969      * <P>In the TriActive implementation of this method, isLoaded() will always
970      * return true. If the field is not loaded, it will be loaded as a side effect
971      * of the call to this method. If it is in the default fetch group, the default
972      * fetch group, including this field, will be loaded.
973      *
974      * @param pc the calling PersistenceCapable instance
975      * @param field the absolute field number
976      * @return always returns true (this implementation)
977      */

978
979     public boolean isLoaded(PersistenceCapable pc, int field)
980     {
981         if (disconnectClone(pc))
982             return true;
983         else
984         {
985             transitionReadField();
986
987             if (!loadedFields[field])
988             {
989                 if (defaultFetchGroupFields[field])
990                     loadDFGFields();
991                 else
992                     loadNonDFGField(field);
993             }
994
995             return true;
996         }
997     }
998
999
1000    /**
1001     * Reads the current value of the specified field.
1002     * This has the same effect on the state of the instance as if user code
1003     * attempted to read the field.
1004     *
1005     * @param field
1006     * The field number to read.
1007     * @return
1008     * The value of the field. Primitives are boxed in appropriate
1009     * java.lang wrapper classes.
1010     */

1011
1012    public Object JavaDoc getField(int field)
1013    {
1014        isLoaded(myPC, field);
1015        return provideField(myPC, field);
1016    }
1017
1018
1019    /**
1020     * Updates the current value of the specified field.
1021     * This has the same effect on the state of the instance as if user code
1022     * attempted to write the field.
1023     *
1024     * @param field
1025     * The field number to write.
1026     * @param currentValue
1027     * The current value of the field. Used to determine whether or not
1028     * the write is redundant. If necessary, the value can be obtained
1029     * using {@link #getField}.
1030     * @param newValue
1031     * The new value of the field. Primitives must be boxed in appropriate
1032     * java.lang wrapper classes.
1033     */

1034
1035    public void setField(int field, Object JavaDoc currentValue, Object JavaDoc newValue)
1036    {
1037        if (!loadedFields[field] || currentValue != newValue)
1038            writeObjectField(field, currentValue, newValue);
1039    }
1040
1041
1042    /**
1043     * This method is called by the associated PersistenceCapable if the
1044     * value for the specified field is not cached (i.e., StateManager.isLoaded()
1045     * fails). In this implementation of the StateManager, isLoaded() has a
1046     * side effect of loading unloaded information and will always return true.
1047     * As such, this method should never be called.
1048     */

1049
1050    public boolean getBooleanField(PersistenceCapable pc, int field, boolean currentValue)
1051    {
1052        throw new JDOUnsupportedOptionException("Method not supported");
1053    }
1054
1055
1056    /**
1057     * This method is called by the associated PersistenceCapable if the
1058     * value for the specified field is not cached (i.e., StateManager.isLoaded()
1059     * fails). In this implementation of the StateManager, isLoaded() has a
1060     * side effect of loading unloaded information and will always return true.
1061     * As such, this method should never be called.
1062     */

1063
1064    public byte getByteField(PersistenceCapable pc, int field, byte currentValue)
1065    {
1066        throw new JDOUnsupportedOptionException("Method not supported");
1067    }
1068
1069
1070    /**
1071     * This method is called by the associated PersistenceCapable if the
1072     * value for the specified field is not cached (i.e., StateManager.isLoaded()
1073     * fails). In this implementation of the StateManager, isLoaded() has a
1074     * side effect of loading unloaded information and will always return true.
1075     * As such, this method should never be called.
1076     */

1077
1078    public char getCharField(PersistenceCapable pc, int field, char currentValue)
1079    {
1080        throw new JDOUnsupportedOptionException("Method not supported");
1081    }
1082
1083
1084    /**
1085     * This method is called by the associated PersistenceCapable if the
1086     * value for the specified field is not cached (i.e., StateManager.isLoaded()
1087     * fails). In this implementation of the StateManager, isLoaded() has a
1088     * side effect of loading unloaded information and will always return true.
1089     * As such, this method should never be called.
1090     */

1091
1092    public double getDoubleField(PersistenceCapable pc, int field, double currentValue)
1093    {
1094        throw new JDOUnsupportedOptionException("Method not supported");
1095    }
1096
1097
1098    /**
1099     * This method is called by the associated PersistenceCapable if the
1100     * value for the specified field is not cached (i.e., StateManager.isLoaded()
1101     * fails). In this implementation of the StateManager, isLoaded() has a
1102     * side effect of loading unloaded information and will always return true.
1103     * As such, this method should never be called.
1104     */

1105
1106    public float getFloatField(PersistenceCapable pc, int field, float currentValue)
1107    {
1108        throw new JDOUnsupportedOptionException("Method not supported");
1109    }
1110
1111
1112    /**
1113     * This method is called by the associated PersistenceCapable if the
1114     * value for the specified field is not cached (i.e., StateManager.isLoaded()
1115     * fails). In this implementation of the StateManager, isLoaded() has a
1116     * side effect of loading unloaded information and will always return true.
1117     * As such, this method should never be called.
1118     */

1119
1120    public int getIntField(PersistenceCapable pc, int field, int currentValue)
1121    {
1122        throw new JDOUnsupportedOptionException("Method not supported");
1123    }
1124
1125
1126    /**
1127     * This method is called by the associated PersistenceCapable if the
1128     * value for the specified field is not cached (i.e., StateManager.isLoaded()
1129     * fails). In this implementation of the StateManager, isLoaded() has a
1130     * side effect of loading unloaded information and will always return true.
1131     * As such, this method should never be called.
1132     */

1133
1134    public long getLongField(PersistenceCapable pc, int field, long currentValue)
1135    {
1136        throw new JDOUnsupportedOptionException("Method not supported");
1137    }
1138
1139
1140    /**
1141     * This method is called by the associated PersistenceCapable if the
1142     * value for the specified field is not cached (i.e., StateManager.isLoaded()
1143     * fails). In this implementation of the StateManager, isLoaded() has a
1144     * side effect of loading unloaded information and will always return true.
1145     * As such, this method should never be called.
1146     */

1147
1148    public short getShortField(PersistenceCapable pc, int field, short currentValue)
1149    {
1150        throw new JDOUnsupportedOptionException("Method not supported");
1151    }
1152
1153
1154    /**
1155     * This method is called by the associated PersistenceCapable if the
1156     * value for the specified field is not cached (i.e., StateManager.isLoaded()
1157     * fails). In this implementation of the StateManager, isLoaded() has a
1158     * side effect of loading unloaded information and will always return true.
1159     * As such, this method should never be called.
1160     */

1161
1162    public String JavaDoc getStringField(PersistenceCapable pc, int field, String JavaDoc currentValue)
1163    {
1164        throw new JDOUnsupportedOptionException("Method not supported");
1165    }
1166
1167
1168    /**
1169     * This method is called by the associated PersistenceCapable if the
1170     * value for the specified field is not cached (i.e., StateManager.isLoaded()
1171     * fails). In this implementation of the StateManager, isLoaded() has a
1172     * side effect of loading unloaded information and will always return true.
1173     * As such, this method should never be called.
1174     */

1175
1176    public Object JavaDoc getObjectField(PersistenceCapable pc, int field, Object JavaDoc currentValue)
1177    {
1178        throw new JDOUnsupportedOptionException("Method not supported");
1179    }
1180
1181
1182    /**
1183     * Called by the various setXXXField() methods once it's established that
1184     * the field really deserves to be written.
1185     * Makes the state transition and replaces the field value in the PC
1186     * instance.
1187     */

1188
1189    private void writeField(int field, Object JavaDoc newValue)
1190    {
1191        transitionWriteField();
1192        replaceField(myPC, field, newValue);
1193        postWriteField(field);
1194    }
1195
1196
1197    /**
1198     * Called by setObjectField() once it's established that the field really
1199     * deserves to be written.
1200     * Makes the state transition and replaces the field value in the PC
1201     * instance.
1202     * Wraps SCO fields if needed.
1203     */

1204
1205    private void writeObjectField(int field, Object JavaDoc currentValue, Object JavaDoc newValue)
1206    {
1207        transitionWriteField();
1208
1209        if (secondClassMutableFields[field])
1210        {
1211            if (currentValue instanceof SCO)
1212                ((SCO)currentValue).unsetOwner();
1213
1214            newValue = wrapSCOInstance(field, newValue);
1215        }
1216
1217        replaceField(myPC, field, newValue);
1218        postWriteField(field);
1219    }
1220
1221
1222    /**
1223     * This method is called by the associated PersistenceCapable when the
1224     * corresponding mutator method (setXXX()) is called on the PersistenceCapable.
1225     *
1226     * @param pc the calling PersistenceCapable instance
1227     * @param field the field number
1228     * @param currentValue the current value of the field
1229     * @param newValue the new value for the field
1230     */

1231
1232    public void setBooleanField(PersistenceCapable pc, int field, boolean currentValue, boolean newValue)
1233    {
1234        if (pc != myPC)
1235        {
1236            replaceField(pc, field, newValue ? Boolean.TRUE : Boolean.FALSE);
1237            disconnectClone(pc);
1238        }
1239        else
1240        {
1241            if (!loadedFields[field] || currentValue != newValue)
1242                writeField(field, newValue ? Boolean.TRUE : Boolean.FALSE);
1243        }
1244    }
1245
1246
1247    /**
1248     * This method is called by the associated PersistenceCapable when the
1249     * corresponding mutator method (setXXX()) is called on the PersistenceCapable.
1250     *
1251     * @param pc the calling PersistenceCapable instance
1252     * @param field the field number
1253     * @param currentValue the current value of the field
1254     * @param newValue the new value for the field
1255     */

1256
1257    public void setByteField(PersistenceCapable pc, int field, byte currentValue, byte newValue)
1258    {
1259        if (pc != myPC)
1260        {
1261            replaceField(pc, field, new Byte JavaDoc(newValue));
1262            disconnectClone(pc);
1263        }
1264        else
1265        {
1266            if (!loadedFields[field] || currentValue != newValue)
1267                writeField(field, new Byte JavaDoc(newValue));
1268        }
1269    }
1270
1271
1272    /**
1273     * This method is called by the associated PersistenceCapable when the
1274     * corresponding mutator method (setXXX()) is called on the PersistenceCapable.
1275     *
1276     * @param pc the calling PersistenceCapable instance
1277     * @param field the field number
1278     * @param currentValue the current value of the field
1279     * @param newValue the new value for the field
1280     */

1281
1282    public void setCharField(PersistenceCapable pc, int field, char currentValue, char newValue)
1283    {
1284        if (pc != myPC)
1285        {
1286            replaceField(pc, field, new Character JavaDoc(newValue));
1287            disconnectClone(pc);
1288        }
1289        else
1290        {
1291            if (!loadedFields[field] || currentValue != newValue)
1292                writeField(field, new Character JavaDoc(newValue));
1293        }
1294    }
1295
1296
1297    /**
1298     * This method is called by the associated PersistenceCapable when the
1299     * corresponding mutator method (setXXX()) is called on the PersistenceCapable.
1300     *
1301     * @param pc the calling PersistenceCapable instance
1302     * @param field the field number
1303     * @param currentValue the current value of the field
1304     * @param newValue the new value for the field
1305     */

1306
1307    public void setDoubleField(PersistenceCapable pc, int field, double currentValue, double newValue)
1308    {
1309        if (pc != myPC)
1310        {
1311            replaceField(pc, field, new Double JavaDoc(newValue));
1312            disconnectClone(pc);
1313        }
1314        else
1315        {
1316            if (!loadedFields[field] || currentValue != newValue)
1317                writeField(field, new Double JavaDoc(newValue));
1318        }
1319    }
1320
1321
1322    /**
1323     * This method is called by the associated PersistenceCapable when the
1324     * corresponding mutator method (setXXX()) is called on the PersistenceCapable.
1325     *
1326     * @param pc the calling PersistenceCapable instance
1327     * @param field the field number
1328     * @param currentValue the current value of the field
1329     * @param newValue the new value for the field
1330     */

1331
1332    public void setFloatField(PersistenceCapable pc, int field, float currentValue, float newValue)
1333    {
1334        if (pc != myPC)
1335        {
1336            replaceField(pc, field, new Float JavaDoc(newValue));
1337            disconnectClone(pc);
1338        }
1339        else
1340        {
1341            if (!loadedFields[field] || currentValue != newValue)
1342                writeField(field, new Float JavaDoc(newValue));
1343        }
1344    }
1345
1346
1347    /**
1348     * This method is called by the associated PersistenceCapable when the
1349     * corresponding mutator method (setXXX()) is called on the PersistenceCapable.
1350     *
1351     * @param pc the calling PersistenceCapable instance
1352     * @param field the field number
1353     * @param currentValue the current value of the field
1354     * @param newValue the new value for the field
1355     */

1356
1357    public void setIntField(PersistenceCapable pc, int field, int currentValue, int newValue)
1358    {
1359        if (pc != myPC)
1360        {
1361            replaceField(pc, field, new Integer JavaDoc(newValue));
1362            disconnectClone(pc);
1363        }
1364        else
1365        {
1366            if (!loadedFields[field] || currentValue != newValue)
1367                writeField(field, new Integer JavaDoc(newValue));
1368        }
1369    }
1370
1371
1372    /**
1373     * This method is called by the associated PersistenceCapable when the
1374     * corresponding mutator method (setXXX()) is called on the PersistenceCapable.
1375     *
1376     * @param pc the calling PersistenceCapable instance
1377     * @param field the field number
1378     * @param currentValue the current value of the field
1379     * @param newValue the new value for the field
1380     */

1381
1382    public void setLongField(PersistenceCapable pc, int field, long currentValue, long newValue)
1383    {
1384        if (pc != myPC)
1385        {
1386            replaceField(pc, field, new Long JavaDoc(newValue));
1387            disconnectClone(pc);
1388        }
1389        else
1390        {
1391            if (!loadedFields[field] || currentValue != newValue)
1392                writeField(field, new Long JavaDoc(newValue));
1393        }
1394    }
1395
1396
1397    /**
1398     * This method is called by the associated PersistenceCapable when the
1399     * corresponding mutator method (setXXX()) is called on the PersistenceCapable.
1400     *
1401     * @param pc the calling PersistenceCapable instance
1402     * @param field the field number
1403     * @param currentValue the current value of the field
1404     * @param newValue the new value for the field
1405     */

1406
1407    public void setShortField(PersistenceCapable pc, int field, short currentValue, short newValue)
1408    {
1409        if (pc != myPC)
1410        {
1411            replaceField(pc, field, new Short JavaDoc(newValue));
1412            disconnectClone(pc);
1413        }
1414        else
1415        {
1416            if (!loadedFields[field] || currentValue != newValue)
1417                writeField(field, new Short JavaDoc(newValue));
1418        }
1419    }
1420
1421
1422    /**
1423     * This method is called by the associated PersistenceCapable when the
1424     * corresponding mutator method (setXXX()) is called on the PersistenceCapable.
1425     *
1426     * @param pc the calling PersistenceCapable instance
1427     * @param field the field number
1428     * @param currentValue the current value of the field
1429     * @param newValue the new value for the field
1430     */

1431
1432    public void setStringField(PersistenceCapable pc, int field, String JavaDoc currentValue, String JavaDoc newValue)
1433    {
1434        if (pc != myPC)
1435        {
1436            replaceField(pc, field, newValue);
1437            disconnectClone(pc);
1438        }
1439        else
1440        {
1441            if (!loadedFields[field] || !equals(currentValue, newValue))
1442                writeField(field, newValue);
1443        }
1444    }
1445
1446
1447    /**
1448     * This method is called by the associated PersistenceCapable when the
1449     * corresponding mutator method (setXXX()) is called on the PersistenceCapable.
1450     *
1451     * @param pc the calling PersistenceCapable instance
1452     * @param field the field number
1453     * @param currentValue the current value of the field
1454     * @param newValue the new value for the field
1455     */

1456
1457    public void setObjectField(PersistenceCapable pc, int field, Object JavaDoc currentValue, Object JavaDoc newValue)
1458    {
1459        if (pc != myPC)
1460        {
1461            replaceField(pc, field, newValue);
1462            disconnectClone(pc);
1463        }
1464        else
1465        {
1466            if (!loadedFields[field] || currentValue != newValue)
1467                writeObjectField(field, currentValue, newValue);
1468        }
1469    }
1470
1471
1472    /**
1473     * Compares two objects for equality, where one or both of the object
1474     * references may be null.
1475     *
1476     * @return <code>true</code> if the objects are both <code>null</code> or
1477     * compare equal according to their equals() method,
1478     * <code>false</code> otherwise.
1479     */

1480
1481    private static boolean equals(Object JavaDoc o1, Object JavaDoc o2)
1482    {
1483        return o1 == null ? (o2 == null) : o1.equals(o2);
1484    }
1485
1486
1487    /**
1488     * This method is called from the associated PersistenceCapable when its
1489     * PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
1490     * to provide the value of the specified field to the StateManager.
1491     *
1492     * @param pc the calling PersistenceCapable instance
1493     * @param field the field number
1494     * @param currentValue the current value of the field
1495     */

1496
1497    public void providedBooleanField(PersistenceCapable pc, int field, boolean currentValue)
1498    {
1499        currFM.storeBooleanField(field, currentValue);
1500    }
1501
1502
1503    /**
1504     * This method is called from the associated PersistenceCapable when its
1505     * PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
1506     * to provide the value of the specified field to the StateManager.
1507     *
1508     * @param pc the calling PersistenceCapable instance
1509     * @param field the field number
1510     * @param currentValue the current value of the field
1511     */

1512
1513    public void providedByteField(PersistenceCapable pc, int field, byte currentValue)
1514    {
1515        currFM.storeByteField(field, currentValue);
1516    }
1517
1518
1519    /**
1520     * This method is called from the associated PersistenceCapable when its
1521     * PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
1522     * to provide the value of the specified field to the StateManager.
1523     *
1524     * @param pc the calling PersistenceCapable instance
1525     * @param field the field number
1526     * @param currentValue the current value of the field
1527     */

1528
1529    public void providedCharField(PersistenceCapable pc, int field, char currentValue)
1530    {
1531        currFM.storeCharField(field, currentValue);
1532    }
1533
1534
1535    /**
1536     * This method is called from the associated PersistenceCapable when its
1537     * PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
1538     * to provide the value of the specified field to the StateManager.
1539     *
1540     * @param pc the calling PersistenceCapable instance
1541     * @param field the field number
1542     * @param currentValue the current value of the field
1543     */

1544
1545    public void providedDoubleField(PersistenceCapable pc, int field, double currentValue)
1546    {
1547        currFM.storeDoubleField(field, currentValue);
1548    }
1549
1550
1551    /**
1552     * This method is called from the associated PersistenceCapable when its
1553     * PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
1554     * to provide the value of the specified field to the StateManager.
1555     *
1556     * @param pc the calling PersistenceCapable instance
1557     * @param field the field number
1558     * @param currentValue the current value of the field
1559     */

1560
1561    public void providedFloatField(PersistenceCapable pc, int field, float currentValue)
1562    {
1563        currFM.storeFloatField(field, currentValue);
1564    }
1565
1566
1567    /**
1568     * This method is called from the associated PersistenceCapable when its
1569     * PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
1570     * to provide the value of the specified field to the StateManager.
1571     *
1572     * @param pc the calling PersistenceCapable instance
1573     * @param field the field number
1574     * @param currentValue the current value of the field
1575     */

1576
1577    public void providedIntField(PersistenceCapable pc, int field, int currentValue)
1578    {
1579        currFM.storeIntField(field, currentValue);
1580    }
1581
1582
1583    /**
1584     * This method is called from the associated PersistenceCapable when its
1585     * PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
1586     * to provide the value of the specified field to the StateManager.
1587     *
1588     * @param pc the calling PersistenceCapable instance
1589     * @param field the field number
1590     * @param currentValue the current value of the field
1591     */

1592
1593    public void providedLongField(PersistenceCapable pc, int field, long currentValue)
1594    {
1595        currFM.storeLongField(field, currentValue);
1596    }
1597
1598
1599    /**
1600     * This method is called from the associated PersistenceCapable when its
1601     * PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
1602     * to provide the value of the specified field to the StateManager.
1603     *
1604     * @param pc the calling PersistenceCapable instance
1605     * @param field the field number
1606     * @param currentValue the current value of the field
1607     */

1608
1609    public void providedShortField(PersistenceCapable pc, int field, short currentValue)
1610    {
1611        currFM.storeShortField(field, currentValue);
1612    }
1613
1614
1615
1616    /**
1617     * This method is called from the associated PersistenceCapable when its
1618     * PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
1619     * to provide the value of the specified field to the StateManager.
1620     *
1621     * @param pc the calling PersistenceCapable instance
1622     * @param field the field number
1623     * @param currentValue the current value of the field
1624     */

1625
1626    public void providedStringField(PersistenceCapable pc, int field, String JavaDoc currentValue)
1627    {
1628        currFM.storeStringField(field, currentValue);
1629    }
1630
1631
1632    /**
1633     * This method is called from the associated PersistenceCapable when its
1634     * PersistenceCapable.jdoProvideFields() method is invoked. Its purpose is
1635     * to provide the value of the specified field to the StateManager.
1636     *
1637     * @param pc the calling PersistenceCapable instance
1638     * @param field the field number
1639     * @param currentValue the current value of the field
1640     */

1641
1642    public void providedObjectField(PersistenceCapable pc, int field, Object JavaDoc currentValue)
1643    {
1644        currFM.storeObjectField(field, currentValue);
1645    }
1646
1647
1648    /**
1649     * This method is invoked by the PersistenceCapable object's
1650     * jdoReplaceField() method to refresh the value of a boolean field.
1651     *
1652     * @param pc the calling PersistenceCapable instance
1653     * @param field the field number
1654     * @return the new value for the field
1655     */

1656
1657    public boolean replacingBooleanField(PersistenceCapable pc, int field)
1658    {
1659        boolean value = currFM.fetchBooleanField(field);
1660        loadedFields[field] = true;
1661
1662        return value;
1663    }
1664
1665
1666    /**
1667     * This method is invoked by the PersistenceCapable object's
1668     * jdoReplaceField() method to refresh the value of a byte field.
1669     *
1670     * @param obj the calling PersistenceCapable instance
1671     * @param field the field number
1672     * @return the new value for the field
1673     */

1674
1675    public byte replacingByteField(PersistenceCapable obj, int field)
1676    {
1677        byte value = currFM.fetchByteField(field);
1678        loadedFields[field] = true;
1679
1680        return value;
1681    }
1682
1683
1684    /**
1685     * This method is invoked by the PersistenceCapable object's
1686     * jdoReplaceField() method to refresh the value of a char field.
1687     *
1688     * @param obj the calling PersistenceCapable instance
1689     * @param field the field number
1690     * @return the new value for the field
1691     */

1692
1693    public char replacingCharField(PersistenceCapable obj, int field)
1694    {
1695        char value = currFM.fetchCharField(field);
1696        loadedFields[field] = true;
1697
1698        return value;
1699    }
1700
1701
1702    /**
1703     * This method is invoked by the PersistenceCapable object's
1704     * jdoReplaceField() method to refresh the value of a double field.
1705     *
1706     * @param obj the calling PersistenceCapable instance
1707     * @param field the field number
1708     * @return the new value for the field
1709     */

1710
1711    public double replacingDoubleField(PersistenceCapable obj, int field)
1712    {
1713        double value = currFM.fetchDoubleField(field);
1714        loadedFields[field] = true;
1715
1716        return value;
1717    }
1718
1719
1720    /**
1721     * This method is invoked by the PersistenceCapable object's
1722     * jdoReplaceField() method to refresh the value of a float field.
1723     *
1724     * @param obj the calling PersistenceCapable instance
1725     * @param field the field number
1726     * @return the new value for the field
1727     */

1728
1729    public float replacingFloatField(PersistenceCapable obj, int field)
1730    {
1731        float value = currFM.fetchFloatField(field);
1732        loadedFields[field] = true;
1733
1734        return value;
1735    }
1736
1737
1738    /**
1739     * This method is invoked by the PersistenceCapable object's
1740     * jdoReplaceField() method to refresh the value of a int field.
1741     *
1742     * @param obj the calling PersistenceCapable instance
1743     * @param field the field number
1744     * @return the new value for the field
1745     */

1746
1747    public int replacingIntField(PersistenceCapable obj, int field)
1748    {
1749        int value = currFM.fetchIntField(field);
1750        loadedFields[field] = true;
1751
1752        return value;
1753    }
1754
1755
1756    /**
1757     * This method is invoked by the PersistenceCapable object's
1758     * jdoReplaceField() method to refresh the value of a long field.
1759     *
1760     * @param obj the calling PersistenceCapable instance
1761     * @param field the field number
1762     * @return the new value for the field
1763     */

1764
1765    public long replacingLongField(PersistenceCapable obj, int field)
1766    {
1767        long value = currFM.fetchLongField(field);
1768        loadedFields[field] = true;
1769
1770        return value;
1771    }
1772
1773
1774    /**
1775     * This method is invoked by the PersistenceCapable object's
1776     * jdoReplaceField() method to refresh the value of a short field.
1777     *
1778     * @param obj the calling PersistenceCapable instance
1779     * @param field the field number
1780     * @return the new value for the field
1781     */

1782
1783    public short replacingShortField(PersistenceCapable obj, int field)
1784    {
1785        short value = currFM.fetchShortField(field);
1786        loadedFields[field] = true;
1787
1788        return value;
1789    }
1790
1791
1792    /**
1793     * This method is invoked by the PersistenceCapable object's
1794     * jdoReplaceField() method to refresh the value of a String field.
1795     *
1796     * @param obj the calling PersistenceCapable instance
1797     * @param field the field number
1798     * @return the new value for the field
1799     */

1800
1801    public String JavaDoc replacingStringField(PersistenceCapable obj, int field)
1802    {
1803        String JavaDoc value = currFM.fetchStringField(field);
1804        loadedFields[field] = true;
1805
1806        return value;
1807    }
1808
1809
1810    /**
1811     * This method is invoked by the PersistenceCapable object's
1812     * jdoReplaceField() method to refresh the value of an Object field.
1813     *
1814     * @param obj the calling PersistenceCapable instance
1815     * @param field the field number
1816     * @return the new value for the field
1817     */

1818
1819    public Object JavaDoc replacingObjectField(PersistenceCapable obj, int field)
1820    {
1821        Object JavaDoc value = currFM.fetchObjectField(field);
1822        loadedFields[field] = true;
1823
1824        return value;
1825    }
1826
1827
1828    public PersistenceCapable getObject()
1829    {
1830        return myPC;
1831    }
1832
1833
1834    private synchronized Object JavaDoc provideField(PersistenceCapable pc, int fieldNumber)
1835    {
1836        Object JavaDoc obj;
1837
1838        FieldManager prevFM = currFM;
1839        currFM = new StateFieldManager();
1840
1841        try
1842        {
1843            pc.jdoProvideField(fieldNumber);
1844            return currFM.fetchObjectField(fieldNumber);
1845        }
1846        finally
1847        {
1848            currFM = prevFM;
1849        }
1850    }
1851
1852
1853    private synchronized void replaceField(PersistenceCapable pc, int fieldNumber, final Object JavaDoc value)
1854    {
1855        FieldManager prevFM = currFM;
1856        currFM = new GenericFieldManager()
1857        {
1858            public Object JavaDoc fetchObjectField(int field)
1859            {
1860                return value;
1861            }
1862
1863            public void storeObjectField(int field, Object JavaDoc value) {} // not possible
1864
};
1865
1866        try
1867        {
1868            pc.jdoReplaceField(fieldNumber);
1869        }
1870        finally
1871        {
1872            currFM = prevFM;
1873        }
1874    }
1875
1876
1877    /**
1878     * Called from the StoreManager after StoreManager.update() is called to obtain
1879     * updated values from the PersistenceCapable associated with this StateManager.
1880     *
1881     * @param fieldNumbers
1882     * An array of field numbers to be updated by the Store
1883     * @param fm
1884     * The updated values are stored in this object. This object is only valid
1885     * for the duration of this call.
1886     */

1887
1888    public synchronized void provideFields(int fieldNumbers[], FieldManager fm)
1889    {
1890        FieldManager prevFM = currFM;
1891        currFM = fm;
1892
1893        try
1894        {
1895            myPC.jdoProvideFields(fieldNumbers);
1896        }
1897        finally
1898        {
1899            currFM = prevFM;
1900        }
1901    }
1902
1903
1904    /**
1905     * Called to refresh data in the PersistenceCapable object associated with
1906     * this StateManager.
1907     * <p>
1908     * Unwrapped values for SCO fields are wrapped with an appropriate SCO
1909     * wrapper object.
1910     *
1911     * @param fieldNumbers
1912     * An array of field numbers to be refreshed.
1913     * @param fm
1914     * Provides the updated values. This object is only used for the
1915     * duration of the call.
1916     */

1917
1918    public synchronized void replaceFields(int fieldNumbers[], FieldManager fm)
1919    {
1920        replaceFieldsInternal(fieldNumbers, new SCOWrapper(fm, this, secondClassMutableFields));
1921    }
1922
1923
1924    private synchronized void replaceFieldsInternal(int fieldNumbers[], FieldManager fm)
1925    {
1926        FieldManager prevFM = currFM;
1927        currFM = fm;
1928
1929        try
1930        {
1931            myPC.jdoReplaceFields(fieldNumbers);
1932        }
1933        finally
1934        {
1935            currFM = prevFM;
1936        }
1937    }
1938
1939
1940    private void preStateChange()
1941    {
1942        changingState = true;
1943    }
1944
1945
1946    private void postStateChange()
1947    {
1948        changingState = false;
1949
1950        if (postLoadPending)
1951        {
1952            postLoadPending = false;
1953            postLoad();
1954        }
1955    }
1956
1957
1958    /**
1959     * Makes the instance persistent.
1960     * If the object is already persistent, this method does nothing.
1961     * Otherwise, a new object ID for the instance is obtained from the store
1962     * manager and the object is inserted in the data store.
1963     * <p>
1964     * Any failure will leave the instance in its previous life-cycle state.
1965     *
1966     * @return
1967     * <code>true</code> if the object was successfully made persistent,
1968     * <code>false</code> if the object was already persistent.
1969     */

1970
1971    public boolean makePersistent()
1972    {
1973        if (myLC.isPersistent())
1974            return false;
1975
1976        LifeCycleState oldLC = myLC;
1977        preStateChange();
1978        try { myLC = myLC.transitionMakePersistent(this, myTX); }
1979        finally { postStateChange(); }
1980
1981        boolean succeeded = false;
1982
1983        try
1984        {
1985            myID = srm.newObjectID(myPC.getClass());
1986
1987            if (inserting)
1988                throw new JDOFatalInternalException("makePersistent() called recursively");
1989
1990            inserting = true;
1991
1992            try
1993            {
1994                if (myPC instanceof InstanceCallbacks)
1995                    ((InstanceCallbacks)myPC).jdoPreStore();
1996
1997                srm.insert(this);
1998            }
1999            finally
2000            {
2001                inserting = false;
2002            }
2003
2004            succeeded = true;
2005        }
2006        finally
2007        {
2008            if (!succeeded)
2009            {
2010                preStateChange();
2011                try { myLC = myLC.transitionRollbackState(this, oldLC); }
2012                finally { postStateChange(); }
2013
2014                myID = null;
2015            }
2016        }
2017
2018        myPM.dataStoreModified();
2019
2020        return true;
2021    }
2022
2023
2024    private void transitionReadField()
2025    {
2026        preStateChange();
2027        try { myLC = myLC.transitionReadField(this, myTX); }
2028        finally { postStateChange(); }
2029    }
2030
2031
2032    private void transitionWriteField()
2033    {
2034        preStateChange();
2035        try { myLC = myLC.transitionWriteField(this, myTX); }
2036        finally { postStateChange(); }
2037    }
2038
2039
2040    public void makeTransactional()
2041    {
2042        preStateChange();
2043        try { myLC = myLC.transitionMakeTransactional(this, myTX); }
2044        finally { postStateChange(); }
2045    }
2046
2047
2048    public void makeNontransactional()
2049    {
2050        preStateChange();
2051        try { myLC = myLC.transitionMakeNontransactional(this); }
2052        finally { postStateChange(); }
2053    }
2054
2055
2056    public void makeTransient()
2057    {
2058        preStateChange();
2059        try { myLC = myLC.transitionMakeTransient(this); }
2060        finally { postStateChange(); }
2061    }
2062
2063
2064    public void evict()
2065    {
2066        preStateChange();
2067        try { myLC = myLC.transitionEvict(this); }
2068        finally { postStateChange(); }
2069    }
2070
2071
2072    public void refresh()
2073    {
2074        preStateChange();
2075        try { myLC = myLC.transitionRefresh(this, myTX); }
2076        finally { postStateChange(); }
2077    }
2078
2079
2080    public void retrieve(boolean DFGOnly)
2081    {
2082        preStateChange();
2083        try { myLC = myLC.transitionRetrieve(this, myTX, DFGOnly); }
2084        finally { postStateChange(); }
2085    }
2086
2087
2088    /**
2089     * This method is invoked when a commit is performed in a Transaction
2090     * involving the PersistenceCapable managed by this StateManager
2091     */

2092
2093    public void postCommit()
2094    {
2095        preStateChange();
2096        try { myLC = myLC.transitionCommit(this, myTX); }
2097        finally { postStateChange(); }
2098    }
2099
2100
2101    /**
2102     * This method is invoked when a rollback is performed in a Transaction
2103     * involving the PersistenceCapable managed by this StateManager.
2104     */

2105
2106    public void preRollback()
2107    {
2108        preStateChange();
2109        try { myLC = myLC.transitionRollback(this, myTX); }
2110        finally { postStateChange(); }
2111    }
2112
2113
2114    void preDelete()
2115    {
2116        if (myPC instanceof InstanceCallbacks)
2117            ((InstanceCallbacks)myPC).jdoPreDelete();
2118    }
2119
2120
2121    /**
2122     * Deletes the instance.
2123     * If the object is already deleted, this method does nothing.
2124     * Otherwise, the object is removed from the data store.
2125     * <p>
2126     * Any failure will leave the instance in its previous life-cycle state.
2127     */

2128
2129    public void deletePersistent()
2130    {
2131        if (myLC.isDeleted())
2132            return;
2133
2134        if (deleting)
2135            throw new JDOFatalInternalException("deletePersistent() called recursively");
2136
2137        deleting = true;
2138
2139        try
2140        {
2141            LifeCycleState oldLC = myLC;
2142            preStateChange();
2143            try { myLC = myLC.transitionDeletePersistent(this, myTX); }
2144            finally { postStateChange(); }
2145
2146            boolean succeeded = false;
2147
2148            try
2149            {
2150                srm.delete(this);
2151                clearLoadedFlags();
2152
2153                succeeded = true;
2154            }
2155            finally
2156            {
2157                if (!succeeded)
2158                {
2159                    preStateChange();
2160                    try { myLC = myLC.transitionRollbackState(this, oldLC); }
2161                    finally { postStateChange(); }
2162                }
2163            }
2164        }
2165        finally
2166        {
2167            deleting = false;
2168        }
2169
2170        myPM.dataStoreModified();
2171    }
2172
2173
2174    /**
2175     * Flushes any dirty fields to the data store.
2176     *
2177     * <p>Note that "dirty" in this case is not equated to being in the P_DIRTY
2178     * state. The P_DIRTY state means that at least one field in the object has
2179     * been written by the user during the current transaction, whereas for the
2180     * purposes of this method, a field is "dirty" if it's been written by the
2181     * user but not yet updated in the data store. The difference is, it's
2182     * possible for an object's state to be P_DIRTY, yet have no "dirty" fields
2183     * because flush() has been called at least once during the transaction.
2184     */

2185
2186    public void flush()
2187    {
2188        if (dirty)
2189        {
2190            if (flushing)
2191                throw new JDOFatalInternalException("flush() re-entered");
2192
2193            flushing = true;
2194
2195            try
2196            {
2197                if (myPC instanceof InstanceCallbacks && !deleting)
2198                    ((InstanceCallbacks)myPC).jdoPreStore();
2199
2200                int[] dirtyFieldNumbers = getFlagsSetTo(dirtyFields, true);
2201
2202                if (dirtyFieldNumbers == null)
2203                    throw new JDOFatalInternalException("dirty == true but no fields are marked dirty");
2204
2205                srm.update(this, dirtyFieldNumbers);
2206
2207                clearDirtyFlags();
2208            }
2209            finally
2210            {
2211                flushing = false;
2212            }
2213
2214            myPM.dataStoreModified();
2215        }
2216    }
2217
2218
2219    void disconnect()
2220    {
2221        if (LOG.isDebugEnabled())
2222            LOG.debug("Disconnecting " + toJVMIDString(myPC) + " from " + this);
2223
2224        disownSCOFields();
2225        myPM.removeStateManager(this);
2226        jdoFlags = PersistenceCapable.READ_WRITE_OK;
2227        myPC.jdoReplaceFlags();
2228
2229        disconnecting = true;
2230
2231        try
2232        {
2233            myPC.jdoReplaceStateManager(null);
2234        }
2235        finally
2236        {
2237            disconnecting = false;
2238        }
2239
2240        discardSavedFields();
2241        myPM = null;
2242        myPC = null;
2243        myID = null;
2244        myLC = null;
2245        cmd = null;
2246        srm = null;
2247    }
2248
2249
2250    public String JavaDoc toString()
2251    {
2252        if (myPC == null)
2253            return "StateManager@" + System.identityHashCode(this) + "(disconnected)";
2254        else
2255        {
2256            String JavaDoc pcClassName = myPC.getClass().getName();
2257
2258            return "StateManager:" + pcClassName.substring(pcClassName.lastIndexOf('.') + 1)
2259                 + '@' + System.identityHashCode(myPC) + '(' + myID + ')';
2260        }
2261    }
2262
2263
2264    /* ------------------------ debugging helper methods -------------------- */
2265
2266
2267    private static String JavaDoc toJVMIDString(Object JavaDoc obj)
2268    {
2269        if (obj == null)
2270            return "null";
2271        else
2272            return obj.getClass().getName() + '@' + System.identityHashCode(obj);
2273    }
2274
2275
2276    private static String JavaDoc booleanArrayToString(boolean[] ba)
2277    {
2278        if (ba == null)
2279            return "null";
2280
2281        StringBuffer JavaDoc sb = new StringBuffer JavaDoc("[");
2282
2283        for (int i = 0; i < ba.length; ++i)
2284            sb.append(ba[i] ? 'Y' : 'N');
2285
2286        sb.append(']');
2287
2288        return sb.toString();
2289    }
2290
2291
2292    private static String JavaDoc intArrayToString(int[] ia)
2293    {
2294        if (ia == null)
2295            return "null";
2296
2297        StringBuffer JavaDoc sb = new StringBuffer JavaDoc("[");
2298
2299        for (int i = 0; i < ia.length; ++i)
2300        {
2301            if (i > 0)
2302                sb.append(',');
2303
2304            sb.append(ia[i]);
2305        }
2306
2307        sb.append(']');
2308
2309        return sb.toString();
2310    }
2311
2312
2313    private static String JavaDoc jdoFlagsToString(byte flags)
2314    {
2315        switch (flags)
2316        {
2317            case PersistenceCapable.LOAD_REQUIRED:
2318                return "LOAD_REQUIRED";
2319            case PersistenceCapable.READ_OK:
2320                return "READ_OK";
2321            case PersistenceCapable.READ_WRITE_OK:
2322                return "READ_WRITE_OK";
2323            default:
2324                return "???";
2325        }
2326    }
2327
2328
2329    private static void dumpPC(PersistenceCapable pc, PrintWriter JavaDoc out)
2330    {
2331        out.println(toJVMIDString(pc));
2332
2333        if (pc == null)
2334            return;
2335
2336        out.print("jdoStateManager = "); out.println(peekField(pc, "jdoStateManager"));
2337        out.print("jdoFlags = ");
2338        Object JavaDoc flagsObj = peekField(pc, "jdoFlags");
2339
2340        if (flagsObj instanceof Byte JavaDoc)
2341            out.println(jdoFlagsToString(((Byte JavaDoc)flagsObj).byteValue()));
2342        else
2343            out.println(flagsObj);
2344
2345        Class JavaDoc c = pc.getClass();
2346
2347        do
2348        {
2349            String JavaDoc[] fieldNames = helper.getFieldNames(c);
2350
2351            for (int i = 0; i < fieldNames.length; ++i)
2352            {
2353                out.print(fieldNames[i]); out.print(" = "); out.println(peekField(pc, fieldNames[i]));
2354            }
2355
2356            c = c.getSuperclass();
2357        } while (c != null && PersistenceCapable.class.isAssignableFrom(c));
2358    }
2359
2360
2361    private static Object JavaDoc peekField(Object JavaDoc obj, String JavaDoc fieldName)
2362    {
2363        try
2364        {
2365            /*
2366             * This doesn't work due to security problems but you get the idea.
2367             * I'm trying to get field values directly without going through
2368             * the provideField machinery.
2369             */

2370            Object JavaDoc value = obj.getClass().getDeclaredField(fieldName).get(obj);
2371
2372            if (value instanceof PersistenceCapable)
2373                return toJVMIDString(value);
2374            else
2375                return value;
2376        }
2377        catch (Exception JavaDoc e)
2378        {
2379            return e.toString();
2380        }
2381    }
2382
2383
2384    public void dump(PrintWriter JavaDoc out)
2385    {
2386        out.print("myPM = "); out.println(myPM);
2387        out.print("myTX = "); out.println(myTX);
2388        out.print("myID = "); out.println(myID);
2389        out.print("myLC = "); out.println(myLC);
2390        out.print("cmd = "); out.println(cmd);
2391        out.print("srm = "); out.println(srm);
2392        out.print("fieldCount = "); out.println(fieldCount);
2393        out.print("dirty = "); out.println(dirty);
2394        out.print("flushing = "); out.println(flushing);
2395        out.print("changingState = "); out.println(changingState);
2396        out.print("postLoadPending = "); out.println(postLoadPending);
2397        out.print("disconnecting = "); out.println(disconnecting);
2398        out.print("dirtyFields = "); out.println(booleanArrayToString(dirtyFields));
2399        out.print("defaultFetchGroupFields = "); out.println(booleanArrayToString(defaultFetchGroupFields));
2400        out.print("secondClassMutableFields = "); out.println(booleanArrayToString(secondClassMutableFields));
2401        out.print("allFieldNumbers = "); out.println(intArrayToString(allFieldNumbers));
2402        out.print("persistentFieldNumbers = "); out.println(intArrayToString(persistentFieldNumbers));
2403        out.print("defaultFetchGroupFieldNumbers = "); out.println(intArrayToString(defaultFetchGroupFieldNumbers));
2404        out.print("secondClassMutableFieldNumbers = "); out.println(intArrayToString(secondClassMutableFieldNumbers));
2405
2406        out.println();
2407        out.print("jdoFlags = "); out.println(jdoFlagsToString(jdoFlags));
2408        out.print("loadedFields = "); out.println(booleanArrayToString(loadedFields));
2409        out.print("myPC = "); dumpPC(myPC, out);
2410
2411        out.println();
2412        out.print("savedFlags = "); out.println(jdoFlagsToString(savedFlags));
2413        out.print("savedLoadedFields = "); out.println(booleanArrayToString(savedLoadedFields));
2414        out.print("savedImage = "); dumpPC(savedImage, out);
2415    }
2416}
2417
Popular Tags