Skip to content

Commit

Permalink
Merge pull request #175 from LorenzoBettini/task_174-Detect_duplicate…
Browse files Browse the repository at this point in the history
…_metamodel_imports

Task 174 detect duplicate metamodel imports
  • Loading branch information
LorenzoBettini committed May 16, 2020
2 parents e1acd37 + 12ba97b commit 3b88b6c
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 2 deletions.
Expand Up @@ -97,4 +97,24 @@ class EdeltaModelUtilTest extends EdeltaAbstractTest {
]
}

@Test
def void testGetMetamodelImportText() {
val input = '''
metamodel "foo"
metamodel "bar"
metamodel "foo"
'''
input.parseWithTestEcore => [
assertEquals('"foo"',
getMetamodelImportText(it, 0))
assertEquals('"bar"',
getMetamodelImportText(it, 1))
val node = getMetamodelImportNodes(it).get(1) // metamodel "bar"
assertEquals(input.indexOf('"bar"'), node.offset)
assertEquals('"bar"'.length, node.length)
assertEquals(input.indexOf("metamodel", 2),
node.previousSibling.previousSibling.offset) // the second metamodel
]
}

}
Expand Up @@ -300,6 +300,31 @@ class EdeltaValidatorTest extends EdeltaAbstractTest {
]
}

@Test
def void testDuplicateMetamodelImport() {
val input = '''
metamodel "foo"
metamodel "bar"
metamodel "nonexistent"
metamodel "nonexistent" // also check unresolved imports
metamodel "foo"
'''
input.parseWithTestEcores => [
assertError(
EdeltaPackage.eINSTANCE.edeltaProgram,
EdeltaValidator.DUPLICATE_METAMODEL_IMPORT,
input.lastIndexOf('"nonexistent"'), '"nonexistent"'.length,
'Duplicate metamodel import "nonexistent"'
)
assertError(
EdeltaPackage.eINSTANCE.edeltaProgram,
EdeltaValidator.DUPLICATE_METAMODEL_IMPORT,
input.lastIndexOf('"foo"'), '"foo"'.length,
'Duplicate metamodel import "foo"'
)
]
}

@Test
def void testReferenceToEClassRemoved() {
val input = referenceToEClassRemoved.toString
Expand Down
Expand Up @@ -9,6 +9,7 @@
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.xtend2.lib.StringConcatenation;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.testing.InjectWith;
import org.eclipse.xtext.testing.XtextRunner;
import org.eclipse.xtext.xbase.XExpression;
Expand Down Expand Up @@ -138,4 +139,29 @@ public void testGetEcoreReferenceText() {
};
ObjectExtensions.<EList<XExpression>>operator_doubleArrow(_expressions, _function);
}

@Test
public void testGetMetamodelImportText() {
StringConcatenation _builder = new StringConcatenation();
_builder.append("metamodel \"foo\"");
_builder.newLine();
_builder.append("metamodel \"bar\"");
_builder.newLine();
_builder.append("metamodel \"foo\"");
_builder.newLine();
final String input = _builder.toString();
EdeltaProgram _parseWithTestEcore = this.parseWithTestEcore(input);
final Procedure1<EdeltaProgram> _function = (EdeltaProgram it) -> {
Assert.assertEquals("\"foo\"",
EdeltaModelUtil.getMetamodelImportText(it, 0));
Assert.assertEquals("\"bar\"",
EdeltaModelUtil.getMetamodelImportText(it, 1));
final INode node = EdeltaModelUtil.getMetamodelImportNodes(it).get(1);
Assert.assertEquals(input.indexOf("\"bar\""), node.getOffset());
Assert.assertEquals("\"bar\"".length(), node.getLength());
Assert.assertEquals(input.indexOf("metamodel", 2),
node.getPreviousSibling().getPreviousSibling().getOffset());
};
ObjectExtensions.<EdeltaProgram>operator_doubleArrow(_parseWithTestEcore, _function);
}
}
Expand Up @@ -443,6 +443,36 @@ public void testDuplicateDeclarations() {
ObjectExtensions.<EdeltaProgram>operator_doubleArrow(_parseWithTestEcore, _function);
}

