Просмотр исходного кода

Improve metamodel parser, separate token generation from the tree view generation.

Timur Sağlam 4 лет назад
Родитель
Сommit
b6302a2ca7

+ 21 - 196
jplag.frontend.emf/src/main/java/de/jplag/emf/EcoreParser.java

@@ -1,39 +1,11 @@
 package de.jplag.emf;
 
-import static de.jplag.emf.MetamodelTokenConstants.ANNOTATION;
-import static de.jplag.emf.MetamodelTokenConstants.ATTRIBUTE;
-import static de.jplag.emf.MetamodelTokenConstants.CLASS;
-import static de.jplag.emf.MetamodelTokenConstants.DATATYPE;
-import static de.jplag.emf.MetamodelTokenConstants.ENUM;
-import static de.jplag.emf.MetamodelTokenConstants.ENUM_LITERAL;
-import static de.jplag.emf.MetamodelTokenConstants.INTERFACE;
-import static de.jplag.emf.MetamodelTokenConstants.OPERATION;
-import static de.jplag.emf.MetamodelTokenConstants.PACKAGE;
-import static de.jplag.emf.MetamodelTokenConstants.PARAMETER;
-import static de.jplag.emf.MetamodelTokenConstants.REFERENCE;
-import static de.jplag.emf.MetamodelTokenConstants.SUPER_TYPE;
-
-import java.io.BufferedWriter;
 import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 
 import org.eclipse.emf.common.util.URI;
-import org.eclipse.emf.ecore.EAnnotation;
-import org.eclipse.emf.ecore.EAttribute;
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EClassifier;
-import org.eclipse.emf.ecore.EDataType;
-import org.eclipse.emf.ecore.EEnum;
-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.EPackage;
-import org.eclipse.emf.ecore.EReference;
-import org.eclipse.emf.ecore.EStructuralFeature;
 import org.eclipse.emf.ecore.EcorePackage;
 import org.eclipse.emf.ecore.resource.Resource;
 import org.eclipse.emf.ecore.resource.ResourceSet;
@@ -49,14 +21,12 @@ import de.jplag.TokenList;
  * Parser for EMF metamodels.
  * @author Timur Saglam
  */
