public void cdoInternalPostDetach(boolean remote) {
    if (remote) {
      setInstanceContainer(null, eContainerFeatureID());
      setInstanceResource(null);
      return;
    }

    // This loop adjusts the opposite wrapper objects to support dangling references. See
    // Bugzilla_251263_Test
    InternalCDORevision revision = cdoRevision();
    for (EReference reference : classInfo.getAllPersistentReferences()) {
      if (!reference.isContainer() && classInfo.hasPersistentOpposite(reference)) {
        if (reference.isMany()) {
          EReference oppositeReference = reference.getEOpposite();

          int size = revision.size(reference);
          for (int i = 0; i < size; i++) {
            EObject object = (EObject) getValueFromRevision(reference, i);
            adjustPersistentOppositeReference(this, object, oppositeReference);
          }
        } else {
          EObject oppositeObject = (EObject) instance.eGet(reference);
          if (oppositeObject != null) {
            EReference oppositeReference = reference.getEOpposite();
            adjustPersistentOppositeReference(this, oppositeObject, oppositeReference);
          }
        }
      }
    }
  }
 /** TODO Consider using only EMF concepts for resolving proxies! */
 protected void resolveAllProxies() {
   for (EStructuralFeature feature : classInfo.getAllPersistentFeatures()) {
     if (feature instanceof EReference) {
       resolveProxies(feature);
     }
   }
 }
  /**
   * CDO persists the isUnset state of an eObject in the database. The indicator for this is that
   * the feature is null in the revision (see CDOStore.isSet()). When committing a legacy object all
   * values in the instance for native attributes are set with the java default values. So, these
   * values will be stored in the revision and CDO cannot distinguish whether the feature is set or
   * not. This method must ensure that the value will be set to null if the feature is not set.
   */
  public void cdoInternalPreCommit() {
    // We have to set this here because the CDOLegacyAdapter will not be notified when the instance
    // is the target of a
    // single-directional containment reference.
    // If the container is not an legacy Object the system will get no information
    instanceToRevisionContainment();

    InternalCDORevision revision = cdoRevision();
    for (EStructuralFeature feature : classInfo.getAllPersistentFeatures()) {
      if (feature.isUnsettable()) {
        if (!isSetInstanceValue(instance, feature)) {
          if (feature.isMany()) {
            @SuppressWarnings("unchecked")
            InternalEList<Object> list = (InternalEList<Object>) instance.eGet(feature);
            clearEList(list);
          } else {
            revision.set(feature, EStore.NO_INDEX, null);
          }
        } else if (instance.eGet(feature) == null) {
          // Must be single-valued!
          revision.set(feature, EStore.NO_INDEX, CDORevisionData.NIL);
        }
      }
    }
  }
  protected void instanceToRevision() {
    InternalCDORevision revision = cdoRevision();
    if (TRACER.isEnabled()) {
      TRACER.format(
          "Transfering instance to revision: {0} --> {1}", instance, revision); // $NON-NLS-1$
    }

    // Handle containment
    instanceToRevisionContainment();

    // Handle values
    for (EStructuralFeature feature : classInfo.getAllPersistentFeatures()) {
      instanceToRevisionFeature(feature);
    }

    revision.setUnchunked();
  }
  /** @since 3.0 */
  protected void revisionToInstanceFeature(EStructuralFeature feature) {
    if (feature.isUnsettable() && !viewAndState.view.getStore().isSet(this, feature)) {
      // Clarify if this is sufficient for bidirectional references
      instance.eUnset(feature);
      return;
    }

    if (feature.isMany()) {
      if (TRACER.isEnabled()) {
        TRACER.format(
            "State of Object ("
                + this
                + "/"
                + instance
                + ") is : "
                + viewAndState.state); // $NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
      }

      if (viewAndState.state == CDOState.CLEAN
          || viewAndState.state == CDOState.PROXY
          || viewAndState.state == CDOState.NEW
          || viewAndState.state == CDOState.DIRTY) {
        InternalCDORevision revision = cdoRevision();
        int size = revision.size(feature);

        @SuppressWarnings("unchecked")
        InternalEList<Object> list = (InternalEList<Object>) instance.eGet(feature);

        clearEList(list);
        for (int i = 0; i < size; i++) {
          Object object = getValueFromRevision(feature, i);

          if (TRACER.isEnabled()) {
            TRACER.format(
                "Adding "
                    + object
                    + " to feature "
                    + feature
                    + "in instance "
                    + instance); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
          }

          // Disable notifications from value during the
          // invalidation in case of
          // eInverseAdd/eInverseRemove
          boolean eDeliver = false;
          if (object instanceof Notifier) {
            Notifier notifier = (Notifier) object;
            eDeliver = notifier.eDeliver();
            if (eDeliver) {
              notifier.eSetDeliver(false);
            }
          }

          list.basicAdd(object, null);

          if (object instanceof Notifier && eDeliver) {
            Notifier notifier = (Notifier) object;
            notifier.eSetDeliver(eDeliver);
          }
        }
      }
    } else {
      // !feature.isMany()
      Object object = getValueFromRevision(feature, 0);
      if (feature instanceof EAttribute) {
        if (TRACER.isEnabled()) {
          TRACER.format(
              "Setting attribute value "
                  + object
                  + " to feature "
                  + feature
                  + " in instance "
                  + instance); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
        }

        // Just fake it for the store :(
        if (feature.isUnsettable() && object.equals(CDORevisionData.NIL)) {
          eSet(feature, null);
        } else {
          if (object != null) {
            eSet(feature, object);
          } else {
            // TODO Unset for features with non-null default values would not lead to null values.
            // Probably CDORevisionData.NIL has to be used, but that impacts all IStores. Deferred
            // ;-(
            eUnset(feature);
          }
        }
      } else {
        // EReferences
        if (TRACER.isEnabled()) {
          TRACER.format(
              "Adding object "
                  + object
                  + " to feature "
                  + feature
                  + " in instance "
                  + instance); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
        }

        // Disable notifications from value during the
        // invalidation in case of
        // eInverseAdd/eInverseRemove
        boolean eDeliver = false;
        if (object instanceof Notifier) {
          Notifier notifier = (Notifier) object;
          eDeliver = notifier.eDeliver();
          if (eDeliver) {
            notifier.eSetDeliver(false);
          }
        }
        EObject oldContainerOfValue = null;
        boolean eDeliverForOldContainerOfValue = false;
        if (object instanceof InternalEObject) {
          InternalEObject eObject = (InternalEObject) object;
          oldContainerOfValue = eObject.eInternalContainer();
          if (oldContainerOfValue != null) {
            eDeliverForOldContainerOfValue = oldContainerOfValue.eDeliver();
            if (eDeliverForOldContainerOfValue) {
              oldContainerOfValue.eSetDeliver(false);
            }
          }
        }

        int featureID = instance.eClass().getFeatureID(feature);
        Class<? extends Object> baseClass = object == null ? null : object.getClass();
        EStructuralFeature.Internal internalFeature = (EStructuralFeature.Internal) feature;
        EReference oppositeReference = internalFeature.getEOpposite();

        if (oppositeReference != null) {
          if (object != null && object != instance.eGet(feature)) {
            // If you have a containment reference but the container is not set, but the object is
            // attached to a
            // resource
            // do not set the feature to null. Otherwise the object will be removed from the
            // container which is the
            // resource instead of the original container. As a result the object will be detached.
            // See
            // MapTest.testEObjectToEObjectValueContainedMap for more information
            if (object != instance.eContainer() || !oppositeReference.isContainment()) {
              instance.eInverseAdd((InternalEObject) object, featureID, baseClass, null);
            }

            if (!classInfo.hasPersistentOpposite(internalFeature)) {
              adjustTransientOppositeReference(
                  instance, (InternalEObject) object, oppositeReference);
            }
          }
        } else {
          if (object != CDORevisionData.NIL) {
            EReference reference = (EReference) feature;
            if (reference.isContainment()) {
              if (object != null) {
                // Calling eSet it not the optimal approach, but currently there is no other way to
                // set the value here.
                // To avoid attaching already processed (clean) objects a check was introduced to
                // CDOResourceImpl.attached(EObject).
                // If we find a way to avoid the call of eSet and if we are able to only set the
                // feature value directly
                // this check can be removed from CDOResourceImpl. See also Bug 352204.
                instance.eSet(feature, object);
              } else {
                instance.eSet(feature, null);
              }
            } else {
              instance.eSet(feature, object);
            }
          } else {
            instance.eSet(feature, null);
          }
        }

        if (object instanceof Notifier && eDeliver) {
          Notifier notifier = (Notifier) object;
          notifier.eSetDeliver(eDeliver);
        }
        if (oldContainerOfValue != null && eDeliverForOldContainerOfValue) {
          oldContainerOfValue.eSetDeliver(eDeliverForOldContainerOfValue);
        }

        if (TRACER.isEnabled()) {
          TRACER.format(
              "Added object "
                  + object
                  + " to feature "
                  + feature
                  + " in instance "
                  + instance); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
        }
      }
    }
  }
  protected void revisionToInstance() {
    synchronized (recursionCounter) {
      if (underConstruction) {
        // Return if revisionToInstance was called before to avoid doubled calls
        return;
      }

      underConstruction = true;
      InternalCDORevision revision = cdoRevision();

      if (TRACER.isEnabled()) {
        TRACER.format(
            "Transfering revision to instance: {0} --> {1}", revision, instance); // $NON-NLS-1$
      }

      boolean deliver = instance.eDeliver();
      if (deliver) {
        instance.eSetDeliver(false);
      }

      Counter counter = recursionCounter.get();
      if (counter == null) {
        counter = new Counter();
        recursionCounter.set(counter);
      }

      InternalCDOResource resource = null;
      boolean bypassPermissionChecks = revision.bypassPermissionChecks(true);

      try {
        registerWrapper(this);
        counter.increment();
        viewAndState.view.registerObject(this);

        revisionToInstanceResource();
        revisionToInstanceContainer();

        Resource eResource = instance.eResource();
        if (eResource instanceof InternalCDOResource) {
          resource = (InternalCDOResource) eResource;
          resource.cdoInternalLoading(instance);
        }

        for (EStructuralFeature feature : classInfo.getAllPersistentFeatures()) {
          revisionToInstanceFeature(feature);
        }
      } catch (RuntimeException ex) {
        OM.LOG.error(ex);
        throw ex;
      } catch (Exception ex) {
        OM.LOG.error(ex);
        throw new CDOException(ex);
      } finally {
        try {
          revision.bypassPermissionChecks(bypassPermissionChecks);

          if (resource != null) {
            resource.cdoInternalLoadingDone(instance);
          }

          if (deliver) {
            instance.eSetDeliver(true);
          }
        } finally {
          if (counter.decrement() == 0) {
            recursionCounter.remove();
          }

          unregisterWrapper(this);
          underConstruction = false;
        }
      }
    }
  }