@Test
public void testDuplicateMetamodelImport() {
StringConcatenation _builder = new StringConcatenation();
_builder.append("metamodel \"foo\"");
_builder.newLine();
_builder.append("metamodel \"bar\"");
_builder.newLine();
_builder.append("metamodel \"nonexistent\"");
_builder.newLine();
_builder.append("metamodel \"nonexistent\" // also check unresolved imports");
_builder.newLine();
_builder.append("metamodel \"foo\"");
_builder.newLine();
final String input = _builder.toString();
EdeltaProgram _parseWithTestEcores = this.parseWithTestEcores(input);
final Procedure1<EdeltaProgram> _function = (EdeltaProgram it) -> {
this._validationTestHelper.assertError(it,
EdeltaPackage.eINSTANCE.getEdeltaProgram(),
EdeltaValidator.DUPLICATE_METAMODEL_IMPORT,
input.lastIndexOf("\"nonexistent\""), "\"nonexistent\"".length(),
"Duplicate metamodel import \"nonexistent\"");
this._validationTestHelper.assertError(it,
EdeltaPackage.eINSTANCE.getEdeltaProgram(),
EdeltaValidator.DUPLICATE_METAMODEL_IMPORT,
input.lastIndexOf("\"foo\""), "\"foo\"".length(),
"Duplicate metamodel import \"foo\"");
};
ObjectExtensions.<EdeltaProgram>operator_doubleArrow(_parseWithTestEcores, _function);
}

