Commit ce354e76 authored by Erwan Bousse's avatar Erwan Bousse

Major improvement: now inheritance and abstract classes and interfaces are...

Major improvement: now inheritance and abstract classes and interfaces are taken into account. Also error messages are now displayed when generating cloning material
parent eee4fcef
......@@ -30,22 +30,27 @@ import org.eclipse.emf.ecore.util.EcoreUtil.Copier;
import org.eclipse.gmt.modisco.java.AbstractTypeDeclaration;
import org.eclipse.gmt.modisco.java.Assignment;
import org.eclipse.gmt.modisco.java.BodyDeclaration;
import org.eclipse.gmt.modisco.java.CastExpression;
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.Expression;
import org.eclipse.gmt.modisco.java.ExpressionStatement;
import org.eclipse.gmt.modisco.java.FieldDeclaration;
import org.eclipse.gmt.modisco.java.ImportDeclaration;
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.Package;
import org.eclipse.gmt.modisco.java.ParenthesizedExpression;
import org.eclipse.gmt.modisco.java.ReturnStatement;
import org.eclipse.gmt.modisco.java.SingleVariableAccess;
import org.eclipse.gmt.modisco.java.SingleVariableDeclaration;
import org.eclipse.gmt.modisco.java.Statement;
import org.eclipse.gmt.modisco.java.StringLiteral;
import org.eclipse.gmt.modisco.java.SuperConstructorInvocation;
import org.eclipse.gmt.modisco.java.TypeAccess;
import org.eclipse.gmt.modisco.java.VariableDeclaration;
import org.eclipse.gmt.modisco.java.VariableDeclarationFragment;
......@@ -84,14 +89,16 @@ public class EMFImpl2PImpl {
private Model javaModel;
/**
* The cloning tagger to consider. It completely drives the transformation regarding what is mutable or not.
* The cloning tagger to consider. It completely drives the transformation
* regarding what is mutable or not.
*/
private Tagger tagger;
private boolean readonlyWarningsInSetters;
/**
* Retrieves from the tagger the mutability tag of a java class that implements a metaclass.
* Retrieves from the tagger the mutability tag of a java class that
* implements a metaclass.
*
* @param classd
* A java class that implements a metaclass.
......@@ -101,8 +108,23 @@ public class EMFImpl2PImpl {
return getTagOfInterface((InterfaceDeclaration) classd.getSuperInterfaces().get(0).getType());
}
private boolean hasProxySuperClass(ClassDeclaration classd) {
ClassDeclaration javaSuperClass = (ClassDeclaration) classd.getSuperClass().getType();
if (!javaSuperClass.getSuperInterfaces().isEmpty()) {
InterfaceDeclaration javaInterface = (InterfaceDeclaration) javaSuperClass.getSuperInterfaces().get(0)
.getType();
EClass superEClass = classesMapping.get(javaInterface);
if (superEClass != null) {
return (tagger.getTagOfEClass(superEClass) == ClassTag.PARTIALLY_SHAREABLE);
}
}
return false;
}
/**
* Retrieves from the tagger the mutability tag of a java interfaces that corresponds to a metaclass.
* Retrieves from the tagger the mutability tag of a java interfaces that
* corresponds to a metaclass.
*
* @param classd
* A java interfaces that matches a metaclass.
......@@ -113,8 +135,8 @@ public class EMFImpl2PImpl {
}
/**
* Retrieves from the tagger the mutability (mutable or not) of a java property that implements a metaclass
* property.
* Retrieves from the tagger the mutability (mutable or not) of a java
* property that implements a metaclass property.
*
* @param vard
* The java property that implements a metaclass property.
......@@ -125,33 +147,8 @@ public class EMFImpl2PImpl {
}
/**
* 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 boolean isMutable(VariableDeclarationFragment variable) {
*
* boolean pointsToMutable = false; if (variable.getVariablesContainer() instanceof FieldDeclaration) {
* FieldDeclaration fieldd = (FieldDeclaration) variable.getVariablesContainer(); if (fieldd.getType() != null &&
* fieldd.getType().getType() instanceof InterfaceDeclaration) { InterfaceDeclaration interfaced =
* (InterfaceDeclaration) fieldd.getType().getType(); ClassTag tag = getTagOfInterface(interfaced);
*
* switch (tag) { // case canReachMutable: // pointsToMutable = true; case completelyMutable: pointsToMutable =
* true; case completelyReadonly: pointsToMutable = false; case partiallyMutable: pointsToMutable = true; }
*
* }
*
* }
*
* return pointsToMutable || variable.getName().endsWith("_m") || variable.getName().endsWith("_M_EDEFAULT"); }
*/
/**
* Helper method that allows one to find the method that contain some model element.
* Helper method that allows one to find the method that contain some model
* element.
*
* @param o
* The model element.
......@@ -169,7 +166,8 @@ public class EMFImpl2PImpl {
}
/**
* Constructor of the transformation. Parameterized by the model to transform and the tagger used.
* Constructor of the transformation. Parameterized by the model to
* transform and the tagger used.
*
* @param javaModel
* @param tagger
......@@ -187,8 +185,9 @@ public class EMFImpl2PImpl {
}
/**
* Helper method to create "System.<stream>.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.
* Helper method to create "System.<stream>.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.
......@@ -240,7 +239,8 @@ public class EMFImpl2PImpl {
printlncall.setExpression(erraccess);
printlncall.getArguments().add(arg);
// Finally creating the expression result, containing the invocation of println
// Finally creating the expression result, containing the invocation of
// println
ExpressionStatement result = javaFactory.createExpressionStatement();
result.setExpression(printlncall);
......@@ -249,8 +249,9 @@ public class EMFImpl2PImpl {
}
/**
* 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.
* 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.
......@@ -290,8 +291,8 @@ public class EMFImpl2PImpl {
* @param clonedVar
* The field in the java regular implementation
*/
private void processShareableProperty(VariableDeclarationFragment variabled, Copier copier, ClassDeclaration classd,
VariableDeclaration clonedVar) {
private void processShareableProperty(VariableDeclarationFragment variabled, Copier copier,
ClassDeclaration classd, VariableDeclaration clonedVar) {
// Preparing the factory
JavaFactory javaFactory = (JavaFactory) Registry.INSTANCE
......@@ -338,7 +339,8 @@ public class EMFImpl2PImpl {
if (containingMethod != null) {
// If we are in a getter, we remove everything except the return
// If we are in a getter, we remove everything except
// the return
// (i.e. we remove everything proxy related)
// TODO check if in a return ?
if (containingMethod.getName().startsWith("basicGet")
......@@ -362,7 +364,31 @@ public class EMFImpl2PImpl {
// Creating the call to the getter
MethodInvocation getCall = javaFactory.createMethodInvocation();
getCall.setMethod(getter);
getCall.setExpression(clonedAccess);
// If the class inherits some other "cloned" field,
// we must cast it
if (hasProxySuperClass(classd)) {
// Putting the access within a cast
CastExpression cast = javaFactory.createCastExpression();
cast.setExpression(clonedAccess);
TypeAccess interfaceAccess = javaFactory.createTypeAccess();
interfaceAccess.setType(classd.getSuperInterfaces().get(0).getType());
cast.setType(interfaceAccess);
// Putting the cast between parentheses
ParenthesizedExpression parentheses = javaFactory.createParenthesizedExpression();
parentheses.setExpression(cast);
// And finally giving the casted variable to the
// get call
getCall.setExpression(parentheses);
} else {
// Otherwise, we call get without casting
getCall.setExpression(clonedAccess);
}
// Two cases : contained in a collection or not
// In both, we replace the access by cloned.get
......@@ -408,10 +434,23 @@ public class EMFImpl2PImpl {
JavaFactory javaFactory = (JavaFactory) Registry.INSTANCE
.getEFactory("http://www.eclipse.org/MoDisco/Java/0.2.incubation/java");
// Adding the import to class we are inheriting from
boolean mustImportSuperClass = true;
for (ImportDeclaration i : classd.getOriginalCompilationUnit().getImports()) {
if (i.getImportedElement().equals(classd.getSuperClass().getType())) {
mustImportSuperClass = false;
break;
}
}
if (mustImportSuperClass) {
ImportDeclaration importDecl = javaFactory.createImportDeclaration();
importDecl.setImportedElement(classd.getSuperClass().getType());
classd.getOriginalCompilationUnit().getImports().add(importDecl);
}
// 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
......@@ -446,7 +485,8 @@ public class EMFImpl2PImpl {
// If it is not static
if (!fieldd.getModifier().isStatic()) {
// We prepare the set of mutable variables (to remove eventually)
// 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()) {
......@@ -473,7 +513,7 @@ public class EMFImpl2PImpl {
// Changing the name of the constructor
constructor.setName(newName);
// Adding a parameter "cloned"
// Adding a parameter "clonedParam"
TypeAccess typeaccess = javaFactory.createTypeAccess();
typeaccess.setType(interfaced);
SingleVariableDeclaration vard = javaFactory.createSingleVariableDeclaration();
......@@ -481,17 +521,30 @@ public class EMFImpl2PImpl {
vard.setName("clonedParam");
constructor.getParameters().add(vard);
// Adding a set of the "cloned" field
SingleVariableAccess clonedFieldAccess = javaFactory.createSingleVariableAccess();
clonedFieldAccess.setVariable(clonedVar);
SingleVariableAccess clonedParamAccess = javaFactory.createSingleVariableAccess();
clonedParamAccess.setVariable(vard);
Assignment assignment = javaFactory.createAssignment();
assignment.setLeftHandSide(clonedFieldAccess);
assignment.setRightHandSide(clonedParamAccess);
ExpressionStatement assignStatement = javaFactory.createExpressionStatement();
assignStatement.setExpression(assignment);
constructor.getBody().getStatements().add(assignStatement);
// If inheriting from a proxy class, we add a param to the call
// of "super"
// super(ClonedParam)
if (hasProxySuperClass(classd)) {
SingleVariableAccess clonedParamAccess2 = javaFactory.createSingleVariableAccess();
clonedParamAccess2.setVariable(vard);
SuperConstructorInvocation superStatement = (SuperConstructorInvocation) constructor.getBody()
.getStatements().get(0);
superStatement.getArguments().add(clonedParamAccess2);
}
// Else, we then an assignment to initialize cloned
// cloned = clonedParam
else {
SingleVariableAccess clonedFieldAccess = javaFactory.createSingleVariableAccess();
clonedFieldAccess.setVariable(clonedVar);
SingleVariableAccess clonedParamAccess = javaFactory.createSingleVariableAccess();
clonedParamAccess.setVariable(vard);
Assignment assignment = javaFactory.createAssignment();
assignment.setLeftHandSide(clonedFieldAccess);
assignment.setRightHandSide(clonedParamAccess);
ExpressionStatement assignStatement = javaFactory.createExpressionStatement();
assignStatement.setExpression(assignment);
constructor.getBody().getStatements().add(assignStatement);
}
}
}
......@@ -501,7 +554,9 @@ public class EMFImpl2PImpl {
// Adding cloned field, at last (otherwise it is treated as a RO
// variable etc.)
classd.getBodyDeclarations().add(0, clonedField);
// But only if there isn't such inherited field already
if (!hasProxySuperClass(classd))
classd.getBodyDeclarations().add(0, clonedField);
}
......@@ -515,9 +570,9 @@ public class EMFImpl2PImpl {
*/
public Model transform2PImpl(String srcOutputFolder) {
// First we make a copy, because we will have to make references to original classes
// First we make a copy, because we will have to make references to
// original classes
EcoreUtil.Copier copier = new EcoreUtil.Copier();
// Model originalModel = (Model) copier.copy(javaModel);
Model originalModel = (Model) copier.copy(javaModel);
copier.copyReferences();
......@@ -552,20 +607,15 @@ public class EMFImpl2PImpl {
String sep = java.nio.file.FileSystems.getDefault().getSeparator().toString();
File outputDir = new File(srcOutputFolder + sep + generateFullyQualifiedName(pack, sep));
// Generating the copier
if (tagger.mayTagClassesPartShareable())
LightCopierGenerator.generate(tagger.getMetamodel(),generateFullyQualifiedName(pack), outputDir, tagger);
//generateCopierClass(tagger.getMetamodel(),generateFullyQualifiedName(pack), outputDir, tagger);
TagsGenerator.generateTagsClass(tagger.getMetamodel(),generateFullyQualifiedName(pack), outputDir, tagger);
//generateTagsClass(pack, srcOutputFolder);
CloningMaterialClassGenerator.generateCloningMaterialClass(tagger.getMetamodel(),generateFullyQualifiedName(pack), outputDir, tagger);
//generateCloningMaterialClass(pack, srcOutputFolder);
LightCopierGenerator.generate(tagger.getMetamodel(), generateFullyQualifiedName(pack), outputDir, tagger);
TagsGenerator.generateTagsClass(tagger.getMetamodel(), generateFullyQualifiedName(pack), outputDir, tagger);
CloningMaterialClassGenerator.generateCloningMaterialClass(tagger.getMetamodel(),
generateFullyQualifiedName(pack), outputDir, tagger);
for (ClassDeclaration classd : toProcess) {
// Then we put it in the target package
......@@ -574,7 +624,8 @@ public class EMFImpl2PImpl {
classd.getOriginalCompilationUnit().setPackage(pack);
}
// Keeping only compilation units that have been written, to only serialize them
// Keeping only compilation units that have been written, to only
// serialize them
javaModel.getCompilationUnits().retainAll(cuToKeep);
// returns the model (useless ? getter better ?)
......@@ -582,14 +633,15 @@ public class EMFImpl2PImpl {
}
/**
* Generates the fully qualified name of a package, with a specific separator between packages. Useful to construct
* a path.
* Generates the fully qualified name of a package, with a specific
* separator between packages. Useful to construct a path.
*
* @param pack
* The java package.
* @param separator
* The separator, e.g. "/" or "."
* @return The fully qualified name of the package, using the given separator.
* @return The fully qualified name of the package, using the given
* separator.
*/
private String generateFullyQualifiedName(Package pack, String separator) {
if (pack.getPackage() != null)
......@@ -599,7 +651,8 @@ public class EMFImpl2PImpl {
}
/**
* Generates the java fully qualified name of a pack, using the separator "."
* Generates the java fully qualified name of a pack, using the separator
* "."
*
* @param pack
* The java package.
......@@ -609,11 +662,6 @@ public class EMFImpl2PImpl {
return generateFullyQualifiedName(pack, ".");
}
/*private String generateFullyQualifiedName(InterfaceDeclaration interf) {
return generateFullyQualifiedName(interf.getPackage()) + "." + interf.getName();
}*/
/**
* Creates the mapping from the javamodel to the ecore model
*/
......@@ -689,7 +737,8 @@ public class EMFImpl2PImpl {
}
/**
* Creates the mapping between the properties of a java class and its ecore metaclass.
* Creates the mapping between the properties of a java class and its ecore
* metaclass.
*
* @param javaClass
* The java class.
......
......@@ -41,7 +41,8 @@ public class LightCopierGenerator {
try {
// Preparing the output file
// File outputDir = new File(srcOutputFolder + "/" + generateFullyQualifiedName(pack, "/"));*/
// File outputDir = new File(srcOutputFolder + "/" +
// generateFullyQualifiedName(pack, "/"));*/
outputDir.mkdirs();
File outputFile = new File(outputDir, className + ".java");
......@@ -69,35 +70,38 @@ public class LightCopierGenerator {
Set<EClass> eClasses = EcoreHelper.findEClasses(metamodel);
for (EClass c : eClasses) {
if (tagger.getTagOfEClass(c) == ClassTag.PARTIALLY_SHAREABLE) {
hasShareable = true;
imports += "import " + EcoreHelper.computeFullyQualifiedName(c) + ";\n";
if (!c.isAbstract() && !c.isInterface()) {
if (tagger.getTagOfEClass(c) == ClassTag.PARTIALLY_SHAREABLE) {
hasShareable = true;
imports += "import " + EcoreHelper.computeFullyQualifiedName(c) + ";\n";
// We generate its if/elseif lines
if (!firstIf) {
classdef += "else\n";
firstIf = false;
}
classdef += "if (eObject instanceof " + c.getName() + ")\n";
classdef += "return new " + c.getName() + tagger.getSuffix() + "((" + c.getName() + ")"
+ "eObject);\n";
// We generate its if/elseif lines
if (!firstIf) {
classdef += "else\n";
firstIf = false;
}
classdef += "if (eObject instanceof " + c.getName() + ")\n";
classdef += "return new " + c.getName() + tagger.getSuffix() + "((" + c.getName() + ")"
+ "eObject);\n";
}
else if (tagger.getTagOfEClass(c) == ClassTag.COMPLETELY_SHAREABLE) {
hasShareable = true;
imports += "import " + EcoreHelper.computeFullyQualifiedName(c) + ";\n";
// We generate its if/elseif lines
if (!firstIf) {
classdef += "else\n";
firstIf = false;
else if (tagger.getTagOfEClass(c) == ClassTag.COMPLETELY_SHAREABLE) {
hasShareable = true;
imports += "import " + EcoreHelper.computeFullyQualifiedName(c) + ";\n";
// We generate its if/elseif lines
if (!firstIf) {
classdef += "else\n";
firstIf = false;
}
classdef += "if (eObject instanceof " + c.getName() + ")\n";
classdef += "return eObject;\n";
}
classdef += "if (eObject instanceof " + c.getName() + ")\n";
classdef += "return eObject;\n";
}
}
......
......@@ -30,8 +30,8 @@ import fr.inria.diverse.cloning.runtime.util.Log;
public class TagsGenerator {
/**
* Generates a java class implementing the MetamodelTags interface, to retrieve at runtine the generate tags of the
* metamodel classes.
* Generates a java class implementing the MetamodelTags interface, to
* retrieve at runtine the generate tags of the metamodel classes.
*
* @param pack
* The java package in which to generate the class.
......@@ -77,21 +77,20 @@ public class TagsGenerator {
boolean firstIf = true;
Set<EClass> eClasses = EcoreHelper.findEClasses(metamodel);
for (EClass c : eClasses) {
if (!c.isInterface()) {
//imports += "import " + EcoreHelper.computeFullyQualifiedName(c) + ";\n";
// We generate its if/elseif lines
if (!firstIf) {
classdef += "else\n";
firstIf = false;
}
String packageInterfaceName = EcoreHelper.computeFullyQualifiedName(c.getEPackage()) + "."
+ CodeGeneration.firstCharUp(c.getEPackage().getName()) + "Package";
// We generate its if/elseif lines
if (!firstIf) {
classdef += "else\n";
firstIf = false;
}
classdef += "if (eClass.equals(" + packageInterfaceName + ".eINSTANCE.get" + c.getName() + "()))\n";
classdef += "return " + ClassTag.class.getSimpleName() + "." + tagger.getTagOfEClass(c) + ";\n";
String packageInterfaceName = EcoreHelper.computeFullyQualifiedName(c.getEPackage()) + "."
+ CodeGeneration.firstCharUp(c.getEPackage().getName()) + "Package";
classdef += "if (eClass.equals(" + packageInterfaceName + ".eINSTANCE.get" + c.getName() + "()))\n";
classdef += "return " + ClassTag.class.getSimpleName() + "." + tagger.getTagOfEClass(c) + ";\n";
}
}
// Default case: call to the super method
......@@ -115,22 +114,23 @@ public class TagsGenerator {
// For each class that led to a pimpl
boolean firstIf2 = true;
for (EClass c : eClasses) {
if (!c.isInterface()) {
for (EStructuralFeature prop : c.getEStructuralFeatures()) {
for (EStructuralFeature prop : c.getEStructuralFeatures()) {
// We generate its if/elseif lines
if (!firstIf2) {
classdef += "else\n";
firstIf2 = false;
}
String packageInterfaceName = EcoreHelper.computeFullyQualifiedName(c.getEPackage()) + "."
+ CodeGeneration.firstCharUp(c.getEPackage().getName()) + "Package";
// We generate its if/elseif lines
if (!firstIf2) {
classdef += "else\n";
firstIf2 = false;
}
classdef += "if (prop.equals(" + packageInterfaceName + ".eINSTANCE.get" + c.getName() + "_"
+ CodeGeneration.firstCharUp(prop.getName()) + "()))\n";
classdef += "return " + String.valueOf(tagger.isPropertyShareable(prop)) + ";\n";
String packageInterfaceName = EcoreHelper.computeFullyQualifiedName(c.getEPackage()) + "."
+ CodeGeneration.firstCharUp(c.getEPackage().getName()) + "Package";
classdef += "if (prop.equals(" + packageInterfaceName + ".eINSTANCE.get" + c.getName() + "_"
+ CodeGeneration.firstCharUp(prop.getName()) + "()))\n";
classdef += "return " + String.valueOf(tagger.isPropertyShareable(prop)) + ";\n";
}
}
}
// Default case
......@@ -139,8 +139,7 @@ public class TagsGenerator {
// end isPropertyMutable method declaration
classdef += "}\n";
// metrics methods declaration
classdef += "public double getShareableClassesRatio() {\n";
classdef += "return " + CloningMetrics.computeShareableClassesRatio(metamodel, tagger) + ";\n";
......@@ -152,10 +151,12 @@ public class TagsGenerator {
classdef += "return " + CloningMetrics.computeIsolatedShareablePropertiesRatio(metamodel, tagger) + ";\n";
classdef += "}\n";
classdef += "public double getShareablePropertiesInPartShareableClassesDensity() {\n";
classdef += "return " + CloningMetrics.computeShareablePropertiesInPartShareableClassesDensity(metamodel, tagger)+ ";\n";
classdef += "return "
+ CloningMetrics.computeShareablePropertiesInPartShareableClassesDensity(metamodel, tagger) + ";\n";