Skip to main content
summaryrefslogblamecommitdiffstats
blob: 3141e9ebff66d081cf0da68810ef5e066017ecfc (plain) (tree)
1
2
3
4
5
6
7
8
9
10









                                                                        
                                                                    



                                            
                                            

                                           
                                               
                                                     
                                                







                                                                
                                                                 

                                                                   
                                        












                                                         









                                                 
                                                   
                                               
                                                            




                                           










                                                                                                         

                                               








                                                                                                                                       
                                   











                                                                                              
                                      
 
                                













                                                                                                                       
                                                                                   


                                          




                                            
















































                                                                                



                            
                              



                                          





                                                    













                                          

                                      
                                              

   

                                                     

                                           





                                                                              
                                                         

                     
                                    
       
                                                                          














                                                                  







                              









                                                 
                                                          











                                                   
                                                                                                              
                                                 
                                                                       
     
                                                                                 
       
                               
         


                                                                  

                                        
                                                                         




                                                                               
                                                                     

                                     
                                                                    




























                                                                                                                        
                                                 
                                                                           


                                 
                                                   











































                                                                                                           

                                     
                                                 








                                                                                                       
                                                                           
     
                                         
     

                            



                                                

                                                 










                                                                             
                                                                            




                                                                               
                                                                      
   


                                                                 
                                                                                               
     



                                     






                                                                              
                                                 












                                                                                                       
                                        
 



                            
                                             
 
                                   

                                    

                                                   
       

                                                  

       
                                                                             


                                           












                                 
                           
       
                                                  

       















                                              
                                                 








                                                        
                                                                                                  



















                                                                           
                                                 


                                                  
                                                                                                


                                                       
                                                                      








                                                                      
                                                                                     









                                                                   
                                                                                                                                              

       

                                                                                        
       
                                                     




































                                                                                                                                                               









                                                                                                      























                                                                                                                                                     
                                                                                      



                                                                                        
                                                                  
























































                                                                                                                                                    
                                                 









                                                      
                                                                                                              







                                                              
                                                                                                   


















                                                                  
                                                                                                          


            
                                                   











                                                                  































                                                                                                                               
                                                                                                              



                                               
                                                                    






















































                                                                                                                      
                                                                           


                                        
                                







                                                                                                                      
                                                           
   
                                                       



















                                                                  
                                                                                            































                                                                                                     
                                                                                          











                                                                            
                                             


                               
                                                                                                        

























                                                                                     
                                   
     

                                       
       



                                                   






























































































                                                                                                                        
                                                      









                                                                                
                                                                                                 
































































































































                                                                                                                          
/*
 * Copyright (c) 2004 - 2012 Eike Stepper (Berlin, Germany) and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Eike Stepper - initial API and implementation
 *    Martin Fluegge - bug 247226: Transparently support legacy models
 *    Christian W. Damus (CEA) - isLoading() support for CDOResource
 */
package org.eclipse.emf.internal.cdo.object;

import org.eclipse.emf.cdo.CDOObject;
import org.eclipse.emf.cdo.CDOObjectHistory;
import org.eclipse.emf.cdo.CDOState;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.id.CDOIDUtil;
import org.eclipse.emf.cdo.common.model.CDOModelUtil;
import org.eclipse.emf.cdo.common.model.CDOType;
import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
import org.eclipse.emf.cdo.common.revision.CDOElementProxy;
import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.common.revision.CDORevisionData;
import org.eclipse.emf.cdo.common.security.CDOPermission;
import org.eclipse.emf.cdo.common.util.CDOException;
import org.eclipse.emf.cdo.eresource.CDOResource;
import org.eclipse.emf.cdo.eresource.impl.CDOResourceImpl;
import org.eclipse.emf.cdo.spi.common.model.InternalCDOClassInfo;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
import org.eclipse.emf.cdo.util.CDOUtil;
import org.eclipse.emf.cdo.view.CDOView;

import org.eclipse.emf.internal.cdo.CDOObjectImpl;
import org.eclipse.emf.internal.cdo.bundle.OM;
import org.eclipse.emf.internal.cdo.view.CDOStateMachine;

