Skip to content

Commit

Permalink
Merge pull request #1227 from jplag/fix/emfatic-mapping
Browse files Browse the repository at this point in the history
Fix the token to line mapping for the EMF language and the Emfatic views
  • Loading branch information
tsaglam authored Aug 9, 2023
2 parents d6bd843 + 3bc3aa1 commit f366b76
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ public TestDataContext testFile(String... fileNames) {
return new TestDataContext(data);
}

/**
* Adds all files matching a certain type. Returns a {@link TestDataContext}, that can be used to configure various
* tests on the given files.
* @param fileSuffix is the suffix of the files to be added.
* @return The {@link TestDataContext}
*/
public TestDataContext testAllOfType(String fileSuffix) {
Set<TestData> data = Arrays.stream(testFileLocation.list()).filter(it -> it.endsWith(fileSuffix))
.map(it -> new File(this.testFileLocation, it)).map(FileTestData::new).collect(Collectors.toSet());
return new TestDataContext(data);
}

/**
* Adds a list of source string to the test data. Returns a {@link TestDataContext}, that can be used to configure
* various tests on the given files.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.ENamedElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.ETypedElement;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil.Copier;
Expand All @@ -27,9 +29,11 @@
*/
public final class EmfaticModelView extends AbstractModelView {
// The following regular expressions match keywords of the Emfatic syntax:
private static final String PACKAGE_REGEX = "package\\s+\\S+;";
private static final String TYPE_KEYWORD_REGEX = "(package |class |datatype |enum )";
private static final String FEATURE_KEYWORD_REGEX = "(.*attr .*|op .*|.*ref .*|.*val .*).*";
private static final String TYPE_SUFFIX_REGEX = "(;| extends| \\{)";
private static final String LINE_SUFFIX_REGEX = ";";
private static final char CLOSING_CHAR = '}';
private static final String ANYTHING_REGEX = ".*";

Expand All @@ -40,6 +44,7 @@ public final class EmfaticModelView extends AbstractModelView {

private final Copier modelCopier; // Allows to trace between original and copied elements
private int lastLineIndex; // last line given to a token
private final int rootPackageIndex;

/**
* Creates an Emfatic view for a metamodel.
Expand All @@ -57,6 +62,7 @@ public EmfaticModelView(File file, Resource modelResource) throws ParsingExcepti
Resource copiedResource = EMFUtil.copyModel(modelResource, modelCopier);
replaceElementNamesWithHashes(copiedResource);
hashedLines = generateEmfaticCode(new StringBuilder(), copiedResource);
rootPackageIndex = findIndexOfRootPackage(hashedLines);
}

/**
Expand Down Expand Up @@ -108,6 +114,18 @@ private final List<String> generateEmfaticCode(StringBuilder builder, Resource m
return builder.toString().lines().toList();
}

/**
* Calculates the index of the root package declaration, as it has unique syntax in Emfatic.
*/
private final int findIndexOfRootPackage(List<String> lines) {
for (int index = 0; index < lines.size(); index++) {
if (lines.get(index).matches(PACKAGE_REGEX)) {
return index;
}
}
return -1;
}

/**
* Calculates the line index of a metamodel token from the emfatic code. If it cannot be found, the last index is used.
*/
Expand Down Expand Up @@ -135,7 +153,7 @@ private int calculateLineIndexOf(MetamodelToken token) {
*/
private int findEndIndexOf(int declarationIndex) {
int indentation = indentationOf(lines.get(declarationIndex));
if (declarationIndex > 1) { // exception for top level package
if (declarationIndex > rootPackageIndex) { // exception for top level package
for (int i = declarationIndex + 1; i < lines.size(); i++) {
String nextLine = lines.get(i);
if (nextLine.length() > indentation && CLOSING_CHAR == nextLine.charAt(indentation)) {
Expand Down Expand Up @@ -186,7 +204,8 @@ private int findLineIndexOf(ENamedElement element) {
* Checks if a line (with leading whitespace removed) contains an element based on the elements hash.
*/
private boolean isDeclaration(ENamedElement element, String hash, String line) {
return isStructuralFeature(element, hash, line) || isEnumLiteral(element, hash, line) || isType(hash, line);
return isStructuralFeature(element, hash, line) || isTypedElement(element, hash, line) || isEnumLiteral(element, hash, line)
|| isType(hash, line);
}

private boolean isType(String hash, String line) {
Expand All @@ -197,8 +216,12 @@ private boolean isEnumLiteral(ENamedElement element, String hash, String line) {
return element instanceof EEnumLiteral && line.matches(hash + ANYTHING_REGEX);
}

private boolean isTypedElement(ENamedElement element, String hash, String line) {
return element instanceof EOperation && element instanceof EParameter && line.matches(FEATURE_KEYWORD_REGEX + hash + ANYTHING_REGEX);
}

private boolean isStructuralFeature(ENamedElement element, String hash, String line) {
return element instanceof ETypedElement && line.matches(FEATURE_KEYWORD_REGEX + hash + ANYTHING_REGEX);
return element instanceof ETypedElement && line.matches(FEATURE_KEYWORD_REGEX + hash + LINE_SUFFIX_REGEX);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
*/
public abstract class AbstractEmfTest {

protected static final Path BASE_PATH = Path.of("src", "test", "resources", "de", "jplag", "models");
protected static final Path BASE_PATH = Path.of("src", "test", "resources", "de", "jplag", "emf");

protected static final String[] TEST_SUBJECTS = {"bookStore.ecore", // base metamodel
"bookStoreExtended.ecore", // extended version of base metamodel
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package de.jplag.emf;

import static de.jplag.emf.MetamodelTokenType.ATTRIBUTE;
import static de.jplag.emf.MetamodelTokenType.CLASS;
import static de.jplag.emf.MetamodelTokenType.CLASS_END;
import static de.jplag.emf.MetamodelTokenType.CONTAINMENT_MULT;
import static de.jplag.emf.MetamodelTokenType.PACKAGE;
import static de.jplag.emf.MetamodelTokenType.PACKAGE_END;

import org.junit.jupiter.api.AfterEach;

import de.jplag.testutils.FileUtil;
import de.jplag.testutils.LanguageModuleTest;
import de.jplag.testutils.datacollector.TestDataCollector;
import de.jplag.testutils.datacollector.TestSourceIgnoredLinesCollector;

/**
* Basic EMF test that mainly serves the purpose of checking ascending line indices for the tokens with Emfatic views.
*/
public class EmfLanguageTest extends LanguageModuleTest {

public EmfLanguageTest() {
super(new EmfLanguage(), MetamodelTokenType.class);
}

@Override
protected void collectTestData(TestDataCollector collector) {
collector.testAllOfType(EmfLanguage.FILE_ENDING).testContainedTokens(PACKAGE, PACKAGE_END, CLASS, CLASS_END, ATTRIBUTE, CONTAINMENT_MULT);

}

@Override
protected void configureIgnoredLines(TestSourceIgnoredLinesCollector collector) {
// None, does not really apply for modeling artifacts.
}

@AfterEach
protected void tearDown() {
FileUtil.clearFiles(getTestFileLocation(), EmfLanguage.VIEW_FILE_SUFFIX); // clean up the view files.
}

}

0 comments on commit f366b76

Please sign in to comment.