Commit cf54973e authored by Erwan Bousse's avatar Erwan Bousse
Browse files

First commit

parents
package fr.diverse.fancyemfcloning.generator;
public class EMFImpl2PImpl {
}
package fr.diverse.fancyemfcloning.generator;
public class FactoryGenerator {
}
package fr.diverse.fancyemfcloning.main;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.CopyOption;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage.Registry;
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 org.eclipse.emf.ecore.util.EcoreUtil.Copier;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
import org.eclipse.gmt.modisco.java.AbstractTypeDeclaration;
import org.eclipse.gmt.modisco.java.BodyDeclaration;
import org.eclipse.gmt.modisco.java.ClassDeclaration;
import org.eclipse.gmt.modisco.java.CompilationUnit;
import org.eclipse.gmt.modisco.java.ConstructorDeclaration;
import org.eclipse.gmt.modisco.java.ExpressionStatement;
import org.eclipse.gmt.modisco.java.FieldDeclaration;
import org.eclipse.gmt.modisco.java.InterfaceDeclaration;
import org.eclipse.gmt.modisco.java.MethodDeclaration;
import org.eclipse.gmt.modisco.java.MethodInvocation;
import org.eclipse.gmt.modisco.java.Model;
import org.eclipse.gmt.modisco.java.Modifier;
import org.eclipse.gmt.modisco.java.ReturnStatement;
import org.eclipse.gmt.modisco.java.SingleVariableAccess;
import org.eclipse.gmt.modisco.java.Statement;
import org.eclipse.gmt.modisco.java.StringLiteral;
import org.eclipse.gmt.modisco.java.TypeAccess;
import org.eclipse.gmt.modisco.java.VariableDeclaration;
import org.eclipse.gmt.modisco.java.VariableDeclarationFragment;
import org.eclipse.gmt.modisco.java.VisibilityKind;
import org.eclipse.gmt.modisco.java.emf.JavaFactory;
import org.eclipse.gmt.modisco.java.generation.files.GenerateJavaExtended;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.modisco.java.discoverer.DiscoverJavaModelFromJavaProject;
//import org.eclipse.gmt.modisco.java.;
/**
* This class controls all aspects of the application's execution
*/
public class Application implements IApplication {
/**
* To decide whether a property is mutable or not.
* Only relies on its name, which should end with "_m" or "_M_EDEFAULT" for default values.
* @param variable The variable to analyse
* @return True if mutable, false otherwise.
*/
private static boolean isMutable(VariableDeclarationFragment variable) {
return variable.getName().endsWith("_m") || variable.getName().endsWith("_M_EDEFAULT");
}
/**
* Helper method that allows one to find the method that contain some model element.
* @param o The model element.
* @return The method containing the model element.
*/
private static MethodDeclaration getContainingMethod(EObject o) {
if (o.eContainer() != null) {
if (o.eContainer() instanceof MethodDeclaration)
return (MethodDeclaration) o.eContainer();
else
return getContainingMethod(o.eContainer());
} else {
return null;
}
}
/**
* Helper method to create "System.err.println(message)" calls in the code.
* Creates a fake System class, a fake err static property, and a fake printlnt method.
* Then creates the call itself and return it.
* @param message The message that should be printed by the call.
* @return The method call.
*/
private ExpressionStatement createPrintErr(String message) {
// Preparing the factory
JavaFactory javaFactory = (JavaFactory) Registry.INSTANCE
.getEFactory("http://www.eclipse.org/MoDisco/Java/0.2.incubation/java");
// Fake "err" static field of System
FieldDeclaration errField = javaFactory.createFieldDeclaration();
Modifier errModifier = javaFactory.createModifier();
errModifier.setStatic(true);
errField.setModifier(errModifier);
VariableDeclarationFragment err = javaFactory.createVariableDeclarationFragment();
err.setName("err");
errField.getFragments().add(err);
// Fake println method
MethodDeclaration println = javaFactory.createMethodDeclaration();
println.setName("println");
// Argument for the call, which is the message
StringLiteral arg = javaFactory.createStringLiteral();
arg.setEscapedValue("\"" + message + "\"");
// Fake System class
ClassDeclaration system = javaFactory.createClassDeclaration();
system.setName("System");
system.getBodyDeclarations().add(println);
system.getBodyDeclarations().add(errField);
// To access to the system class
TypeAccess systemAccess = javaFactory.createTypeAccess();
systemAccess.setType(system);
// To access to the "err" field
SingleVariableAccess erraccess = javaFactory.createSingleVariableAccess();
erraccess.setVariable(err);
erraccess.setQualifier(systemAccess);
// Invocation of the fake println method
MethodInvocation printlncall = javaFactory.createMethodInvocation();
printlncall.setMethod(println);
printlncall.setExpression(erraccess);
printlncall.getArguments().add(arg);
// Finally creating the expression result, containing the invocation of println
ExpressionStatement result = javaFactory.createExpressionStatement();
result.setExpression(printlncall);
// And returning it
return result;
}
/**
* Helper method to find the getter of some property in the original model.
* For instance: in APImpl the attribute a would return the getA() method of AImpl.
* @param variable The property for which we need the original getter.
* @param copier The copier that produced the java model clone.
* @return
*/
private MethodDeclaration findGetterInImpl(VariableDeclarationFragment variable, EcoreUtil.Copier copier) {
VariableDeclarationFragment realvariable = (VariableDeclarationFragment) copier.get(variable);
for (SingleVariableAccess access : realvariable.getUsageInVariableAccess()) {
if (access.eContainer() instanceof ReturnStatement) {
ReturnStatement ret = (ReturnStatement) access.eContainer();
if (ret.eContainer().eContainer() instanceof MethodDeclaration) {
MethodDeclaration potentialGetter = (MethodDeclaration) ret.eContainer().eContainer();
if (potentialGetter.getName().startsWith("get"))
return potentialGetter;
}
}
}
return null;
}
/**
* Main transformation method.
*
* @param classd_M_EDEFAULT
* @param copier
*/
private void processClass(ClassDeclaration classd, Copier copier) {
// Logging
System.out.println(classd.getOriginalCompilationUnit().getOriginalFilePath() + "##" + classd.getName());
// Renaming the class
String originalName = classd.getName();
classd.setName(classd.getName().substring(0, classd.getName().length() - 4) + "PClone");
classd.getOriginalCompilationUnit().setName(classd.getName() + ".java");
// Preparing the factory
JavaFactory javaFactory = (JavaFactory) Registry.INSTANCE
.getEFactory("http://www.eclipse.org/MoDisco/Java/0.2.incubation/java");
// Creating the "cloned" field...
// Added later to the class
// Maybe we should find the type in the original model ?
FieldDeclaration clonedField = javaFactory.createFieldDeclaration();
// We construct an access to the "XImpl" type
TypeAccess clonedFieldTypeaccess = javaFactory.createTypeAccess();
InterfaceDeclaration interfaced = (InterfaceDeclaration) classd.getSuperInterfaces().get(0).getType();
clonedFieldTypeaccess.setType(interfaced);
VariableDeclarationFragment clonedVar = javaFactory.createVariableDeclarationFragment();
clonedVar.setName("cloned");
clonedVar.setExtraArrayDimensions(0);
clonedVar.setProxy(false);
clonedVar.setVariablesContainer(clonedField);
clonedVar.setOriginalCompilationUnit(classd.getOriginalCompilationUnit());
Modifier clonedFieldModifier = javaFactory.createModifier();
clonedFieldModifier.setStatic(false);
clonedFieldModifier.setVisibility(VisibilityKind.PROTECTED);
clonedField.setType(clonedFieldTypeaccess);
clonedField.setModifier(clonedFieldModifier);
// Preparing a set of the body declarations to remove
Set<BodyDeclaration> emptyBodies = new HashSet<BodyDeclaration>();
// First pass: analysis and processing the fields
for (BodyDeclaration bodyd : classd.getBodyDeclarations()) {
// If it is a field
if (bodyd instanceof FieldDeclaration) {
FieldDeclaration fieldd = (FieldDeclaration) bodyd;
// If it is not static
if (!fieldd.getModifier().isStatic()) {
// We prepare the set of mutable variables (to remove
// eventually)
Set<VariableDeclarationFragment> localReadonlys = new HashSet<VariableDeclarationFragment>();
// And we iterate through the variables
for (VariableDeclarationFragment variabled : fieldd.getFragments()) {
//TODO Case readonly "reference to a mutable class" !!!
// Relire KMF stuff ?
// If it is readonly
if (!isMutable(variabled)) {
System.out.println("Found readonly attribute! " + variabled.getName());
// We will remove it eventually
localReadonlys.add(variabled);
// We iterate through its usages
for (SingleVariableAccess access : variabled.getUsageInVariableAccess()) {
// Getting the method that contains this write
MethodDeclaration containingMethod = getContainingMethod(access);
if (containingMethod != null) {
// case WRITE (setters)
if (containingMethod.getName().startsWith("basicSet")
|| containingMethod.getName().startsWith("set")) {
System.out.println("WRITE");
// If we are, we clear its content
containingMethod.getBody().getStatements().clear();
// and we replace it with some error printing
ExpressionStatement printExpr = createPrintErr(classd.getName() + ": "
+ variabled.getName() + " is readonly and cannot be set.");
printExpr.setOriginalCompilationUnit(classd.getOriginalCompilationUnit());
containingMethod.getBody().getStatements().add(printExpr);
// If in a basicSet, we have to return the NotificationChain
if (containingMethod.getName().startsWith("basicSet")) {
SingleVariableAccess parameterAccess = javaFactory
.createSingleVariableAccess();
parameterAccess.setVariable(containingMethod.getParameters().get(1));
ReturnStatement ret = javaFactory.createReturnStatement();
ret.setExpression(parameterAccess);
containingMethod.getBody().getStatements().add(ret);
}
}
// case READ
else {
System.out.println("READ");
if (containingMethod != null) {
// If we are in a getter, we remove everything except the return
// (i.e. we remove everything proxy related)
if (containingMethod.getName().startsWith("basicGet")
|| containingMethod.getName().startsWith("get")) {
EList<Statement> statements = new BasicEList<Statement>();
statements.addAll(containingMethod.getBody().getStatements());
for (Statement s : statements)
if (!(s instanceof ReturnStatement))
containingMethod.getBody().getStatements().remove(s);
}
MethodDeclaration getter = findGetterInImpl(variabled, copier);
if (getter != null) {
// Creating the access to the cloned variable
SingleVariableAccess clonedAccess = javaFactory
.createSingleVariableAccess();
clonedAccess.setVariable(clonedVar);
// Creating the call to the getter
MethodInvocation getCall = javaFactory.createMethodInvocation();
getCall.setMethod(getter);
getCall.setExpression(clonedAccess);
// Two cases : contained in a collection or not
// In both, we replace the access by cloned.get
if (access.eContainmentFeature().isMany()) {
EList<EObject> list = (EList<EObject>) access.eContainer().eGet(
access.eContainmentFeature());
list.set(list.indexOf(access), getCall);
} else {
access.eContainer().eSet(access.eContainmentFeature(), getCall);
}
}
}
}
}
}
}
}
// We remove from the field the readonly variables
fieldd.getFragments().removeAll(localReadonlys);
// If the field is empty, it must be removed eventually
if (fieldd.getFragments().isEmpty())
emptyBodies.add(fieldd);
}
}
// Changing constructor
// TODO parameter + body
if (bodyd instanceof ConstructorDeclaration) {
ConstructorDeclaration constructor = (ConstructorDeclaration) bodyd;
constructor.setName(classd.getName());
}
}
// We remove empty fields
classd.getBodyDeclarations().removeAll(emptyBodies);
// Adding cloned field, at last (otherwise it is treated as a RO
// variable etc.)
classd.getBodyDeclarations().add(0, clonedField);
// TODO a protected "getCloned" with proxy resolve ?
}
public Model transform2PImpl(Model javaModel) {
// Copying the obtained model in order to refer to elements of the original one
EcoreUtil.Copier copier = new EcoreUtil.Copier();
Model originalJavaModel = (Model) copier.copy(javaModel);
copier.copyReferences();
// Preparing the set of already visited packages
Set<org.eclipse.gmt.modisco.java.Package> packages = new HashSet<org.eclipse.gmt.modisco.java.Package>();
// Preparing the set of compilation units to remove (ie. those not modified)
Set<CompilationUnit> cuToRemove = new HashSet<CompilationUnit>();
// Then we iterate over compilation units (~ files)
for (CompilationUnit c : javaModel.getCompilationUnits()) {
// Goes to false if the cu is an implementation of our interest
boolean toRemove = true;
// We look for files with "impl"
if (c.getName().contains("Impl")) {
// In which there are classes
for (AbstractTypeDeclaration type : c.getTypes()) {
if (type instanceof ClassDeclaration) {
ClassDeclaration classd = (ClassDeclaration) type;
if (classd.getSuperClass() instanceof TypeAccess) {
TypeAccess superClass = (TypeAccess) classd.getSuperClass();
// If the class extends our custom class "PotentiallyReadonlyEObject", it means its
// the implementation class of some metaclass
if (superClass.getType().getName().equals("PotentiallyReadonlyEObject")) {
// Thus we keep it
toRemove = false;
// We process its package (if not done already)
if (!packages.contains(classd.getPackage())) {
packages.add(classd.getPackage());
classd.getPackage().setName("pclone");
}
// And we process the class
processClass(classd, copier);
}
}
}
}
}
// If we did not find the class in the compilation unit, we can remove it (later)
if (toRemove)
cuToRemove.add(c);
}
// Removing non modified compilations units
javaModel.getCompilationUnits().removeAll(cuToRemove);
return javaModel;
}
/*
*
*
* (non-Javadoc)
*
* @see org.eclipse.equinox.app.IApplication#start(org.eclipse.equinox.app. IApplicationContext)
*/
public Object start(IApplicationContext context) throws Exception {
System.out.println("Starting EMFImpl2PClone");
// Creating a "fake" java project from a real existing eclipse java
// project on the filesystem
IProjectDescription description = ResourcesPlugin.getWorkspace().loadProjectDescription(
new Path("/home/ebousse/Dev/modelCloning/emf/SomeMetamodel/.project"));
IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(description.getName());
project.delete(false, true, null);
project.create(description, null);
project.open(null);
IJavaProject javaProject = JavaCore.create(project);
// Discovering the model from the project
DiscoverJavaModelFromJavaProject discoverer = new DiscoverJavaModelFromJavaProject();
discoverer.discoverElement(javaProject, new NullProgressMonitor());
// Accessing the obtained model
Resource result = discoverer.getTargetModel();
Model javaModel = (Model) result.getContents().get(0);
transform2PImpl(javaModel);
// Generating the code from the obtained model
GenerateJavaExtended javaGenerator = new GenerateJavaExtended(javaModel, new File(
"/home/ebousse/Dev/modelCloning/emf/SomeMetamodel/src"), new ArrayList<Object>());
javaGenerator.doGenerate(null);
// Done !
System.out.println("Done !");
return IApplication.EXIT_OK;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.equinox.app.IApplication#stop()
*/
public void stop() {
// nothing to do
}
}
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,