@Test
public void testReferenceToEClassRemoved() {
final String input = this._inputs.referenceToEClassRemoved().toString();
Expand Down
Expand Up @@ -156,4 +156,23 @@ class EdeltaQuickfixTest extends AbstractQuickfixTest {
'''))
}

@Test def fixRemoveDuplicateImport() {
'''
metamodel "bar"
metamodel "bar"
metamodel "foo"
'''.testQuickfixesOn
(EdeltaValidator.DUPLICATE_METAMODEL_IMPORT,
new Quickfix("Remove duplicate metamodel import",
"Remove duplicate metamodel import",
'''
metamodel "bar"
metamodel "foo"
'''))
}

}
Expand Up @@ -232,4 +232,27 @@ public void fixAmbiguousEcoreRef() {
"Fix ambiguity with \'mainpackage.subpackage.subsubpackage.MyClass\'", _builder_3.toString());
this.testQuickfixesOn(_builder, EdeltaValidator.AMBIGUOUS_REFERENCE, _quickfix, _quickfix_1, _quickfix_2);
}

@Test
public void fixRemoveDuplicateImport() {
StringConcatenation _builder = new StringConcatenation();
_builder.append("metamodel \"bar\"");
_builder.newLine();
_builder.newLine();
_builder.append("metamodel \"bar\"");
_builder.newLine();
_builder.newLine();
_builder.append("metamodel \"foo\"");
_builder.newLine();
StringConcatenation _builder_1 = new StringConcatenation();
_builder_1.append("metamodel \"bar\"");
_builder_1.newLine();
_builder_1.newLine();
_builder_1.newLine();
_builder_1.append("metamodel \"foo\"");
_builder_1.newLine();
AbstractQuickfixTest.Quickfix _quickfix = new AbstractQuickfixTest.Quickfix("Remove duplicate metamodel import",
"Remove duplicate metamodel import", _builder_1.toString());
this.testQuickfixesOn(_builder, EdeltaValidator.DUPLICATE_METAMODEL_IMPORT, _quickfix);
}
}
Expand Up @@ -3,11 +3,16 @@
*/
package edelta.ui.quickfix;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.ui.editor.model.edit.IModificationContext;
import org.eclipse.xtext.ui.editor.quickfix.Fix;
import org.eclipse.xtext.ui.editor.quickfix.IssueResolutionAcceptor;
import org.eclipse.xtext.validation.Issue;
import org.eclipse.xtext.xbase.annotations.ui.quickfix.XbaseWithAnnotationsQuickfixProvider;

import edelta.edelta.EdeltaProgram;
import edelta.util.EdeltaModelUtil;
import edelta.validation.EdeltaValidator;

/**
Expand Down Expand Up @@ -67,4 +72,26 @@ public void fixEcoreRefAmbiguity(final Issue issue, final IssueResolutionAccepto
);
}
}

@Fix(EdeltaValidator.DUPLICATE_METAMODEL_IMPORT)
public void removeDuplicateMetamodelImport(final Issue issue, final IssueResolutionAcceptor acceptor) {
final int importToRemove = Integer.parseInt(issue.getData()[0]);
acceptor.accept(
issue,
"Remove duplicate metamodel import",
"Remove duplicate metamodel import",
"EPackage.gif",
(EObject element, IModificationContext context) -> {
INode node = EdeltaModelUtil.getMetamodelImportNodes
((EdeltaProgram) element).get(importToRemove);
// the node corresponding to the keyword 'metamodel'
INode metamodelNode = node.getPreviousSibling().getPreviousSibling();
int offset = metamodelNode.getOffset();
int length = node.getEndOffset() - offset + 1;
// also remove newline
context.getXtextDocument().replace(offset, length, "");
}
);
}

}
15 changes: 13 additions & 2 deletions edelta.parent/edelta/src/edelta/util/EdeltaModelUtil.java
@@ -1,14 +1,16 @@
package edelta.util;

import static edelta.edelta.EdeltaPackage.Literals.EDELTA_PROGRAM__METAMODELS;
import static org.eclipse.xtext.EcoreUtil2.getContainerOfType;
import static org.eclipse.xtext.nodemodel.util.NodeModelUtils.findActualNodeFor;
import static org.eclipse.xtext.nodemodel.util.NodeModelUtils.getTokenText;
import static org.eclipse.xtext.nodemodel.util.NodeModelUtils.*;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.xtext.nodemodel.INode;

import edelta.edelta.EdeltaEcoreReference;
import edelta.edelta.EdeltaProgram;
Expand All @@ -31,6 +33,15 @@ public static String getEcoreReferenceText(EdeltaEcoreReference ref) {
return getTokenText(findActualNodeFor(ref));
}

public static String getMetamodelImportText(EdeltaProgram p, int index) {
return getTokenText(
getMetamodelImportNodes(p).get(index));
}

public static List<INode> getMetamodelImportNodes(EdeltaProgram p) {
return findNodesForFeature(p, EDELTA_PROGRAM__METAMODELS);
}

public static boolean hasCycleInSuperPackage(EPackage ePackage) {
Set<EPackage> seen = new HashSet<>();
EPackage superPackage = ePackage.getESuperPackage();
Expand Down
14 changes: 14 additions & 0 deletions edelta.parent/edelta/src/edelta/validation/EdeltaValidator.xtend
Expand Up @@ -39,6 +39,7 @@ class EdeltaValidator extends AbstractEdeltaValidator {
public static val INTERPRETER_ACCESS_REMOVED_ELEMENT = PREFIX + "InterpreterAccessRemovedElement";
public static val INTERPRETER_ACCESS_RENAMED_ELEMENT = PREFIX + "InterpreterAccessRenamedElement";
public static val DUPLICATE_DECLARATION = PREFIX + "DuplicateDeclaration";
public static val DUPLICATE_METAMODEL_IMPORT = PREFIX + "DuplicateMetamodelImport";
public static val INVALID_SUBPACKAGE_IMPORT = PREFIX + "InvalidSubPackageImport";
public static val INVALID_SUBPACKAGE_MODIFICATION = PREFIX + "InvalidSubPackageModification";
public static val AMBIGUOUS_REFERENCE = PREFIX + "AmbiguousReference";
Expand Down Expand Up @@ -77,6 +78,7 @@ class EdeltaValidator extends AbstractEdeltaValidator {
@Check
def void checkProgram(EdeltaProgram p) {
var metamodelIndex = 0
val metamodelImportSet = newHashSet
for (metamodel : p.metamodels) {
val rootPackage = findRootSuperPackage(metamodel)
if (rootPackage !== null) {
Expand All @@ -89,6 +91,18 @@ class EdeltaValidator extends AbstractEdeltaValidator {
rootPackage.name // the fix for the import
)
}
val metamodelImport = getMetamodelImportText(p, metamodelIndex)
if (metamodelImportSet.contains(metamodelImport)) {
error(
"Duplicate metamodel import " + metamodelImport,
p,
EDELTA_PROGRAM__METAMODELS,
metamodelIndex,
DUPLICATE_METAMODEL_IMPORT,
"" + metamodelIndex // the fix for the import
)
}
metamodelImportSet.add(metamodelImport)
metamodelIndex++
}

Expand Down

0 comments on commit 3b88b6c

Please sign in to comment.