import org.eclipse.net4j.util.ReflectUtil;
import org.eclipse.net4j.util.WrappedException;
import org.eclipse.net4j.util.om.trace.ContextTracer;

import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.InternalEList;
import org.eclipse.emf.spi.cdo.CDOStore;
import org.eclipse.emf.spi.cdo.FSMUtil;
import org.eclipse.emf.spi.cdo.InternalCDOObject;
import org.eclipse.emf.spi.cdo.InternalCDOResource;
import org.eclipse.emf.spi.cdo.InternalCDOView;
import org.eclipse.emf.spi.cdo.InternalCDOView.ViewAndState;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;

/**
 * @author Eike Stepper
 * @author Martin Fluegge
 * @since 2.0
 */
public abstract class CDOLegacyWrapper extends CDOObjectWrapper
{
  private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_OBJECT, CDOLegacyWrapper.class);

  private final InternalCDOClassInfo classInfo;

  /**
   * This ThreadLocal map stores all pre-registered objects. This avoids a never-ending loop when setting the container
   * of an object.
   */
  private static ThreadLocal<Map<CDOID, CDOLegacyWrapper>> wrapperRegistry = new InheritableThreadLocal<Map<CDOID, CDOLegacyWrapper>>()
  {
    @Override
    protected Map<CDOID, CDOLegacyWrapper> initialValue()
    {
      return CDOIDUtil.createMap();
    }
  };

  private static ThreadLocal<Counter> recursionCounter = new InheritableThreadLocal<Counter>()
  {
    @Override
    protected Counter initialValue()
    {
      return new Counter();
    }
  };

  protected ViewAndState viewAndState;

  protected Object idOrRevision;

  /**
   * It could happen that while <i>revisionToInstance()</i> is executed externally the <i>internalPostLoad()</i> method
   * will be called. This happens for example if <i>internalPostInvalidate()</i> is called. The leads to another
   * <i>revisionToInstance()</i> call while the first call has not finished. This is certainly not so cool. That's why
   * <b>underConstruction</b> will flag that <i>revisionToInstance()</i> is still running and avoid the second call.
   *
   * @since 3.0
   */
  private boolean underConstruction;

  public CDOLegacyWrapper(InternalEObject instance)
  {
    this.instance = instance;
    classInfo = (InternalCDOClassInfo)CDOModelUtil.getClassInfo(instance.eClass());
    viewAndState = ViewAndState.TRANSIENT;
  }

  public InternalCDOClassInfo cdoClassInfo()
  {
    return classInfo;
  }

  public CDOID cdoID()
  {
    if (idOrRevision == null)
    {
      return null;
    }

    if (idOrRevision instanceof CDOID)
    {
      return (CDOID)idOrRevision;
    }

    return ((InternalCDORevision)idOrRevision).getID();
  }

  public InternalCDOView cdoView()
  {
    return viewAndState.view;
  }

  public void cdoInternalSetID(CDOID id)
  {
    if (TRACER.isEnabled())
    {
      TRACER.format("Setting ID: {0} for {1}", id, instance); //$NON-NLS-1$
    }

    if (idOrRevision == null || id == null)
    {
      idOrRevision = id;
    }
  }

  public void cdoInternalSetView(CDOView view)
  {
    if (TRACER.isEnabled())
    {
      TRACER.format("Setting view: {0} for {1}", view, instance); //$NON-NLS-1$
    }

    InternalCDOView newView = (InternalCDOView)view;
    if (newView != null)
    {
      viewAndState = newView.getViewAndState(viewAndState.state);
    }
    else
    {
      viewAndState = ViewAndState.TRANSIENT.getViewAndState(viewAndState.state);
    }
  }

  public CDOState cdoState()
  {
    return viewAndState.state;
  }

  public InternalCDORevision cdoRevision()
  {
    if (idOrRevision instanceof InternalCDORevision)
    {
      return (InternalCDORevision)idOrRevision;
    }

    return null;
  }

  @Override
  public CDOResourceImpl cdoResource()
  {
    revisionToInstanceResource();
    return super.cdoResource();
  }

  public void cdoReload()
  {
    CDOStateMachine.INSTANCE.reload(this);
  }

  public CDOObjectHistory cdoHistory()
  {
    return viewAndState.view.getHistory(this);
  }

  public CDOState cdoInternalSetState(CDOState state)
  {
    CDOState oldState = viewAndState.state;
    if (oldState != state)
    {
      if (TRACER.isEnabled())
      {
        TRACER.format("Setting state {0} for {1}", state, this); //$NON-NLS-1$
      }

      viewAndState = viewAndState.getViewAndState(state);
      adjustEProxy();

      if (viewAndState.view != null)
      {
        viewAndState.view.handleObjectStateChanged(this, oldState, state);
      }

      return oldState;
    }

    return null;
  }

  public void cdoInternalSetRevision(CDORevision revision)
  {
    if (TRACER.isEnabled())
    {
      TRACER.trace("Setting revision: " + revision); //$NON-NLS-1$
    }

    if (revision == null)
    {
      idOrRevision = cdoID();
    }
    else
    {
      idOrRevision = revision;
    }
  }

  public void cdoInternalPostAttach()
  {
    instanceToRevision();

    for (Adapter adapter : eAdapters())
    {
      if (!(adapter instanceof CDOObjectWrapper))
      {
        viewAndState.view.handleAddAdapter(this, adapter);
      }
    }
  }

  public void cdoInternalPostDetach(boolean remote)
  {
    if (remote)
    {
      // Do nothing??
      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);
          }
        }
      }
    }
  }

  /**
   * @since 3.0
   */
  public void cdoInternalPostRollback()
  {
    CDOStateMachine.INSTANCE.read(this);
  }

  /**
   * 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);
        }
      }
    }
  }

  public void cdoInternalPreLoad()
  {
    // Do nothing
  }

  public void cdoInternalPostLoad()
  {
    // TODO Consider not remembering the revision after copying it to the instance (spare 1/2 of the space)
    revisionToInstance();
  }

  public void cdoInternalPostInvalidate()
  {
    if (cdoState() != CDOState.CLEAN)
    {
      InternalCDORevision revision = cdoView().getRevision(cdoID(), true);
      cdoInternalSetRevision(revision);
    }

    revisionToInstance();
    cdoInternalSetState(CDOState.CLEAN);
  }

  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();
  }

  protected void instanceToRevisionContainment()
  {
    InternalCDORevision revision = cdoRevision();

    CDOResource resource = (CDOResource)getInstanceResource(instance);
    revision.setResourceID(resource == null ? CDOID.NULL : resource.cdoID());

    InternalEObject eContainer = getInstanceContainer(instance);
    if (eContainer == null)
    {
      revision.setContainerID(CDOID.NULL);
      revision.setContainingFeatureID(0);
    }
    else
    {
      CDOObject cdoContainer = FSMUtil.adapt(eContainer, viewAndState.view);
      revision.setContainerID(cdoContainer);
      revision.setContainingFeatureID(getInstanceContainerFeatureID(instance));
    }
  }

  protected void instanceToRevisionFeature(EStructuralFeature feature)
  {
    if (isSetInstanceValue(instance, feature))
    {
      Object instanceValue = getInstanceValue(instance, feature);
      CDOObjectImpl.instanceToRevisionFeature(viewAndState.view, this, feature, instanceValue);
    }
  }

  protected void revisionToInstance()
  {
    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();
    InternalCDOResource resource = null;

    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
    {
      if (resource != null)
      {
        resource.cdoInternalLoadingDone(instance);
      }

      if (deliver)
      {
        instance.eSetDeliver(true);
      }

      counter.decrement();
      unregisterWrapper(this);
      underConstruction = false;
    }
  }

  /**
   * @since 4.0
   */
  protected void revisionToInstanceContainer()
  {
    InternalCDORevision revision = cdoRevision();
    CDOPermission permission = revision.getPermission();
    if (permission != CDOPermission.WRITE)
    {
      revision.setPermission(CDOPermission.WRITE);
    }

    try
    {
      Object containerID = revision.getContainerID();
      InternalEObject container = getEObjectFromPotentialID(viewAndState.view, null, containerID);
      EObject oldContainer = instance.eContainer();
      if (oldContainer != container)
      {
        setInstanceContainer(container, revision.getContainingFeatureID());
      }
    }
    finally
    {
      if (permission != CDOPermission.WRITE)
      {
        revision.setPermission(permission);
      }
    }
  }

  /**
   * @since 4.0
   */
  protected void revisionToInstanceResource()
  {
    InternalCDORevision revision = cdoRevision();
    if (revision != null)
    {
      CDOID resourceID = revision.getResourceID();
      InternalEObject resource = getEObjectFromPotentialID(viewAndState.view, null, resourceID);
      setInstanceResource((Resource.Internal)resource);
      if (resource != null)
      {
        viewAndState.view.registerObject((InternalCDOObject)resource);
      }
    }
  }

  /**
   * @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$
          }

          list.basicAdd(object, null);
        }
      }
    }
    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$
        }

        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)
          {
            // 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 (TRACER.isEnabled())
        {
          TRACER.format("Added object " + object + " to feature " + feature + " in instance " + instance); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
        }
      }
    }
  }

  /**
   * This method retrieves the value from the feature at the given index. It retrieves the value either from the views's
   * store or the internal pre-registration Map.
   *
   * @param feature
   *          the feature to retrieve the value from
   * @param index
   *          the given index of the object in the feature
   * @return the value from the feature at the given index
   */
  private Object getValueFromRevision(EStructuralFeature feature, int index)
  {
    InternalCDORevision revision = cdoRevision();
    Object object = revision.get(feature, index);
    if (object == null)
    {
      return null;
    }

    if (object instanceof CDOElementProxy)
    {
      // Resolve proxy
      CDOElementProxy proxy = (CDOElementProxy)object;
      object = viewAndState.view.getSession().resolveElementProxy(revision, feature, index, proxy.getIndex());
    }

    if (object instanceof CDOLegacyWrapper)
    {
      return ((CDOLegacyWrapper)object).cdoInternalInstance();
    }

    CDOType type = CDOModelUtil.getType(feature.getEType());
    object = viewAndState.view.getStore().convertToEMF(instance, revision, feature, index, object);

    if (type == CDOType.OBJECT)
    {
      if (object instanceof CDOID)
      {
        CDOID id = (CDOID)object;
        if (id.isNull())
        {
          return null;
        }

        object = getRegisteredWrapper(id);
        if (object != null)
        {
          return ((CDOLegacyWrapper)object).cdoInternalInstance();
        }

        if (id.isExternal())
        {
          object = viewAndState.view.getResourceSet().getEObject(URI.createURI(id.toURIFragment()), true);
        }
        else
        {
          object = viewAndState.view.getObject(id);
        }

        if (object instanceof CDOObjectWrapper)
        {
          return ((CDOObjectWrapper)object).cdoInternalInstance();
        }
      }
    }

    return object;
  }

  /**
   * @param feature
   *          in case that a proxy has to be created the feature that will determine the interface type of the proxy and
   *          that will be used later to resolve the proxy. <code>null</code> indicates that proxy creation will be
   *          avoided!
   */
  protected InternalEObject getEObjectFromPotentialID(InternalCDOView view, EStructuralFeature feature,
      Object potentialID)
  {
    CDOLegacyWrapper wrapper;
    if (potentialID instanceof CDOID && (wrapper = getRegisteredWrapper((CDOID)potentialID)) != null)
    {
      potentialID = wrapper.instance;

      if (TRACER.isEnabled())
      {
        TRACER.format("Getting Object (" + potentialID + ") from localThread instead of the view"); //$NON-NLS-1$ //$NON-NLS-2$
      }
    }
    else
    {
      if (potentialID instanceof CDOID)
      {
        CDOID id = (CDOID)potentialID;
        if (id.isNull())
        {
          return null;
        }

        if (id.isExternal())
        {
          URI uri = URI.createURI(id.toURIFragment());
          InternalEObject eObject = (InternalEObject)viewAndState.view.getResourceSet().getEObject(uri, true);
          return eObject;
        }

        boolean loadOnDemand = feature == null;
        potentialID = viewAndState.view.getObject(id, loadOnDemand);
        if (potentialID == null && !loadOnDemand)
        {
          return createProxy(view, feature, id);
        }
      }

      if (potentialID instanceof InternalCDOObject)
      {
        return ((InternalCDOObject)potentialID).cdoInternalInstance();
      }
    }

    return (InternalEObject)potentialID;
  }

  /**
   * Creates and returns a <em>proxy</em> object. The usage of a proxy object is strongly limited. The only guarantee
   * that can be made is that the following methods are callable and will behave in the expected way:
   * <ul>
   * <li>{@link CDOObject#cdoID()} will return the {@link CDOID} of the target object
   * <li>{@link CDOObject#cdoState()} will return {@link CDOState#PROXY PROXY}
   * <li>{@link InternalEObject#eIsProxy()} will return <code>true</code>
   * <li>{@link InternalEObject#eProxyURI()} will return the EMF proxy URI of the target object
   * </ul>
   * Calling any other method on the proxy object will result in an {@link UnsupportedOperationException} being thrown
   * at runtime. Note also that the proxy object might even not be cast to the concrete type of the target object. The
   * proxy can only guaranteed to be of <em>any</em> concrete subtype of the declared type of the given feature.
   * <p>
   * TODO {@link InternalEObject#eResolveProxy(InternalEObject)}
   */
  protected InternalEObject createProxy(InternalCDOView view, EStructuralFeature feature, CDOID id)
  {
    EClassifier eType = feature.getEType();
    Class<?> instanceClass = eType.getInstanceClass();

    Class<?>[] interfaces = { instanceClass, InternalEObject.class, LegacyProxy.class };
    ClassLoader classLoader = CDOLegacyWrapper.class.getClassLoader();
    LegacyProxyInvocationHandler handler = new LegacyProxyInvocationHandler(this, id);
    return (InternalEObject)Proxy.newProxyInstance(classLoader, interfaces, handler);
  }

  protected void clearEList(InternalEList<?> list)
  {
    for (int i = list.size() - 1; i >= 0; --i)
    {
      Object obj = list.get(i);
      list.basicRemove(obj, null);
    }
  }

  /**
   * TODO Consider using only EMF concepts for resolving proxies!
   */
  protected void resolveAllProxies()
  {
    for (EStructuralFeature feature : classInfo.getAllPersistentFeatures())
    {
      if (feature instanceof EReference)
      {
        resolveProxies(feature);
      }
    }
  }

  /**
   * IMPORTANT: Compile errors in this method might indicate an old version of EMF. Legacy support is only enabled for
   * EMF with fixed bug #247130. These compile errors do not affect native models!
   */
  protected void resolveProxies(EStructuralFeature feature)
  {
    Object value = getInstanceValue(instance, feature);
    if (value != null)
    {
      if (feature.isMany())
      {
        @SuppressWarnings("unchecked")
        InternalEList<Object> list = (InternalEList<Object>)value;
        int size = list.size();

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

        for (int i = 0; i < size; i++)
        {
          Object element = list.get(i);
          if (element instanceof LegacyProxy)
          {
            CDOID id = ((LegacyProxy)element).getID();
            InternalCDOObject resolved = (InternalCDOObject)viewAndState.view.getObject(id);
            InternalEObject instance = resolved.cdoInternalInstance();

            // TODO LEGACY
            // // TODO Is InternalEList.basicSet() needed???
            // if (list instanceof
            // org.eclipse.emf.ecore.util.DelegatingInternalEList)
            // {
            // list =
            // ((org.eclipse.emf.ecore.util.DelegatingInternalEList)list).getDelegateInternalEList();
            // }

            // if (list instanceof NotifyingListImpl<?>)
            // {
            // ((NotifyingListImpl<Object>)list).basicSet(i, instance, null);
            // }
            // else
            // {
            list.set(i, instance);
            // }
          }
        }

        if (deliver)
        {
          instance.eSetDeliver(true);
        }
      }
      else
      {
        if (value instanceof LegacyProxy)
        {
          CDOID id = ((LegacyProxy)value).getID();
          InternalCDOObject resolved = (InternalCDOObject)viewAndState.view.getObject(id);
          InternalEObject instance = resolved.cdoInternalInstance();
          setInstanceValue(instance, feature, instance);
        }
      }
    }
  }

  protected void adjustEProxy()
  {
    // Setting eProxyURI is necessary to prevent content adapters from
    // loading the whole content tree.
    // TODO Does not have the desired effect ;-( see CDOEditor.createModel()
    if (viewAndState.state == CDOState.PROXY)
    {
      if (!instance.eIsProxy())
      {
        URI uri = URI.createURI(CDOProtocolConstants.PROTOCOL_NAME + ":proxy#" + cdoID()); //$NON-NLS-1$
        if (TRACER.isEnabled())
        {
          TRACER.format("Setting proxyURI {0} for {1}", uri, instance); //$NON-NLS-1$
        }

        instance.eSetProxyURI(uri);
      }
    }
    else
    {
      if (instance.eIsProxy())
      {
        if (TRACER.isEnabled())
        {
          TRACER.format("Unsetting proxyURI for {0}", instance); //$NON-NLS-1$
        }

        instance.eSetProxyURI(null);
      }
    }
  }

  @Override
  public synchronized EList<Adapter> eAdapters()
  {
    EList<Adapter> adapters = instance.eAdapters();
    if (!FSMUtil.isTransient(this))
    {
      InternalCDOView view = cdoView();
      for (Adapter adapter : adapters)
      {
        if (!(adapter instanceof CDOLegacyWrapper))
        {
          view.handleAddAdapter(this, adapter);
        }
      }
    }

    return adapters;
  }

  public static boolean isLegacyProxy(Object object)
  {
    return object instanceof LegacyProxy;
  }

  protected static int getEFlagMask(Class<?> instanceClass, String flagName)
  {
    Field field = ReflectUtil.getField(instanceClass, flagName);
    if (!field.isAccessible())
    {
      field.setAccessible(true);
    }

    try
    {
      return (Integer)field.get(null);
    }
    catch (IllegalAccessException ex)
    {
      throw WrappedException.wrap(ex);
    }
  }

  /**
   * @since 3.0
   */
  protected static CDOLegacyWrapper getRegisteredWrapper(CDOID id)
  {
    return wrapperRegistry.get().get(id);
  }

  /**
   * Adds an object to the pre-registered objects list which hold all created objects even if they are not registered in
   * the view
   *
   * @since 3.0
   */
  protected static void registerWrapper(CDOLegacyWrapper wrapper)
  {
    wrapperRegistry.get().put(wrapper.cdoID(), wrapper);
  }

  /**
   * @since 3.0
   */
  protected static void unregisterWrapper(CDOLegacyWrapper wrapper)
  {
    wrapperRegistry.get().remove(wrapper.cdoID());
  }

  /**
   * @since 3.0
   */
  protected static boolean isRegisteredWrapper(CDOLegacyWrapper wrapper)
  {
    return wrapperRegistry.get().containsKey(wrapper.cdoID());
  }

  // TODO: Remove this method if it is ensured that ist is not needed anymore
  // private void adjustOppositeReference(InternalCDOObject cdoObject, EObject oppositeObject, EReference
  // oppositeReference)
  // {
  // if (oppositeObject != null)
  // {
  // InternalCDOObject oppositeCDOObject = (InternalCDOObject)CDOUtil.getCDOObject(oppositeObject);
  //
  // if (!FSMUtil.isTransient(oppositeCDOObject) && !EMFUtil.isPersistent(oppositeReference))
  // {
  // adjustPersistentOppositeReference(cdoObject, oppositeObject, oppositeReference);
  // }
  // else
  // {
  // if (oppositeReference.isResolveProxies())
  // {
  // adjustTransientOppositeReference(instance, (InternalEObject)oppositeObject, oppositeReference);
  // }
  // }
  // }
  // }

  private void adjustPersistentOppositeReference(InternalCDOObject cdoObject, EObject oppositeObject,
      EReference oppositeReference)
  {
    InternalCDOObject oppositeCDOObject = (InternalCDOObject)CDOUtil.getCDOObject(oppositeObject);
    if (oppositeCDOObject != null)
    {
      InternalCDOView view = oppositeCDOObject.cdoView();
      if (view != null)
      {
        CDOStore store = viewAndState.view.getStore();
        if (store != null)
        {
          if (oppositeReference.isMany())
          {
            EObject eObject = oppositeCDOObject.cdoInternalInstance();

            @SuppressWarnings("unchecked")
            EList<Object> list = (EList<Object>)eObject.eGet(oppositeReference);
            int index = list.indexOf(instance);

            if (index != EStore.NO_INDEX && !store.isEmpty(oppositeCDOObject, oppositeReference))
            {
              store.set(oppositeCDOObject, oppositeReference, index, cdoObject);
            }
          }
          else
          {
            store.set(oppositeCDOObject, oppositeReference, 0, cdoObject);
          }
        }
      }
    }
  }

  private void adjustTransientOppositeReference(InternalEObject instance, InternalEObject object,
      EReference oppositeReference)
  {
    boolean wasDeliver = object.eDeliver(); // Disable notifications
    if (wasDeliver)
    {
      object.eSetDeliver(false);
    }

    try
    {
      if (oppositeReference.isMany())
      {
        @SuppressWarnings("unchecked")
        InternalEList<Object> list = (InternalEList<Object>)object.eGet(oppositeReference);
        list.basicAdd(instance, null);
      }
      else
      {
        if (object.eGet(oppositeReference) != instance)
        {
          object.eInverseAdd(instance, oppositeReference.getFeatureID(), ((EObject)instance).getClass(), null);
        }
      }
    }
    finally
    {
      if (wasDeliver)
      {
        object.eSetDeliver(true);
      }
    }
  }

  /**
   * @author Eike Stepper
   */
  private static interface LegacyProxy
  {
    public CDOID getID();
  }

  /**
   * @author Eike Stepper
   */
  private static final class LegacyProxyInvocationHandler implements InvocationHandler, LegacyProxy
  {
    private static final Method getIDMethod = ReflectUtil.getMethod(LegacyProxy.class, "getID"); //$NON-NLS-1$

    private static final Method eIsProxyMethod = ReflectUtil.getMethod(EObject.class, "eIsProxy"); //$NON-NLS-1$

    private static final Method eProxyURIMethod = ReflectUtil.getMethod(InternalEObject.class, "eProxyURI"); //$NON-NLS-1$

    private CDOLegacyWrapper wrapper;

    private CDOID id;

    public LegacyProxyInvocationHandler(CDOLegacyWrapper wrapper, CDOID id)
    {
      this.wrapper = wrapper;
      this.id = id;
    }

    public CDOID getID()
    {
      return id;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
      if (method.equals(getIDMethod))
      {
        return id;
      }

      if (method.equals(eIsProxyMethod))
      {
        return true;
      }

      if (method.equals(eProxyURIMethod))
      {
        // Use container's resource because it's guaranteed to be in the same CDOView as the resource of the target!
        Resource resource = wrapper.eResource();

        // TODO Consider using a "fake" Resource implementation. See Resource.getEObject(...)
        return resource.getURI().appendFragment(id.toURIFragment());
      }

      // A client must have invoked the proxy while being told not to do so!
      throw new UnsupportedOperationException(method.getName());
    }
  }

  /**
   * @author Martin Fluegge
   */
  private static final class Counter
  {
    private int value;

    public Counter()
    {
    }

    public void increment()
    {
      ++value;
    }

    public int decrement()
    {
      return --value;
    }
  }
}

Back to the top