Commit cf54973e authored by Erwan Bousse's avatar Erwan Bousse

First commit

parents
package fr.diverse.fancyemfcloning.generator;
public class EMFImpl2PImpl {
}
package fr.diverse.fancyemfcloning.generator;
public class FactoryGenerator {
}
package fr.diverse.fancyemfcloning.tagger;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
public abstract class AbstractMetamodelTagger {
/**
* Final result of the algorithm, with a tag for each class concerning its
* "mutability".
*/
protected Map<EClass, ClassTag> tags;
public AbstractMetamodelTagger(Set<Resource> metamodel) {
tags = new HashMap<EClass,ClassTag>();
// Finding all classes of the metamodel
for (Resource resource : metamodel)
for (Iterator<EObject> i = resource.getAllContents(); i.hasNext();) {
EObject current = i.next();
if (current instanceof EClass)
tags.put((EClass) current,null);
}
recomputeTags();
}
protected boolean isMutable(EClass c) {
return (tags.get(c) == ClassTag.completelyMutable)
|| (tags.get(c) == ClassTag.partiallyMutable)
|| (tags.get(c) == ClassTag.canReachMutable);
}
protected static boolean isPropertyMutable(EStructuralFeature f) {
return f.getName().contains("_m");
}
/*protected static Set<EClass> findAllLoadedClasses() {
Set<EClass> allLoadedEClasses = new HashSet<EClass>();
for (Object o : EPackage.Registry.INSTANCE.values()) {
EPackage p = (EPackage) o;
if (p.getNsURI() != "http://www.eclipse.org/emf/2002/Ecore") {
for (EClassifier c : p.getEClassifiers()) {
if (c instanceof EClass)
allLoadedEClasses.add((EClass) c);
}
}
}
return allLoadedEClasses;
}*/
/**
* Without looking to the classes that can be reached from a class,
* determine how mutable it is (locally).
*
* @param c
* The class to analyze.
* @return ClassMutability.partiallyMutableOrCanReachMutable if the class
* has both mutable and readonly properties,
* ClassMutability.completelyMutable if the class only has mutable
* properties, ClassMutability.completelyReadonly if the class only
* has readonly properties.
*/
protected static ClassTag findLocalMutability(EClass c) {
boolean hasMutables = false;
boolean hasReadonlys = false;
for (EStructuralFeature f : c.getEAllStructuralFeatures()) {
if (isPropertyMutable(f))
hasMutables = true;
else
hasReadonlys = true;
}
if (hasMutables && hasReadonlys)
return ClassTag.partiallyMutable;
else if (hasMutables && !hasReadonlys)
return ClassTag.completelyMutable;
else
// only readonlys OR no properties at all
return ClassTag.completelyReadonly;
}
/*
* protected static Set<Set<Resource>> findAllLoadedMetamodels() {
* Set<Set<Resource>> result = new HashSet<Set<Resource>>();
*
* // For each loaded epackage (excepted ecore itself) for (Object o :
* EPackage.Registry.INSTANCE.values()) { EPackage p = (EPackage)o; if
* (p.getNsURI()!="http://www.eclipse.org/emf/2002/Ecore") {
*
*
* // We find or create the metamodel for this package Set<Resource>
* pMetamodel = null; for (Set<Resource> metamodel : result) { if
* (metamodel.contains(p.eResource())) { pMetamodel = metamodel; break; } }
* if (pMetamodel == null){ pMetamodel = new HashSet<Resource>();
* pMetamodel.add(p.eResource()); result.add(pMetamodel); }
*
*
*
* // We look at the content (classes) for(EClassifier c :
* p.getEClassifiers()) { if (c instanceof EClass) { // For each class, we
* check that it does try to access to another resource // If it does, we
* add the resource of the class to the metamodel for (EReference r :
* ((EClass)c).getEAllReferences()) {
* pMetamodel.add(r.getEReferenceType().eResource()); } } } } }
*
* System.out.println("Found metamodels"); return result; }
*/
public ClassTag getTagOfEClass(EClass c) {
if (tags.containsKey(c)) {
if (tags.get(c)==null)
System.out.println("The class " + c.getName()
+ " has no tag! Tagging problem?");
return tags.get(c);
}
else {
System.out.println("The class " + c.getName()
+ " is not in the registered metamodel of the cloner!");
return null;
}
}
public abstract void recomputeTags();
public abstract boolean mayTagClassesReadonly();
}
\ No newline at end of file
package fr.diverse.fancyemfcloning.tagger;
/**
* From a cloning kind point of view a class is either
* - completely readonly (no property change change), thus no need to clone it
* - completely mutable (all properties can change), thus must be cloned completely
* - partially mutable (some properties can change) OR can reach a mutable , thus can be cloned partially or completely
* @author ebousse
*
*/
public enum ClassTag {
completelyReadonly,
completelyMutable,
partiallyMutable,
canReachMutable,
}
\ No newline at end of file
package fr.diverse.fancyemfcloning.tagger;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.Resource;
/**
* This static class allows to tag all the EClasses of a metamodel in order to determine how it can eventaully be
* cloned, for a normal MutableClassesOnly clone.
*
* @author ebousse
*
*/
public class MutClassesOnlyMetamodelTagger extends AbstractMetamodelTagger {
public MutClassesOnlyMetamodelTagger(Set<Resource> metamodel) {
super(metamodel);
}
/**
* Very simple class used to model sccs, for clarity.
*
* @author ebousse
*
*/
private class StronglyConnectedComponent {
private Set<EClass> classes;
public StronglyConnectedComponent() {
classes = new HashSet<EClass>();
}
}
// Tarjan variables
private Map<EClass, Integer> tarjanIndexes;
private Map<EClass, Integer> tarjanLowlinks;
private Stack<EClass> tarjanS;
private int tarjanIndex;
private Set<StronglyConnectedComponent> tarjanResult;
/**
* From https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorith Main operation that
* implements the main operation of the tarjan algorithm. Computes StronglyConnectedComponents, and tags classes in
* each as soon as they are computed.
*
* Note : should we provide a quicker algorithm for tree-shaped metamodels ?
*
* @param vertices
* the EClasses to consider
*/
private void tarjanWithTagging(Set<EClass> vertices) {
tarjanIndexes = new HashMap<EClass, Integer>();
tarjanLowlinks = new HashMap<EClass, Integer>();
tarjanS = new Stack<EClass>();
tarjanIndex = 0;
tarjanResult = new HashSet<StronglyConnectedComponent>();
for (EClass v : vertices) {
if (!tarjanIndexes.containsKey(v)) {
tarjanWithTaggingOnVertex(v);
}
}
}
/**
* Main code of the tarjan algorithm.
*
* @param v
*/
private void tarjanWithTaggingOnVertex(EClass v) {
// Set the depth index for v to the smallest unused index
tarjanIndexes.put(v, tarjanIndex);
tarjanLowlinks.put(v, tarjanIndex);
tarjanIndex = tarjanIndex + 1;
tarjanS.push(v);
// Computing successors
Set<EClass> successors = new HashSet<EClass>();
for (EReference ref : v.getEAllReferences())
successors.add(ref.getEReferenceType());
// Consider successors of v
for (EClass w : successors) {
if (!tarjanIndexes.containsKey(w)) {
// Successor w has not yet been visited; recurse on it
tarjanWithTaggingOnVertex(w);
tarjanLowlinks.put(v, Math.min(tarjanLowlinks.get(v), tarjanLowlinks.get(w)));
} else if (tarjanS.contains(w)) {
// Successor w is in stack S and hence in the current SCC
tarjanLowlinks.put(v, Math.min(tarjanLowlinks.get(v), tarjanIndexes.get(w)));
}
}
// If v is a root node, pop the stack and generate an SCC
if (tarjanLowlinks.get(v) == tarjanIndexes.get(v)) {
StronglyConnectedComponent scc = new StronglyConnectedComponent();
EClass w;
do {
w = tarjanS.pop();
scc.classes.add(w);
} while (w != v);
// BONUS (only part not in Tarjan): computing tags
// Possible because Tarjan guarantees that
// "no strongly connected component will be identified before any of its successors."
computeMutabilityOfScc(scc);
// End BONUS
tarjanResult.add(scc);
}
}
/**
* To me used as soon as some SCC is found by the Tarjan algorithm. Will iterate over all classes of the SCC, and
* tags them for mutability.
*
* @param scc
* A StronglyConnectedComponent freshly computed in Tarjan.
*/
private void computeMutabilityOfScc(StronglyConnectedComponent scc) {
Set<EClass> roWaitingForResult = new HashSet<EClass>();
// Do we know yet if we can reach a mutable somewhere from th scc ?
boolean hasReachableMutable = false;
// We look at all classes if the scc
for (EClass c : scc.classes) {
// First, if still no mutable found, we look for outgoing edges of the scc
if (!hasReachableMutable) {
for (EReference ref : c.getEAllReferences()) {
EClass reachableClass = ref.getEReferenceType();
// If the reached class is not from the current scc AND
// is mutable, then our scc can reach a mutable
if (!scc.classes.contains(reachableClass))
hasReachableMutable = isMutable(reachableClass);
// If we found that we reach a mutable, no need to
// search more
if (hasReachableMutable)
break;
}
}
// Then we compute locally what we can
ClassTag localMut = findLocalMutability(c);
switch (localMut) {
case partiallyMutable:
hasReachableMutable = true;
tags.put(c, ClassTag.partiallyMutable);
break;
case completelyMutable:
hasReachableMutable = true;
tags.put(c, ClassTag.completelyMutable);
break;
case completelyReadonly:
if (hasReachableMutable)
tags.put(c, ClassTag.canReachMutable);
else
roWaitingForResult.add(c);
break;
}
}
// And finally we compute the readonlys waiting to know if a mut can
// be reached
for (EClass c : roWaitingForResult) {
if (hasReachableMutable)
tags.put(c, ClassTag.canReachMutable);
else
tags.put(c, ClassTag.completelyReadonly);
}
}
@Override
public void recomputeTags() {
tarjanWithTagging(tags.keySet());
System.out.println("Computed mutabilities");
for (EClass c : tags.keySet()) {
System.out.println(c.getName() + " : " + tags.get(c));
}
}
@Override
public boolean mayTagClassesReadonly() {
return true;
}
}
package fr.diverse.fancyemfcloning.tagger;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.Resource;
public class WeakMutClassesOnlyMetamodelTagger extends AbstractMetamodelTagger {
public WeakMutClassesOnlyMetamodelTagger(Set<Resource> metamodel) {
super(metamodel);
}
public static class NoSingleRootException extends Exception {
private static final long serialVersionUID = 1L;
private Set<EClass> roots;
public NoSingleRootException(Set<EClass> foundRoots) {
roots = new HashSet<EClass>();
roots.addAll(foundRoots);
}
@Override
public String getMessage() {
String message = "A single root is required. Roots found: ";
for (EClass aClass : roots)
message += aClass.getName() + ", ";
return message.substring(0, message.length() - 2) + "\n" + super.getMessage();
}
}
private EClass findRoot() throws NoSingleRootException {
// Preparing result set
Set<EClass> roots = new HashSet<EClass>();
// Finding all contained classes
Set<EClass> containedClasses = new HashSet<EClass>();
for (EClass aClass : tags.keySet())
for (EReference r : aClass.getEAllContainments())
(containedClasses).add(r.getEReferenceType());
// Finding root classes, ie not contained classes
for (EClass aClass : tags.keySet())
if (!containedClasses.contains(aClass))
roots.add(aClass);
// If there are multiple roots, then we have a problem
if (roots.size() != 1)
throw new NoSingleRootException(roots);
return roots.iterator().next();
}
/**
* Tags a containment tree of EClasses.
*
* @param root
* The root EClass of the containment tree.
* @return True if this (sub)tree has a mutable. Required for the recursive calls.
* @throws NoSingleRootException
*/
private boolean treeTagging(EClass root) throws NoSingleRootException {
// Is this class mutable or can reach a mutable through containment ?
// At first, false
boolean hasMutable = false;
// We first make recursive calls looking for mutables at lower levels
// If some contained classes are mutable, we change hasMutable
for (EReference cont : root.getEAllContainments()) {
hasMutable = hasMutable || treeTagging(cont.getEReferenceType());
}
// Then depending on the local attributes, we tag the current class
ClassTag localMut = findLocalMutability(root);
switch (localMut) {
case partiallyMutable:
hasMutable = true;
tags.put(root, ClassTag.partiallyMutable);
break;
case completelyMutable:
hasMutable = true;
tags.put(root, ClassTag.completelyMutable);
break;
case completelyReadonly:
if (hasMutable)
tags.put(root, ClassTag.canReachMutable);
else
tags.put(root, ClassTag.completelyReadonly);
break;
}
// And we return true if the class is considered mutable
return hasMutable;
}
@Override
public void recomputeTags() {
try {
treeTagging(findRoot());
} catch (NoSingleRootException e) {
System.out.println(e.getMessage());
System.out.println("No tagging possible for WeakMutClassesOnlyMetamodelTagger.");
}
System.out.println("Computed mutabilities:");
if (tags.size() == 0)
System.out.println("No mutable class!");
for (EClass c : tags.keySet()) {
System.out.println(c.getName() + " : " + tags.get(c));
}
}
@Override
public boolean mayTagClassesReadonly() {
// TODO Stub de la méthode généré automatiquement
return true;
}
}
package fr.diverse.fancyemfcloning.cloner;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.resource.ResourceSet;
import fr.diverse.fancyemfcloning.customemf.ClassTag;
/**
* Defines a class in which a cloning technique is implemented.
* @author ebousse
*
*/
public interface Cloner {
/**
* Considering a model stored in a ResourceSet, computes a clone of the model.
* @param model The model to clone.
* @param folder The folder in which to store the model to clone (at least in its URI).
* @return The cloned model.
*/
public ResourceSet clone(ResourceSet model, String folder);
public ClassTag getTagOf(EClass eClass);
//public ClassTag getTagOf(EClass c);
}
package fr.diverse.fancyemfcloning.cloner.impl;
import java.util.Iterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import fr.diverse.fancyemfcloning.cloner.Cloner;
import fr.diverse.fancyemfcloning.customemf.ClassTag;
import fr.diverse.fancyemfcloning.customemf.impl.LooseCopier;
import fr.diverse.fancyemfcloning.customemf.impl.LooseResource;
import fr.diverse.fancyemfcloning.tagger.AbstractMetamodelTagger;
/**
* Self containing class with the cloning algorithm.
*
* @author ebousse
*
*/
public class ClonerImpl implements Cloner {
protected AbstractMetamodelTagger tagger;
public ClonerImpl(AbstractMetamodelTagger tagger) {
this.tagger = tagger;
}
@Override
public ResourceSet clone(ResourceSet cloned, String folder) {
// Creating empty clone resource set
ResourceSet clone = new ResourceSetImpl();
// Preparing the copier, which will allow us to copy objects in new
// resources AND to resolve the references in the clone (even between resources)
EcoreUtil.Copier copier = new LooseCopier();// new EcoreUtil.Copier();
// Cloning all resources
for (Resource r : cloned.getResources()) {
// Getting the complete file name
String filename = r.getURI().segmentsList().get(r.getURI().segmentsList().size() - 1);
// Preparing the URI of the new resource (with a specific 'folder')
URI uri = r.getURI().trimSegments(1).appendSegment(folder).appendSegment(filename);
// Creating the new Resource...
Resource resourceClone = null;
// LooseResource if objects might be shared
if (tagger.mayTagClassesReadonly()) {
resourceClone = new LooseResource(this);
resourceClone.setURI(uri);
clone.getResources().add(resourceClone);
}
// Or regular Resource, if not
else
resourceClone = clone.createResource(uri);
// Copying objects (but not the references, and only tagged mutable ones)
for (Iterator<EObject> i = r.getAllContents(); i.hasNext();) {
EObject o = i.next();
ClassTag tag = tagger.getTagOfEClass(o.eClass());
if (tag == ClassTag.canReachMutable || tag == ClassTag.completelyMutable
|| tag == ClassTag.partiallyMutable) {
resourceClone.getContents().add(copier.copy(o));
} else {
resourceClone.getContents().add(o);
}
}
}
// Resolving references (all of them, even between resources of the clone and
// between the clone and the cloned)
copier.copyReferences();
// Returning the obtained clone
return clone;
}