-public class EcoreParser extends AbstractParser { // TODO TS: Currently combines parser with the tree view generation.
-    private static final String TREE_VIEW_FILE_SUFFIX = ".tmp";
+public class EcoreParser extends AbstractParser {
+    private static final String TMP = ".tmp";
     private TokenList tokens;
     private String currentFile;
-    private StringBuilder treeView;
-    private int lineIndex;
-    private int columnIndex;
-    private int indentation;
+    private MetamodelTreeView treeView;
+    private TokenGeneratingMetamodelVisitor visitor;
 
     /**
      * Creates the parser.
@@ -77,177 +47,32 @@ public class EcoreParser extends AbstractParser { // TODO TS: Currently combines
      * @return the list of parsed tokens.
      */
     public TokenList parse(File directory, List<String> fileNames) {
-        // TODO TS: When the submissions are files, the file names are empty and the directory is the file --> error
         tokens = new TokenList();
         errors = 0;
         for (String fileName : fileNames) {
-            EPackage root = loadModel(directory.toString(), fileName);
-            currentFile = fileName + TREE_VIEW_FILE_SUFFIX;
-            treeView = new StringBuilder();
-            lineIndex = 0;
-            columnIndex = 0;
-            parseEPackage(root);
-
-            tokens.addToken(new MetamodelToken(TokenConstants.FILE_END, currentFile, -1, -1, -1));
-            createTreeViewFile(directory, fileName);
-        }
-        return tokens;
-    }
-
-    private void createTreeViewFile(File directory, String fileName) {
-        File treeViewFile = new File(directory, currentFile);
-        try (BufferedWriter writer = new BufferedWriter(new FileWriter(treeViewFile));) {
-            if (!treeViewFile.exists()) {
-                treeViewFile.createNewFile();
+            currentFile = fileName;
+            treeView = new MetamodelTreeView(currentFile);
+            List<EObject> rootElements = loadModel(directory.toString(), fileName);
+            for (EObject root : rootElements) {
+                visitor = new TokenGeneratingMetamodelVisitor(this);
+                visitor.visit(root);
             }
-            writer.append(treeView.toString());
-        } catch (IOException exception) {
-            errors++;
-            getErrorConsumer().addError("Parsing Error in '" + fileName + "':" + exception.toString());
-            exception.printStackTrace();
+            tokens.addToken(new MetamodelToken(TokenConstants.FILE_END, fileName + TMP));
+            treeView.writeToFile(directory, TMP);
         }
+        return tokens;
     }
 
-    private EPackage loadModel(String directory, String name) {
-
+    private List<EObject> loadModel(String directory, String name) {
         final ResourceSet resourceSet = new ResourceSetImpl();
-        Resource resource = resourceSet.getResource(URI.createFileURI(directory + File.separator + name), true);
-        EObject content = resource.getContents().get(0);
-        if (content instanceof EPackage root) {
-            return root;
-        }
-        throw new IllegalStateException("Root element of model " + name + " is not a package: " + content);
-    }
-
-    private void parseEPackage(EPackage ePackage) {
-        addToken(PACKAGE, ePackage, true);
-        indentation++;
-        ePackage.getEClassifiers().forEach(it -> parseEClassifier(it));
-        ePackage.getEAnnotations().forEach(it -> parseEAnnotation(it));
-        ePackage.getESubpackages().forEach(it -> parseEPackage(it));
-        indentation--;
-    }
-
-    private void parseEAnnotation(EAnnotation annotation) {
-        addToken(ANNOTATION, annotation, true);
-    }
-
-    private void parseEClassifier(EClassifier classifier) {
-        if (classifier instanceof EClass eClass) {
-            parseEClass(eClass);
-        } else if (classifier instanceof EDataType dataType) {
-            parseEDataType(dataType);
-        }
-        // TODO TS: Parse type parameters & generic type?
-        classifier.getEAnnotations().forEach(it -> parseEAnnotation(it));
-    }
-
-    private void parseEClass(EClass eClass) {
-        if (eClass.isInterface()) {
-            addToken(INTERFACE, eClass, false);
-        } else {
-            addToken(CLASS, eClass, false);
-        }
-        indentation++;
-        // TODO TS: Extract abstract? probably not
-        eClass.getESuperTypes().forEach(it -> parseESuperType(it));
-        addNewLine();
-        eClass.getEStructuralFeatures().forEach(it -> parseEStructuralFeature(it));
-        eClass.getEOperations().forEach(it -> parseEOperation(it));
-        indentation--;
-    }
-
-    private void parseESuperType(EClass superType) {
-        addToken(SUPER_TYPE, superType, false, " -> ");
-    }
-
-    private void parseEOperation(EOperation operation) {
-        addToken(OPERATION, operation, true);
-        indentation++;
-        operation.getEParameters().forEach(it -> addToken(PARAMETER, it, true));
-        // TODO TS: Extract return type?
-        // TODO TS: Extract throws declarations?
-        // TODO TS: Parse type parameters & generic type?
-        operation.getEAnnotations().forEach(it -> parseEAnnotation(it));
-        indentation--;
+        String pathName = name.isEmpty() ? directory : directory + File.separator + name;
+        Resource resource = resourceSet.getResource(URI.createFileURI(pathName), true);
+        return resource.getContents();
     }
 
-    private void parseEStructuralFeature(EStructuralFeature structuralFeature) {
-        if (structuralFeature instanceof EAttribute attribute) {
-            addToken(ATTRIBUTE, attribute, true);
-        } else if (structuralFeature instanceof EReference reference) {
-            addToken(REFERENCE, reference, true);
-            // TODO TS: Extract containment?
-        }
-        indentation++;
-        structuralFeature.getEAnnotations().forEach(it -> parseEAnnotation(it));
-        indentation--;
-    }
-
-    private void parseEDataType(EDataType dataType) {
-        if (dataType instanceof EEnum enumeration) {
-            parseEEnum(enumeration);
-        } else {
-            addToken(DATATYPE, dataType, true);
-        }
-        indentation++;
-        dataType.getEAnnotations().forEach(it -> parseEAnnotation(it));
-        indentation--;
-    }
-
-    private void parseEEnum(EEnum enumeration) {
-        addToken(ENUM, enumeration, true);
-        indentation++;
-        enumeration.getELiterals().forEach(it -> parseEEnumLiteral(it));
-        indentation--;
-    }
-
-    private void parseEEnumLiteral(EEnumLiteral enumeral) {
-        addToken(ENUM_LITERAL, enumeral, true);
-        enumeral.getEAnnotations().forEach(it -> parseEAnnotation(it));
-    }
-
-    private void addToken(int type, EObject source, boolean newLine) {
-        addToken(type, source, newLine, "");
-    }
-
-    private void addToken(int type, EObject source, boolean newLine, String prefix) {
-        int tokenLength = addTokenText(source, prefix);
-        tokens.addToken(new MetamodelToken(type, currentFile, lineIndex + 1, columnIndex + 1, tokenLength));
-        updateIndices(newLine, tokenLength);
-    }
-
-    private void updateIndices(boolean newLine, int tokenLength) {
-        if (newLine) {
-            addNewLine();
-        } else {
-            columnIndex += tokenLength;
-        }
-    }
-
-    private void addNewLine() {
-        lineIndex++;
-        columnIndex = 0;
-        treeView.append(System.lineSeparator());
-    }
-
-    private int addTokenText(EObject source, String prefix) {
-        String tokenText = "(" + source.getClass().getInterfaces()[0].getSimpleName().toString() + ")";
-        if (source instanceof ENamedElement element) {
-            tokenText = element.getName() + " " + tokenText;
-        }
-        int tokenLength = tokenText.length();
-        if (prefix.isEmpty()) {
-            for (int i = 0; i < indentation; i++) {
-                String indentationText = "  ";
-                tokenText = indentationText + tokenText;
-                columnIndex += indentationText.length();
-            }
-        } else {
-            tokenText = prefix + tokenText;
-            columnIndex += prefix.length();
-        }
-        treeView.append(tokenText);
-        return tokenLength;
+    public void addToken(int type, EObject source) {
+        MetamodelToken token = new MetamodelToken(type, currentFile + TMP, source);
+        treeView.addToken(token, visitor.getCurrentTreeDepth());
+        tokens.addToken(token);
     }
 }

+ 25 - 3
jplag.frontend.emf/src/main/java/de/jplag/emf/MetamodelToken.java

@@ -1,5 +1,9 @@
 package de.jplag.emf;
 
+import java.util.Optional;
+
+import org.eclipse.emf.ecore.EObject;
+
 import de.jplag.Token;
 import de.jplag.TokenConstants;
 
@@ -9,6 +13,8 @@ import de.jplag.TokenConstants;
  */
 public class MetamodelToken extends Token implements MetamodelTokenConstants {
 
+    private final Optional<EObject> eObject;
+
     /**
      * Creates an Ecore metamodel token.
      * @param type is the corresponding ID of the {@link TokenConstants}.
@@ -16,9 +22,20 @@ public class MetamodelToken extends Token implements MetamodelTokenConstants {
      * @param line is the line index in the metamodel where the token resides. Cannot be smaller than 1.
      * @param column is the column index, meaning where the token starts in the line.
      * @param length is the length of the token in the metamodel.
+     * @param eObject is the corresponding eObject from which this token was extracted.
      */
-    public MetamodelToken(int type, String file, int line, int column, int length) {
-        super(type, file, line, column, length);
+    public MetamodelToken(int type, String file, EObject eObject) {
+        super(type, file, -1);
+        this.eObject = Optional.of(eObject);
+    }
+
+    public MetamodelToken(int type, String file) {
+        super(type, file, -1);
+        this.eObject = Optional.empty();
+    }
+
+    public Optional<EObject> getEObject() {
+        return eObject;
     }
 
     @Override
@@ -34,8 +51,13 @@ public class MetamodelToken extends Token implements MetamodelTokenConstants {
             case REFERENCE -> "EReference";
             case ATTRIBUTE -> "EAttribute";
             case PARAMETER -> "EParameter";
-            case INTERFACE -> "EInterface";
+            case INTERFACE -> "EClass (Interface)";
             case SUPER_TYPE -> "ESuperType";
+            case ID_ATTRIBUTE -> "EAttribute (ID)";
+            case CONTAINMENT -> "EReference (Containment)";
+            case ABSTRACT_CLASS -> "EClass (Abstract)";
+            case RETURN_TYPE -> "EClassifier (Return Type)";
+            case THROWS_DECLARATION -> "EClassifier (Exception)";
             case FILE_END -> "(EOF)";
             default -> "<UNKNOWN" + type + ">";
         };

+ 5 - 0
jplag.frontend.emf/src/main/java/de/jplag/emf/MetamodelTokenConstants.java

@@ -19,6 +19,11 @@ public interface MetamodelTokenConstants extends TokenConstants {
     int PARAMETER = 11;
     int INTERFACE = 12;
     int SUPER_TYPE = 13;
+    int ID_ATTRIBUTE = 14;
+    int CONTAINMENT = 15;
+    int ABSTRACT_CLASS = 16;
+    int RETURN_TYPE = 17;
+    int THROWS_DECLARATION = 18;
 
     /*
      * Number of token constants:

+ 70 - 0
jplag.frontend.emf/src/main/java/de/jplag/emf/MetamodelTreeView.java

@@ -0,0 +1,70 @@
+package de.jplag.emf;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.emf.ecore.ENamedElement;
+
+public class MetamodelTreeView {
+    private final String fileName;
+    private final List<MetamodelToken> tokens;
+    private int lineIndex;
+    private int columnIndex;
+    private final StringBuilder viewBuilder;
+    private final static String INDENTATION = "  ";
+
+    public MetamodelTreeView(String fileName) {
+        this.fileName = fileName;
+        tokens = new ArrayList<>();
+        viewBuilder = new StringBuilder();
+    }
+
+    public void addToken(MetamodelToken token, int treeDepth) {
+        token.getEObject().ifPresent(it -> {
+            tokens.add(token);
+
+            String tokenText = token.type2string();
+            if (it instanceof ENamedElement element) {
+                tokenText = element.getName() + " : " + tokenText;
+            }
+            token.setLength(tokenText.length());
+
+            String prefix = ""; // TS TODO implement prefix for non new lines
+            if (prefix.isEmpty()) {
+                for (int i = 0; i < treeDepth; i++) {
+                    tokenText = INDENTATION + tokenText;
+                    columnIndex += INDENTATION.length();
+                }
+            } else {
+                tokenText = prefix + tokenText;
+                columnIndex += prefix.length();
+            }
+            viewBuilder.append(tokenText);
+
+            token.setLine(lineIndex + 1);
+            token.setColumn(columnIndex + 1);
+
+            lineIndex++;
+            columnIndex = 0;
+            viewBuilder.append(System.lineSeparator());
+        });
+
+    }
+
+    public void writeToFile(File directory, String suffix) {
+        File treeViewFile = fileName.isEmpty() ? new File(directory.toString() + suffix) : new File(directory, fileName + suffix);
+        try (BufferedWriter writer = new BufferedWriter(new FileWriter(treeViewFile));) {
+            if (!treeViewFile.exists()) {
+                treeViewFile.createNewFile();
+            }
+            writer.append(viewBuilder.toString());
+        } catch (IOException exception) {
+            exception.printStackTrace();
+        }
+    }
+
+}

+ 115 - 0
jplag.frontend.emf/src/main/java/de/jplag/emf/TokenGeneratingMetamodelVisitor.java

@@ -0,0 +1,115 @@
+package de.jplag.emf;
+
+import static de.jplag.emf.MetamodelTokenConstants.*;
+import static de.jplag.emf.MetamodelTokenConstants.ANNOTATION;
+import static de.jplag.emf.MetamodelTokenConstants.ATTRIBUTE;
+import static de.jplag.emf.MetamodelTokenConstants.CLASS;
+import static de.jplag.emf.MetamodelTokenConstants.DATATYPE;
+import static de.jplag.emf.MetamodelTokenConstants.ENUM;
+import static de.jplag.emf.MetamodelTokenConstants.ENUM_LITERAL;
+import static de.jplag.emf.MetamodelTokenConstants.INTERFACE;
+import static de.jplag.emf.MetamodelTokenConstants.OPERATION;
+import static de.jplag.emf.MetamodelTokenConstants.PACKAGE;
+import static de.jplag.emf.MetamodelTokenConstants.PARAMETER;
+import static de.jplag.emf.MetamodelTokenConstants.REFERENCE;
+import static de.jplag.emf.MetamodelTokenConstants.SUPER_TYPE;
+
+import org.eclipse.emf.ecore.EAnnotation;
+import org.eclipse.emf.ecore.EAttribute;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EClassifier;
+import org.eclipse.emf.ecore.EDataType;
+import org.eclipse.emf.ecore.EEnum;
+import org.eclipse.emf.ecore.EEnumLiteral;
+import org.eclipse.emf.ecore.EOperation;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EParameter;
+import org.eclipse.emf.ecore.EReference;
+
+public class TokenGeneratingMetamodelVisitor extends AbstractMetamodelVisitor {
+    private EcoreParser parser;
+
+    public TokenGeneratingMetamodelVisitor(EcoreParser parser) {
+        this.parser = parser;
+    }
+
+    @Override
+    protected void visitEAnnotation(EAnnotation eAnnotation) {
+        parser.addToken(ANNOTATION, eAnnotation);
+    }
+
+    @Override
+    protected void visitEAttribute(EAttribute eAttribute) {
+        if (eAttribute.isID()) {
+            parser.addToken(ID_ATTRIBUTE, eAttribute);
+        } else {
+            parser.addToken(ATTRIBUTE, eAttribute);
+        }
+
+    }
+
+    @Override
+    protected void visitEClass(EClass eClass) {
+        if (eClass.isInterface()) {
+            parser.addToken(INTERFACE, eClass);
+        } else if (eClass.isAbstract()) {
+            parser.addToken(ABSTRACT_CLASS, eClass);
+        } else {
+            parser.addToken(CLASS, eClass);
+        }
+        eClass.getESuperTypes().forEach(it -> parser.addToken(SUPER_TYPE, it));
+    }
+
+    @Override
+    protected void visitEClassifier(EClassifier eClassifier) {
+        // TODO TS: Parse type parameters & generic type?
+    }
+
+    @Override
+    protected void visitEDataType(EDataType eDataType) {
+        if (!(eDataType instanceof EEnum)) {
+            parser.addToken(DATATYPE, eDataType);
+        }
+    }
+
+    @Override
+    protected void visitEEnum(EEnum eEnum) {
+        parser.addToken(ENUM, eEnum);
+    }
+
+    @Override
+    protected void visitEEnumLiteral(EEnumLiteral eEnumLiteral) {
+        parser.addToken(ENUM_LITERAL, eEnumLiteral);
+    }
+
+    @Override
+    protected void visitEOperation(EOperation eOperation) {
+        parser.addToken(OPERATION, eOperation);
+        if (eOperation.getEType() != null) {
+            parser.addToken(RETURN_TYPE, eOperation);
+        }
+        eOperation.getEExceptions().forEach(it -> parser.addToken(THROWS_DECLARATION, it));
+        // TODO TS: Parse type parameters & generic type?
+    }
+
+    @Override
+    protected void visitEPackage(EPackage ePackage) {
+        parser.addToken(PACKAGE, ePackage);
+    }
+
+    @Override
+    protected void visitEParameter(EParameter eParameter) {
+        parser.addToken(PARAMETER, eParameter);
+    }
+
+    @Override
+    protected void visitEReference(EReference eReference) {
+        if (eReference.isContainment()) {
+            parser.addToken(CONTAINMENT, eReference);
+        } else {
+            parser.addToken(REFERENCE, eReference);
+        }
+
+    }
+
+}