Home » apache-openjpa-1.1.0-source » org.apache.openjpa » kernel » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one
    3    * or more contributor license agreements.  See the NOTICE file
    4    * distributed with this work for additional information
    5    * regarding copyright ownership.  The ASF licenses this file
    6    * to you under the Apache License, Version 2.0 (the
    7    * "License"); you may not use this file except in compliance
    8    * with the License.  You may obtain a copy of the License at
    9    *
   10    * http://www.apache.org/licenses/LICENSE-2.0
   11    *
   12    * Unless required by applicable law or agreed to in writing,
   13    * software distributed under the License is distributed on an
   14    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   15    * KIND, either express or implied.  See the License for the
   16    * specific language governing permissions and limitations
   17    * under the License.    
   18    */
   19   package org.apache.openjpa.kernel;
   20   
   21   import java.io.IOException;
   22   import java.io.ObjectOutput;
   23   import java.lang.reflect.Array;
   24   import java.util.ArrayList;
   25   import java.util.BitSet;
   26   import java.util.Calendar;
   27   import java.util.Collection;
   28   import java.util.Collections;
   29   import java.util.Comparator;
   30   import java.util.Date;
   31   import java.util.Iterator;
   32   import java.util.LinkedList;
   33   import java.util.List;
   34   import java.util.Map;
   35   
   36   import org.apache.commons.collections.map.IdentityMap;
   37   import org.apache.openjpa.conf.DetachOptions;
   38   import org.apache.openjpa.enhance.PersistenceCapable;
   39   import org.apache.openjpa.event.CallbackModes;
   40   import org.apache.openjpa.event.LifecycleEvent;
   41   import org.apache.openjpa.lib.util.Localizer;
   42   import org.apache.openjpa.meta.ClassMetaData;
   43   import org.apache.openjpa.meta.FieldMetaData;
   44   import org.apache.openjpa.meta.JavaTypes;
   45   import org.apache.openjpa.util.CallbackException;
   46   import org.apache.openjpa.util.ObjectNotFoundException;
   47   import org.apache.openjpa.util.Proxy;
   48   import org.apache.openjpa.util.ProxyManager;
   49   import org.apache.openjpa.util.UserException;
   50   
   51   /**
   52    * Handles detaching instances.
   53    *
   54    * @author Marc Prud'hommeaux
   55    * @nojavadoc
   56    */
   57   public class DetachManager
   58       implements DetachState {
   59   
   60       private static Localizer _loc = Localizer.forPackage(DetachManager.class);
   61   
   62       private final BrokerImpl _broker;
   63       private final boolean _copy;
   64       private final boolean _full;
   65       private final ProxyManager _proxy;
   66       private final DetachOptions _opts;
   67       private final OpCallbacks _call;
   68       private final boolean _failFast;
   69       private boolean _flushed = false;
   70       private boolean _flushBeforeDetach;
   71   
   72       // if we're not detaching full, we need to track all detached objects;
   73       // if we are, then we use a special field manager for more efficient
   74       // detachment than the standard one
   75       private final IdentityMap _detached;
   76       private final DetachFieldManager _fullFM;
   77   
   78       /**
   79        * Used to prepare a detachable instance that does not externalize
   80        * detached state.
   81        */
   82       static boolean preSerialize(StateManagerImpl sm) {
   83           if (!sm.isPersistent())
   84               return false;
   85   
   86           if (sm.getBroker().getConfiguration().getCompatibilityInstance()
   87                   .getFlushBeforeDetach()) {
   88               flushDirty(sm);
   89           }
   90   
   91           ClassMetaData meta = sm.getMetaData();
   92           boolean setState = meta.getDetachedState() != null
   93               && !ClassMetaData.SYNTHETIC.equals(meta.getDetachedState());
   94           BitSet idxs = (setState) ? new BitSet(meta.getFields().length) : null;
   95           preDetach(sm.getBroker(), sm, idxs);
   96   
   97           if (setState) {
   98               sm.getPersistenceCapable().pcSetDetachedState(getDetachedState
   99                   (sm, idxs));
  100               return false; // don't null state
  101           }
  102           return true;
  103       }
  104   
  105       /**
  106        * Used by classes that externalize detached state.
  107        *
  108        * @return whether to use a detached state manager
  109        */
  110       static boolean writeDetachedState(StateManagerImpl sm, ObjectOutput out,
  111           BitSet idxs)
  112           throws IOException {
  113           if (!sm.isPersistent()) {
  114               out.writeObject(null); // state
  115               out.writeObject(null); // sm
  116               return false;
  117           }
  118   
  119           // dirty state causes flush
  120           flushDirty(sm);
  121   
  122           Broker broker = sm.getBroker();
  123           preDetach(broker, sm, idxs);
  124   
  125           // write detached state object and state manager
  126           DetachOptions opts = broker.getConfiguration().
  127               getDetachStateInstance();
  128           if (!opts.getDetachedStateManager()
  129               || !useDetachedStateManager(sm, opts)) {
  130               out.writeObject(getDetachedState(sm, idxs));
  131               out.writeObject(null);
  132               return false;
  133           }
  134           out.writeObject(null);
  135           out.writeObject(new DetachedStateManager(sm.getPersistenceCapable(),
  136               sm, idxs, opts.getAccessUnloaded(), broker.getMultithreaded()));
  137           return true;
  138       }
  139   
  140       /**
  141        * Ready the object for detachment, including loading the fields to be
  142        * detached and updating version information.
  143        *
  144        * @param idxs the indexes of fields to detach will be set as a side
  145        * effect of this method
  146        */
  147       private static void preDetach(Broker broker, StateManagerImpl sm,
  148           BitSet idxs) {
  149           // make sure the existing object has the right fields fetched; call
  150           // even if using currently-loaded fields for detach to make sure
  151           // version is set
  152           int detachMode = broker.getDetachState();
  153           int loadMode = StateManagerImpl.LOAD_FGS;
  154           BitSet exclude = null;
  155           if (detachMode == DETACH_LOADED)
  156               exclude = StoreContext.EXCLUDE_ALL;
  157           else if (detachMode == DETACH_ALL)
  158               loadMode = StateManagerImpl.LOAD_ALL;
  159           try {
  160               sm.load(broker.getFetchConfiguration(), loadMode, exclude, null, 
  161                   false);
  162           } catch (ObjectNotFoundException onfe) {
  163               // consume the exception
  164           }
  165   
  166           // create bitset of fields to detach; if mode is all we can use
  167           // currently loaded bitset clone, since we know all fields are loaded
  168           if (idxs != null) {
  169               if (detachMode == DETACH_FETCH_GROUPS)
  170                   setFetchGroupFields(broker, sm, idxs);
  171               else
  172                   idxs.or(sm.getLoaded());
  173   
  174               // clear lrs fields
  175               FieldMetaData[] fmds = sm.getMetaData().getFields();
  176               for (int i = 0; i < fmds.length; i++)
  177                   if (fmds[i].isLRS())
  178                       idxs.clear(i);
  179           }
  180       }
  181   
  182       /**
  183        * Generate the detached state for the given instance.
  184        */
  185       private static Object getDetachedState(StateManagerImpl sm, BitSet fields) {
  186           // if datastore, store id in first element
  187           int offset = (sm.getMetaData().getIdentityType() ==
  188               ClassMetaData.ID_DATASTORE) ? 1 : 0;
  189   
  190           // make version state array one larger for new instances; marks new
  191           // instances without affecting serialization size much
  192           Object[] state;
  193           if (sm.isNew())
  194               state = new Object[3 + offset];
  195           else
  196               state = new Object[2 + offset];
  197   
  198           if (offset > 0) {
  199               Object id;
  200               if (sm.isEmbedded() || sm.getObjectId() == null)
  201                   id = sm.getId();
  202               else
  203                   id = sm.getObjectId();
  204               state[0] = id.toString();
  205           }
  206           state[offset] = sm.getVersion();
  207           state[offset + 1] = fields;
  208           return state;
  209       }
  210   
  211       /**
  212        * Flush or invoke pre-store callbacks on the given broker if
  213        * needed. Return true if flushed/stored, false otherwise.
  214        */
  215       private static boolean flushDirty(StateManagerImpl sm) {
  216           if (!sm.isDirty() || !sm.getBroker().isActive())
  217               return false;
  218   
  219           // only flush if there are actually any dirty non-flushed fields
  220           BitSet dirtyFields = sm.getDirty();
  221           BitSet flushedFields = sm.getFlushed();
  222           for (int i = 0; i < dirtyFields.size(); i++) {
  223               if (dirtyFields.get(i) && !flushedFields.get(i)) {
  224                   if (sm.getBroker().getRollbackOnly())
  225                       sm.getBroker().preFlush();
  226                   else
  227                       sm.getBroker().flush();
  228                   return true;
  229               }
  230           }
  231           return false;
  232       }
  233   
  234       /**
  235        * Create a bit set for the fields in the current fetch groups.
  236        */
  237       private static void setFetchGroupFields(Broker broker,
  238           StateManagerImpl sm, BitSet idxs) {
  239           FetchConfiguration fetch = broker.getFetchConfiguration();
  240           FieldMetaData[] fmds = sm.getMetaData().getFields();
  241           for (int i = 0; i < fmds.length; i++) {
  242               if (fmds[i].isPrimaryKey() || fetch.requiresFetch(fmds[i])
  243                   != FetchConfiguration.FETCH_NONE)
  244                   idxs.set(i);
  245           }
  246       }
  247   
  248       /**
  249        * Constructor.
  250        *
  251        * @param broker owning broker
  252        * @param full whether the entire broker cache is being detached; if
  253        * this is the case, we assume the broker has already
  254        * flushed if needed, and that we're detaching in-place
  255        */
  256       public DetachManager(BrokerImpl broker, boolean full, OpCallbacks call) {
  257           _broker = broker;
  258           _proxy = broker.getConfiguration().getProxyManagerInstance();
  259           _opts = broker.getConfiguration().getDetachStateInstance();
  260           _copy = !full;
  261           _flushed = full;
  262           _call = call;
  263           _failFast = (broker.getConfiguration().getMetaDataRepositoryInstance().
  264               getMetaDataFactory().getDefaults().getCallbackMode()
  265               & CallbackModes.CALLBACK_FAIL_FAST) != 0;
  266   
  267           // we can only rely on our "all" shortcuts if we know we won't be
  268           // loading any more data
  269           _full = full && broker.getDetachState() == DetachState.DETACH_LOADED;
  270           if (_full) {
  271               _detached = null;
  272               _fullFM = new DetachFieldManager();
  273           } else {
  274               _detached = new IdentityMap();
  275               _fullFM = null;
  276           }
  277           _flushBeforeDetach =
  278                   broker.getConfiguration().getCompatibilityInstance()
  279                           .getFlushBeforeDetach();
  280       }
  281   
  282       /**
  283        * Return a detached version of the given instance.
  284        */
  285       public Object detach(Object toDetach) {
  286           List exceps = null;
  287           try {
  288               return detachInternal(toDetach);
  289           } catch (CallbackException ce) {
  290               exceps = new ArrayList(1);
  291               exceps.add(ce);
  292               return null; // won't be reached as exception will be rethrown
  293           } finally {
  294               if (exceps == null || !_failFast)
  295                   exceps = invokeAfterDetach(Collections.singleton(toDetach),
  296                       exceps);
  297               if (_detached != null)
  298                   _detached.clear();
  299               throwExceptions(exceps);
  300           }
  301       }
  302   
  303       /**
  304        * Return detached versions of all the given instances. If not copying,
  305        * null will be returned.
  306        */
  307       public Object[] detachAll(Collection instances) {
  308           List exceps = null;
  309           List detached = null;
  310           if (_copy)
  311               detached = new ArrayList(instances.size());
  312   
  313           boolean failFast = false;
  314           try {
  315               Object detach;
  316               for (Iterator itr = instances.iterator(); itr.hasNext();) {
  317                   detach = detachInternal(itr.next());
  318                   if (_copy)
  319                       detached.add(detach);
  320               }
  321           }
  322           catch (RuntimeException re) {
  323               if (re instanceof CallbackException && _failFast)
  324                   failFast = true;
  325               exceps = add(exceps, re);
  326           } finally {
  327               if (!failFast)
  328                   exceps = invokeAfterDetach(instances, exceps);
  329               if (_detached != null)
  330                   _detached.clear();
  331           }
  332           throwExceptions(exceps);
  333   
  334           if (_copy)
  335               return detached.toArray();
  336           return null;
  337       }
  338   
  339       /**
  340        * Invoke postDetach() on any detached instances that implement
  341        * PostDetachCallback. This will be done after the entire graph has
  342        * been detached. This method has the side-effect of also clearing
  343        * out the map of all detached instances.
  344        */
  345       private List invokeAfterDetach(Collection objs, List exceps) {
  346           Iterator itr = (_full) ? objs.iterator()
  347               : _detached.entrySet().iterator();
  348   
  349           Object orig, detached;
  350           Map.Entry entry;
  351           while (itr.hasNext()) {
  352               if (_full) {
  353                   orig = itr.next();
  354                   detached = orig;
  355               } else {
  356                   entry = (Map.Entry) itr.next();
  357                   orig = entry.getKey();
  358                   detached = entry.getValue();
  359               }
  360   
  361               StateManagerImpl sm = _broker.getStateManagerImpl(orig, true);
  362               try {
  363                   if (sm != null)
  364                       _broker.fireLifecycleEvent(detached, orig,
  365                           sm.getMetaData(), LifecycleEvent.AFTER_DETACH);
  366               } catch (CallbackException ce) {
  367                   exceps = add(exceps, ce);
  368                   if (_failFast)
  369                       break; // don't continue processing
  370               }
  371           }
  372           return exceps;
  373       }
  374   
  375       /**
  376        * Add an exception to the list.
  377        */
  378       private List add(List exceps, RuntimeException re) {
  379           if (exceps == null)
  380               exceps = new LinkedList();
  381           exceps.add(re);
  382           return exceps;
  383       }
  384   
  385       /**
  386        * Throw all gathered exceptions.
  387        */
  388       private void throwExceptions(List exceps) {
  389           if (exceps == null)
  390               return;
  391   
  392           if (exceps.size() == 1)
  393               throw (RuntimeException) exceps.get(0);
  394           throw new UserException(_loc.get("nested-exceps")).
  395               setNestedThrowables((Throwable[]) exceps.toArray
  396                   (new Throwable[exceps.size()]));
  397       }
  398   
  399       /**
  400        * Detach.
  401        */
  402       private Object detachInternal(Object toDetach) {
  403           if (toDetach == null)
  404               return null;
  405   
  406           // already detached?
  407           if (_detached != null) {
  408               Object detached = _detached.get(toDetach);
  409               if (detached != null)
  410                   return detached;
  411           }
  412   
  413           StateManagerImpl sm = _broker.getStateManagerImpl(toDetach, true);
  414           if (_call != null && (_call.processArgument(OpCallbacks.OP_DETACH,
  415               toDetach, sm) & OpCallbacks.ACT_RUN) == 0)
  416               return toDetach;
  417           if (sm == null)
  418               return toDetach;
  419   
  420           // Call PreDetach first as we can't tell if the new system
  421           // fired an event or just did not fail.
  422           _broker.fireLifecycleEvent(toDetach, null, sm.getMetaData(),
  423               LifecycleEvent.BEFORE_DETACH);
  424   
  425           if(! _flushed)  {
  426               if(_flushBeforeDetach) {
  427                   // any dirty instances cause a flush to occur
  428                   flushDirty(sm);
  429               }
  430               _flushed = true;
  431           }
  432           
  433           BitSet fields = new BitSet();
  434           preDetach(_broker, sm, fields);
  435   
  436           // create and store new object before copy to avoid endless recursion
  437           PersistenceCapable pc = sm.getPersistenceCapable();
  438           PersistenceCapable detachedPC;
  439           if (_copy)
  440               detachedPC = pc.pcNewInstance(null, true);
  441           else
  442               detachedPC = pc;
  443           if (_detached != null)
  444               _detached.put(toDetach, detachedPC);
  445   
  446           // detach fields and set detached variables
  447           DetachedStateManager detSM = null;
  448           if (_opts.getDetachedStateManager()
  449               && useDetachedStateManager(sm, _opts))
  450               detSM = new DetachedStateManager(detachedPC, sm, fields,
  451                   _opts.getAccessUnloaded(), _broker.getMultithreaded());
  452           if (_full) {
  453               _fullFM.setStateManager(sm);
  454               _fullFM.detachVersion();
  455               _fullFM.reproxy(detSM);
  456               _fullFM.setStateManager(null);
  457           } else {
  458               InstanceDetachFieldManager fm = new InstanceDetachFieldManager
  459                   (detachedPC, detSM);
  460               fm.setStateManager(sm);
  461               fm.detachFields(fields);
  462           }
  463   
  464           if (!Boolean.FALSE.equals(sm.getMetaData().usesDetachedState()))
  465               detachedPC.pcSetDetachedState(getDetachedState(sm, fields));
  466           if (!_copy)
  467               sm.release(false, !_copy);
  468           if (detSM != null)
  469               detachedPC.pcReplaceStateManager(detSM);
  470           return detachedPC;
  471       }
  472   
  473       private static boolean useDetachedStateManager(StateManagerImpl sm,
  474           DetachOptions opts) {
  475           ClassMetaData meta = sm.getMetaData();
  476           return !Boolean.FALSE.equals(meta.usesDetachedState()) &&
  477               ClassMetaData.SYNTHETIC.equals(meta.getDetachedState()) &&
  478               opts.getDetachedStateManager();
  479       }
  480   
  481       /**
  482        * Base detach field manager.
  483        */
  484       private static class DetachFieldManager
  485           extends TransferFieldManager {
  486   
  487           protected StateManagerImpl sm;
  488   
  489           /**
  490            * Set the source state manager.
  491            */
  492           public void setStateManager(StateManagerImpl sm) {
  493               this.sm = sm;
  494           }
  495   
  496           /**
  497            * Transfer the current version object from the state manager to the
  498            * detached instance.
  499            */
  500           public void detachVersion() {
  501               FieldMetaData fmd = sm.getMetaData().getVersionField();
  502               if (fmd == null)
  503                   return;
  504   
  505               Object val = JavaTypes.convert(sm.getVersion(),
  506                   fmd.getTypeCode());
  507               val = fmd.getFieldValue(val, sm.getBroker());
  508               switch (fmd.getDeclaredTypeCode()) {
  509               case JavaTypes.LONG:
  510               case JavaTypes.SHORT:
  511               case JavaTypes.INT:
  512               case JavaTypes.BYTE:
  513                   longval = (val == null) ? 0L : ((Number) val).longValue();
  514                   break;
  515               case JavaTypes.DOUBLE:
  516               case JavaTypes.FLOAT:
  517                   dblval = (val == null) ? 0D : ((Number) val).doubleValue();
  518                   break;
  519               default:
  520                   objval = val;
  521               }
  522               sm.replaceField(getDetachedPersistenceCapable(), this,
  523                   fmd.getIndex());
  524           }
  525   
  526           /**
  527            * Unproxies second class object fields.
  528            */
  529           public void reproxy(DetachedStateManager dsm) {
  530               FieldMetaData[] fmds = sm.getMetaData().getFields();
  531               for (int i = 0; i < fmds.length; i++) {
  532                   switch (fmds[i].getDeclaredTypeCode()) {
  533                   case JavaTypes.COLLECTION:
  534                   case JavaTypes.MAP:
  535                       // lrs proxies not detached
  536                       if (fmds[i].isLRS()) {
  537                           objval = null;
  538                           sm.replaceField(getDetachedPersistenceCapable(), 
  539                               this, i);
  540                           break;
  541                       }
  542                       // no break
  543                   case JavaTypes.CALENDAR:
  544                   case JavaTypes.DATE:
  545                   case JavaTypes.OBJECT:
  546                       sm.provideField(getDetachedPersistenceCapable(), this, i);
  547                       if (objval instanceof Proxy) {
  548                           Proxy proxy = (Proxy) objval;
  549                           if (proxy.getChangeTracker() != null)
  550                               proxy.getChangeTracker().stopTracking();
  551                           proxy.setOwner(dsm, (dsm == null) ? -1 : i);
  552                       }
  553                   }
  554               }
  555               clear();
  556           }
  557   
  558           /**
  559            * Return the instance being detached.
  560            */
  561           protected PersistenceCapable getDetachedPersistenceCapable() {
  562               return sm.getPersistenceCapable();
  563           }
  564       }
  565   
  566       /**
  567        * FieldManager that can copy all the fields from one
  568        * PersistenceCapable instance to another. One of the
  569        * instances must be managed by a StateManager, and the
  570        * other must be unmanaged.
  571        *
  572        * @author Marc Prud'hommeaux
  573        */
  574       private class InstanceDetachFieldManager
  575           extends DetachFieldManager {
  576   
  577           private final PersistenceCapable _to;
  578           private final DetachedStateManager _detSM;
  579   
  580           /**
  581            * Constructor. Supply instance to to copy to.
  582            */
  583           public InstanceDetachFieldManager(PersistenceCapable to,
  584               DetachedStateManager detSM) {
  585               _to = to;
  586               _detSM = detSM;
  587           }
  588   
  589           protected PersistenceCapable getDetachedPersistenceCapable() {
  590               return _to;
  591           }
  592   
  593           /**
  594            * Detach the fields of the state manager given on construction to
  595            * the persistence capable given on construction.
  596            * Only the fields in the given bit set will be copied.
  597            */
  598           public void detachFields(BitSet fgfields) {
  599               PersistenceCapable from = sm.getPersistenceCapable();
  600               FieldMetaData[] pks = sm.getMetaData().getPrimaryKeyFields();
  601               FieldMetaData[] fmds = sm.getMetaData().getFields();
  602   
  603               if (_copy)
  604                   _to.pcReplaceStateManager(sm);
  605               try {
  606                   // we start with pk fields: objects might rely on pk fields for
  607                   // equals and hashCode methods, and this ensures that pk fields
  608                   // are set properly if we return any partially-detached objects
  609                   // due to reentrant calls when traversing relations
  610                   for (int i = 0; i < pks.length; i++)
  611                       detachField(from, pks[i].getIndex(), true);
  612                   detachVersion();
  613                   for (int i = 0; i < fmds.length; i++)
  614                       if (!fmds[i].isPrimaryKey() && !fmds[i].isVersion()) 
  615                           detachField(from, i, fgfields.get(i));
  616               } finally {
  617                   // clear the StateManager from the target object
  618                   if (_copy)
  619                       _to.pcReplaceStateManager(null);
  620               }
  621           }
  622   
  623           /**
  624            * Detach (or clear) the given field index.
  625            */
  626           private void detachField(PersistenceCapable from, int i, boolean fg) {
  627               // tell the state manager to provide the fields from the source to
  628               // this field manager, which will then replace the field with a
  629               // detached version
  630               if (fg)
  631                   sm.provideField(from, this, i);
  632               else if (!_copy) {
  633                   // if not copying and field should not be detached, clear it
  634                   clear();
  635                   sm.replaceField(_to, this, i);
  636               }
  637           }
  638   
  639           public void storeBooleanField(int field, boolean curVal) {
  640               super.storeBooleanField(field, curVal);
  641               sm.replaceField(_to, this, field);
  642           }
  643   
  644           public void storeByteField(int field, byte curVal) {
  645               super.storeByteField(field, curVal);
  646               sm.replaceField(_to, this, field);
  647           }
  648   
  649           public void storeCharField(int field, char curVal) {
  650               super.storeCharField(field, curVal);
  651               sm.replaceField(_to, this, field);
  652           }
  653   
  654           public void storeDoubleField(int field, double curVal) {
  655               super.storeDoubleField(field, curVal);
  656               sm.replaceField(_to, this, field);
  657           }
  658   
  659           public void storeFloatField(int field, float curVal) {
  660               super.storeFloatField(field, curVal);
  661               sm.replaceField(_to, this, field);
  662           }
  663   
  664           public void storeIntField(int field, int curVal) {
  665               super.storeIntField(field, curVal);
  666               sm.replaceField(_to, this, field);
  667           }
  668   
  669           public void storeLongField(int field, long curVal) {
  670               super.storeLongField(field, curVal);
  671               sm.replaceField(_to, this, field);
  672           }
  673   
  674           public void storeShortField(int field, short curVal) {
  675               super.storeShortField(field, curVal);
  676               sm.replaceField(_to, this, field);
  677           }
  678   
  679           public void storeStringField(int field, String curVal) {
  680               super.storeStringField(field, curVal);
  681               sm.replaceField(_to, this, field);
  682           }
  683   
  684           public void storeObjectField(int field, Object curVal) {
  685               super.storeObjectField(field, detachField(curVal, field));
  686               sm.replaceField(_to, this, field);
  687           }
  688   
  689           /**
  690            * Set the owner of the field's proxy to the detached state manager.
  691            */
  692           private Object reproxy(Object obj, int field) {
  693               if (obj != null && _detSM != null && obj instanceof Proxy)
  694                   ((Proxy) obj).setOwner(_detSM, field);
  695               return obj;
  696           }
  697   
  698           /**
  699            * Detach the given value if needed.
  700            */
  701           private Object detachField(Object curVal, int field) {
  702               if (curVal == null)
  703                   return null;
  704   
  705               FieldMetaData fmd = sm.getMetaData().getField(field);
  706               Object newVal = null;
  707               switch (fmd.getDeclaredTypeCode()) {
  708               case JavaTypes.ARRAY:
  709                   if (_copy)
  710                       newVal = _proxy.copyArray(curVal);
  711                   else
  712                       newVal = curVal;
  713                   detachArray(newVal, fmd);
  714                   return newVal;
  715               case JavaTypes.COLLECTION:
  716                   if (_copy) {
  717                       if (_detSM != null) {
  718                           newVal = _proxy.newCollectionProxy(fmd.getProxyType(),
  719                               fmd.getElement().getDeclaredType(),
  720                               fmd.getInitializer() instanceof Comparator ?
  721                               (Comparator) fmd.getInitializer() : null);
  722                           ((Collection) newVal).addAll((Collection) curVal);
  723                       } else
  724                           newVal = _proxy.copyCollection((Collection) curVal);
  725                   } else
  726                       newVal = curVal;
  727                   detachCollection((Collection) newVal, (Collection) curVal, fmd);
  728                   return reproxy(newVal, field);
  729               case JavaTypes.MAP:
  730                   if (_copy) {
  731                       if (_detSM != null) {
  732                           newVal = _proxy.newMapProxy(fmd.getProxyType(),
  733                               fmd.getKey().getDeclaredType(),
  734                               fmd.getElement().getDeclaredType(),
  735                               fmd.getInitializer() instanceof Comparator ?
  736                                   (Comparator) fmd.getInitializer() : null);
  737                           ((Map) newVal).putAll((Map) curVal);
  738                       } else
  739                           newVal = _proxy.copyMap((Map) curVal);
  740                   } else
  741                       newVal = curVal;
  742                   detachMap((Map) newVal, (Map) curVal, fmd);
  743                   return reproxy(newVal, field);
  744               case JavaTypes.CALENDAR:
  745                   newVal = (_copy) ? _proxy.copyCalendar((Calendar) curVal) :
  746                       curVal;
  747                   return reproxy(newVal, field);
  748               case JavaTypes.DATE:
  749                   newVal = (_copy) ? _proxy.copyDate((Date) curVal) : curVal;
  750                   return reproxy(newVal, field);
  751               case JavaTypes.OBJECT:
  752                   if (_copy)
  753                       newVal = _proxy.copyCustom(curVal);
  754                   return reproxy((newVal == null) ? curVal : newVal, field);
  755               case JavaTypes.PC:
  756               case JavaTypes.PC_UNTYPED:
  757                   return detachInternal(curVal);
  758               default:
  759                   return curVal;
  760               }
  761           }
  762   
  763           /**
  764            * Make sure all the values in the given array are detached.
  765            */
  766           private void detachArray(Object array, FieldMetaData fmd) {
  767               if (!fmd.getElement().isDeclaredTypePC())
  768                   return;
  769   
  770               int len = Array.getLength(array);
  771               for (int i = 0; i < len; i++)
  772                   Array.set(array, i, detachInternal(Array.get(array, i)));
  773           }
  774   
  775           /**
  776            * Make sure all the values in the given collection are detached.
  777            */
  778           private void detachCollection(Collection coll, Collection orig,
  779               FieldMetaData fmd) {
  780               // coll can be null if not copyable
  781               if (_copy && coll == null)
  782                   throw new UserException(_loc.get("not-copyable", fmd));
  783               if (!fmd.getElement().isDeclaredTypePC())
  784                   return;
  785   
  786               // unfortunately we have to clear the original and re-add to copy
  787               if (_copy)
  788                   coll.clear();
  789               Object detached;
  790               for (Iterator itr = orig.iterator(); itr.hasNext();) {
  791                   detached = detachInternal(itr.next());
  792                   if (_copy)
  793                       coll.add(detached);
  794               }
  795           }
  796   
  797           /**
  798            * Make sure all the values in the given map are detached.
  799            */
  800           private void detachMap(Map map, Map orig, FieldMetaData fmd) {
  801               // map can be null if not copyable
  802               if (_copy && map == null)
  803                   throw new UserException(_loc.get("not-copyable", fmd));
  804               boolean keyPC = fmd.getKey().isDeclaredTypePC();
  805               boolean valPC = fmd.getElement().isDeclaredTypePC();
  806               if (!keyPC && !valPC)
  807                   return;
  808   
  809               // if we have to copy keys, just clear and re-add; otherwise
  810               // we can use the entry set to reset the values only
  811               Map.Entry entry;
  812               if (!_copy || keyPC) {
  813                   if (_copy)
  814                       map.clear();
  815                   Object key, val;
  816                   for (Iterator itr = orig.entrySet().iterator(); itr.hasNext();){
  817                       entry = (Map.Entry) itr.next();
  818                       key = entry.getKey();
  819                       if (keyPC)
  820                           key = detachInternal(key);
  821                       val = entry.getValue();
  822                       if (valPC)
  823                           val = detachInternal(val);
  824                       if (_copy)
  825                           map.put(key, val);
  826                   }
  827               } else {
  828                   for (Iterator itr = map.entrySet().iterator(); itr.hasNext();) {
  829                       entry = (Map.Entry) itr.next ();
  830   					entry.setValue (detachInternal (entry.getValue ()));
  831   				}
  832   			}
  833   		}
  834   	}
  835   }

Save This Page
Home » apache-openjpa-1.1.0-source » org.apache.openjpa » kernel » [javadoc | source]