Commit 06a7e2c3 authored by Erwan Bousse's avatar Erwan Bousse
Browse files

Versionning everything

parent cf54973e
package fr.inria.diverse.cloning.cloner.common;
import fr.inria.diverse.cloning.cloner.emfextension.impl.LooseCopier;
public interface CloningMaterial {
public MetamodelTags getTags();
public LooseCopier createLightCopier();
}
package fr.inria.diverse.cloning.cloner.common;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EStructuralFeature;
/**
* Singleton class that stores the cloning tags of a given metamodel for a given cloning technique.
*
* @author ebousse
*
*/
public interface MetamodelTags {
public ClassTag getTagOf(EClass eClass);
public boolean mayTagClassesShareable();
public boolean isPropertyShareable(EStructuralFeature prop);
/**
* Number of _completely_ shareable classes / total classes
* @return
*/
public double getCompShareableClassesRatio();
/**
* Number of _partially_ shareable classes / total classes
* @return
*/
public double getPartShareableClassesRatio();
/**
* Number of shareable properties in shareable classes / total properties
* @return
*/
public double getShareablePropertiesInShareableClassesRatio();
/**
* Number of shareable properties in shareable classes / total shareable classes
* @return
*/
public double getMeanShareablePropertiesInShareableClasses();
}
package fr.inria.diverse.cloning.cloner.emfextension;
import org.eclipse.emf.ecore.EObject;
/**
* Should allow to know in what you are contained.
* But not "required" for objects that are shared and contained in a shared reference...
* Thus not used for now.
*
* @author ebousse
*
*/
public interface ShareableEObject extends EObject {
/**
* Gives access to the EObject or the Resource that contains this object
* in a given context (a ResourceSet, ie. a model).
* Relevant only if this object is shared between multiple models.
*
* @param context The context in which this object in contained, ie a model
* @return Either a Resource or an EObject that contains this object in the given context.
*/
//public Object getContainerOrResourceInContext(ResourceSet context);
}
package fr.inria.diverse.cloning.cloner.emfextension.impl;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.MinimalEObjectImpl;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.Resource.Internal;
import org.eclipse.emf.ecore.resource.ResourceSet;
import fr.inria.diverse.cloning.cloner.common.ClassTag;
import fr.inria.diverse.cloning.cloner.common.MetamodelTags;
// Default genmodel : org.eclipse.emf.ecore.impl.MinimalEObjectImpl$Container
/**
* Defines an EMF object that can be contained in multiple objects, as long as these objects are in different
* ResourceSets. Yet, it keeps a single "main" container, at all time (eContainer).
*
* @author ebousse
*
*/
public abstract class AbstractShareableEObject extends MinimalEObjectImpl.Container {// implements ShareableEObject {
/**
* Internal class to store the context temporarily
*
* @author ebousse
*
*/
private class ReadonlyContext {
// storage place for the main container in the main resourceset
protected InternalEObject storedMainContainer;
// storage place for the main resource
protected Resource storedResource;
public ReadonlyContext(AbstractShareableEObject o) {
this.storedMainContainer = (InternalEObject) o.eContainer();
this.storedResource = o.eResource();
}
public InternalEObject getContainer() {
return storedMainContainer;
}
public Resource getResource() {
return storedResource;
}
}
/**
* All the containers of the object that are in a clone model.
*/
protected Map<ResourceSet, Object> additionnalContainersOrResources;
/**
* Maps are initialized only at first usage, in order to save some memory (despite)
*/
protected AbstractShareableEObject() {
super();
}
/**
* Simple util operation to find the resource set of an object.
*
* @param o
* The object to analyze.
* @return The resource set of o, or null if there is none.
*/
private static ResourceSet findResourceSet(InternalEObject o) {
if (o.eResource() != null)
return o.eResource().getResourceSet();
else
return null;
}
private static Resource findResource(InternalEObject o) {
if (o.eResource() != null)
return o.eResource();
else if (o.eContainer() != null)
return findResource((InternalEObject) o.eContainer());
else
return null;
}
/**
* Switch to the context of another object.
*
* @param otherEnd
* @return
*/
protected ReadonlyContext changeContext(InternalEObject otherEnd) {
// We find the resource set we are located in
ResourceSet currentRS = findResourceSet(this);
ResourceSet otherRS = findResourceSet(otherEnd);
Resource otherResource = findResource(otherEnd);
// We must only change the context if the otherEnd is in a LooseResource
// in another ResourceSet
// AND if we are considered readonly by the cloning technique of the LooseResource
boolean toChange = (otherResource != null)
&& (otherResource instanceof LooseResource)
&& (((LooseResource) (otherResource)).getTags().getTagOf(this.eClass()) == ClassTag.COMPLETELY_SHAREABLE)
&& (otherRS != null) && (otherRS != currentRS);
// If we do change the context
if (toChange) {
// We store the context
ReadonlyContext context = new ReadonlyContext(this);
// We get the resource set in which "things happen"
ResourceSet otherResourceSet = otherEnd.eResource().getResourceSet();
// We initialize the maps at the first usage
// if (this.additionnalContainersOrResources == null) {
// additionnalContainersOrResources = new HashMap<ResourceSet, Object>(1);
// }
// If we don't have entries for the otherResourceSet,
// we create ones with 'null' (ie not contained in this resource yet)
// if (!this.additionnalContainersOrResources.containsKey(otherResourceSet)) {
// this.additionnalContainersOrResources.put(otherResourceSet, null);
// }
// We remove ourselves from a resource
if (this.eResource() != null)
this.eResource().getContents().remove(this);
// We temporarily change the container (which might of course erase
// the resource)
Object container;
if (this.additionnalContainersOrResources == null)
container = null;
else
container = this.additionnalContainersOrResources.get(otherResourceSet);
if (container != null) {
if (container instanceof InternalEObject) {
this.eBasicSetContainer((InternalEObject) container);
}
if (container instanceof Resource) {
((Resource) container).getContents().add(this);
}
} else {
this.eBasicSetContainer(null);
this.setResourceNonInverse(null);
}
/*
* InternalEObject otherCont = (InternalEObject)
* this.additionnalContainersAndResources.get(otherResourceSet)[0];
*
*
* // We temporarily change the resource, if the container was null if (otherCont == null) { Resource
* otherResource = (Resource) this.additionnalContainersAndResources.get(otherResourceSet)[1]; if
* (otherResource != null) otherResource.getContents().add(this); }
*/
// And we return the context for further restore
return context;
}
// If we don't change the context, we explicitly return null
else {
return null;
}
}
/**
* Restore a previously stored context.
*
* @param context
*/
protected void restoreContext(ReadonlyContext context, InternalEObject otherEnd) {
// If null, nothing to restore
if (context != null) {
// We find the resource set we are located in
ResourceSet currentResourceSet = findResourceSet(this);
boolean storeContainer = true;
Resource otherResource = findResource(otherEnd);
MetamodelTags tags = null;
if (otherResource != null && otherResource instanceof LooseResource)
tags = ((LooseResource) (otherResource)).getTags();
if (tags != null && tags.isPropertyShareable(this.eContainmentFeature())) {
storeContainer = false;
}
if (storeContainer) {
// We initialize the maps at the first usage
if (this.additionnalContainersOrResources == null) {
additionnalContainersOrResources = new HashMap<ResourceSet, Object>(1);
}
// We store the current context in our map
if (this.eContainer != null)
this.additionnalContainersOrResources.put(currentResourceSet, this.eContainer);
else
this.additionnalContainersOrResources.put(currentResourceSet, this.eResource());
}
// And we restore the context
if (this.eResource() != null)
this.eResource().getContents().remove(this);
this.eBasicSetContainer(context.getContainer());
if (context.getContainer() == null && context.getResource() != null)
context.getResource().getContents().add(this);
}
}
@Override
/*
* (non-Javadoc) Appelé lorsqu'un B (this) est ajouté à un A (otherEnd)
*
* @see org.eclipse.emf.ecore.impl.BasicEObjectImpl#eInverseAdd(org.eclipse.emf .ecore.InternalEObject, int,
* java.lang.Class, org.eclipse.emf.common.notify.NotificationChain)
*/
public NotificationChain eInverseAdd(InternalEObject otherEnd, int featureID, Class<?> baseClass,
NotificationChain msgs) {
// Changing the context
ReadonlyContext context = changeContext(otherEnd);
// Actual call
NotificationChain result = super.eInverseAdd(otherEnd, featureID, baseClass, msgs);
// Restoring the context
restoreContext(context, otherEnd);
// And finally we return the NotificationChain
return result;
}
@Override
/*
* (non-Javadoc) Appelé lorsqu'un B (this) est retiré d'un A (otherEnd)
*
* @see org.eclipse.emf.ecore.impl.BasicEObjectImpl#eInverseRemove(org.eclipse .emf.ecore.InternalEObject, int,
* java.lang.Class, org.eclipse.emf.common.notify.NotificationChain)
*/
public NotificationChain eInverseRemove(InternalEObject otherEnd, int featureID, Class<?> baseClass,
NotificationChain msgs) {
// Changing the context
ReadonlyContext context = changeContext(otherEnd);
// Actual call
NotificationChain result = super.eInverseRemove(otherEnd, featureID, baseClass, msgs);
ResourceSet currentRS = findResourceSet(this);
Resource otherResource = findResource(otherEnd);
MetamodelTags tags = null;
if (otherResource != null && otherResource instanceof LooseResource)
tags = ((LooseResource) (otherResource)).getTags();
if (tags != null && tags.isPropertyShareable(this.eContainmentFeature())) {
this.additionnalContainersOrResources.remove(currentRS);
if (this.additionnalContainersOrResources.size() == 0)
this.additionnalContainersOrResources = null;
}
// Restoring the context
restoreContext(context, otherEnd);
// And finally we return the NotificationChain
return result;
}
/**
* Added method that allow one to change the resource of the object without changing what resources think they
* contain. Created for the "save" method of LooseResource, that must briefly change the resources of the elements
* of a model for correct referencing between resources.
*
* @param resource
* The resource to set in the object. The resource won't be changed.
*/
public void setResourceNonInverse(Internal resource) {
eSetDirectResource(resource);
}
// @Override
// public Object getContainerOrResourceInContext(ResourceSet context) {
// if (findResourceSet(this) == context)
// return this.eContainer();
// else if (this.additionnalContainersOrResources.containsKey(context))
// return this.additionnalContainersOrResources.get(context);
// else
// return null;
// }
}
package fr.inria.diverse.cloning.cloner.emfextension.impl;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.util.EcoreUtil.Copier;
import org.eclipse.emf.ecore.util.InternalEList;
/**
* Customized Copier that will ignore containment relationships and treat them the same way as simple references.
*
*
* @author ebousse
*
*/
public class LooseCopier extends Copier {
private static final long serialVersionUID = -3587653043296819827L;
private Set<EReference> context;
/**
* Temporarily change the EClass of some EObject by setting its containments as simple references.
*
* @param o
* The considered EObject.
* @return The stored context, that is the list of the changed ERefences.
*/
private Set<EReference> removeContainmentsFromClassOfObject(EObject o) {
Set<EReference> context = new HashSet<EReference>();
for (EReference r : o.eClass().getEAllContainments()) {
context.add(r);
r.setContainment(false);
}
return context;
}
/**
* Restores the contaiments of some references.
*
* @param context
* The list of references.
*/
private void restoreContainments(Set<EReference> context) {
for (EReference r : context)
r.setContainment(true);
}
// ------------------
@Override
/*
* (non-Javadoc)
*
* @see org.eclipse.emf.ecore.util.EcoreUtil.Copier#copy(org.eclipse.emf.ecore .EObject)
*/
public EObject copy(EObject eObject) {
// Saving context (ie. what are the containments of the class of the copied objects)
// and changing it (ie. removing all containments)
Set<EReference> context = removeContainmentsFromClassOfObject(eObject);
// Calling copier copy code
EObject result = super.copy(eObject);
// Restoring context
restoreContainments(context);
// And returning result
return result;
}
@Override
public void copyReferences() {
// Saving context (ie. what are the containments of the _classes_ of the copied objects)
// and changing it (ie. removing all containments)
context = new HashSet<EReference>();
// Set<EReference> context = new HashSet<EReference>();
for (EObject o : this.keySet())
context.addAll(removeContainmentsFromClassOfObject(o));
// Calling copier copyReferences code
super.copyReferences();
// Restoring context
restoreContainments(context);
}
@SuppressWarnings("unchecked")
@Override
protected void copyReference(EReference eReference, EObject eObject, EObject copyEObject) {
boolean ok = true;
// Little hack: we avoid to try to copy from the same collection in the same collection
// This might happen with "light" cloning, as the clone object will return the same collection as the cloned
// And EMF copier does not handle that correctly :)
if (eObject.eIsSet(eReference)) {
if (eReference.isMany()) {
InternalEList<EObject> source = (InternalEList<EObject>) eObject.eGet(eReference);
InternalEList<EObject> targetMany = (InternalEList<EObject>) copyEObject.eGet(getTarget(eReference));
if (source == targetMany) {
ok = false;
}
}
}
// We make the copy... if not same collection
if (ok)
super.copyReference(eReference, eObject, copyEObject);
}
}
package fr.inria.diverse.cloning.cloner.emfextension.impl;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl;
import fr.inria.diverse.cloning.cloner.common.ClassTag;
import fr.inria.diverse.cloning.cloner.common.MetamodelTags;
public class LooseResource extends XMIResourceImpl {
/**
* A LooseResource considers readonly classes that a given cloning technique
* consider readonly. Thus it needs to know ther cloner that created it.
*/
private MetamodelTags tags;
/**
* The default constructor is private, because such resources can only be
* constructed with tags, otherwise invalid.
*/
private LooseResource() {};
/**
* Constructs a LooseResource to build a clone made by a specific cloning
* technique.
*
* @param c
* The cloning technique tagger.
*/
public LooseResource(MetamodelTags t) {
this();
this.tags = t;
}
/**
* We might need to know the cloner, in order to know what objects are
* readonly for this resource.
*