From 07d647c8602b263a079f236eb723c1c97046a3cc Mon Sep 17 00:00:00 2001
From: Johannes Mey <johannes.mey@tu-dresden.de>
Date: Sun, 15 Sep 2019 22:22:18 +0200
Subject: [PATCH] further work on parser, printer and some fixes for grammar

---
 src/main/jastadd/XMI/XMIPathConstruction.jrag |  28 +-
 src/main/jastadd/XMI/XMIPathResolution.jrag   |  60 +-
 src/main/jastadd/XMI/XMIWriter.jadd           | 239 +++++++-
 src/main/jastadd/ecore.relast                 |  10 +-
 .../inf/st/e2j/parser/EcoreParser.java        | 519 +++++++++++++++++-
 5 files changed, 809 insertions(+), 47 deletions(-)

diff --git a/src/main/jastadd/XMI/XMIPathConstruction.jrag b/src/main/jastadd/XMI/XMIPathConstruction.jrag
index b85806c..a2bbe0b 100644
--- a/src/main/jastadd/XMI/XMIPathConstruction.jrag
+++ b/src/main/jastadd/XMI/XMIPathConstruction.jrag
@@ -4,13 +4,25 @@ aspect XMIPathConstruction {
   syn String EModelElement.xmiReference() = xmiPath() + localName();
 
   eq EDataType.xmiReference() {
-    switch (getName()) {
-      case "EInt":
-        return "ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt";
-      case "EString":
-        return "ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString";
-      default:
-        throw new RuntimeException("Unable to create reference to EDataType " + getName());
+    if (getParent() == null) {
+      switch (getName()) {
+        case "EInt":
+          return "ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt";
+        case "EString":
+          return "ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString";
+        case "EBoolean":
+          return "ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean";
+        case "EJavaObject":
+          return "ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EJavaObject";
+        case "EEnumerator":
+          return "ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EEnumerator";
+        case "EResource":
+          return "ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EResource";
+        default:
+          throw new RuntimeException("Unable to create reference to EDataType " + getName());
+      }
+    } else {
+      return super.xmiReference();
     }
   }
 
@@ -56,7 +68,7 @@ aspect XMIPathConstruction {
 
   eq EEnum.getELiteral(int i).localRelativeName() = "/@eLiterals." + i;
 
-  eq EOperation.getGenericException(int i).localRelativeName() = "/@eGenericExceptions." + i;
+  eq EOperation.getEGenericException(int i).localRelativeName() = "/@eGenericExceptions." + i;
   eq EOperation.getETypeParameter(int i).localRelativeName() = "/@eTypeParameters." + i;
   eq EOperation.getEParameter(int i).localRelativeName() = "/@eParameters." + i;
 
diff --git a/src/main/jastadd/XMI/XMIPathResolution.jrag b/src/main/jastadd/XMI/XMIPathResolution.jrag
index 47a4bb7..e72245b 100644
--- a/src/main/jastadd/XMI/XMIPathResolution.jrag
+++ b/src/main/jastadd/XMI/XMIPathResolution.jrag
@@ -1,7 +1,9 @@
 aspect XMIPathResolution {
 
   refine RefResolverStubs eq ETypedElement.resolveETypeByToken(String id) {
-    return (EClassifier) resolve(id);
+
+    EClassifier result = (EClassifier) resolve(id);
+    return result;
   }
 
   refine RefResolverStubs eq EReference.resolveEOppositeByToken(String id) {
@@ -12,6 +14,14 @@ aspect XMIPathResolution {
     return (EClass) resolve(id);
   }
 
+  refine RefResolverStubs eq EGenericType.resolveEClassifierByToken(String id) {
+    return (EClassifier) resolve(id);
+  }
+
+  refine RefResolverStubs eq EOperation.resolveEExceptionsByToken(String id, int position) {
+    return (EClassifier) resolve(id);
+  }
+
   private static EDataType EDataType.eInt;
   public static final EDataType EDataType.eInt() {
     if (eInt == null) {
@@ -32,6 +42,46 @@ aspect XMIPathResolution {
     return eString;
   }
 
+  private static EDataType EDataType.eBoolean;
+  public static final EDataType EDataType.eBoolean() {
+    if (eBoolean == null) {
+      eBoolean = new EDataType();
+      eBoolean.setInstanceClassName("boolean");
+      eBoolean.setName("EBoolean");
+    }
+    return eBoolean;
+  }
+
+  private static EDataType EDataType.eJavaObject;
+  public static final EDataType EDataType.eJavaObject() {
+    if (eJavaObject == null) {
+      eJavaObject = new EDataType();
+      eJavaObject.setInstanceClassName("java.lang.Object");
+      eJavaObject.setName("EJavaObject");
+    }
+    return eBoolean;
+  }
+
+  private static EDataType EDataType.eEnumerator;
+  public static final EDataType EDataType.eEnumerator() {
+    if (eEnumerator == null) {
+      eEnumerator = new EDataType();
+      eEnumerator.setInstanceClassName("org.eclipse.emf.common.util.Enumerator");
+      eEnumerator.setName("EEnumerator");
+    }
+    return eBoolean;
+  }
+
+  private static EDataType EDataType.eResource;
+  public static final EDataType EDataType.eResource() {
+    if (eResource == null) {
+      eResource = new EDataType();
+      eResource.setInstanceClassName("org.eclipse.emf.ecore.resource.Resource");
+      eResource.setName("EResource");
+    }
+    return eResource;
+  }
+
   syn ASTNode ASTNode.resolve(String uriString) {
 
     // built-in data types
@@ -41,6 +91,14 @@ aspect XMIPathResolution {
           return EDataType.eInt();
         case "http://www.eclipse.org/emf/2002/Ecore#//EString":
           return EDataType.eString();
+        case "http://www.eclipse.org/emf/2002/Ecore#//EBoolean":
+          return EDataType.eBoolean();
+        case "http://www.eclipse.org/emf/2002/Ecore#//EJavaObject":
+          return EDataType.eJavaObject();
+        case "http://www.eclipse.org/emf/2002/Ecore#//EEnumerator":
+          return EDataType.eEnumerator();
+        case "http://www.eclipse.org/emf/2002/Ecore#//EResource":
+          return EDataType.eResource();
         default:
           throw new RuntimeException("Unable to resolve built-in data type '" + uriString + "'");
       }
diff --git a/src/main/jastadd/XMI/XMIWriter.jadd b/src/main/jastadd/XMI/XMIWriter.jadd
index 189c425..62c6955 100644
--- a/src/main/jastadd/XMI/XMIWriter.jadd
+++ b/src/main/jastadd/XMI/XMIWriter.jadd
@@ -19,7 +19,11 @@ aspect XMIWriter {
               " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" +
               " xmlns:ecore=\"http://www.eclipse.org/emf/2002/Ecore\"");
     } else {
-      b.append(context).append(" xsi:type=\"").append(type).append("\"");
+      b.append(context);
+      if (!"".equals(type)) {
+        // TODO whether to print the type can be determined by static analysis!
+        b.append(" xsi:type=\"").append(type).append("\"");
+      }
     }
   }
 
@@ -111,12 +115,12 @@ aspect XMIWriter {
       eTypeParameter.writeXMI(b, "eTypeParameters", indentationLevel + 1);
     }
     // from EClass
-    for (EStructuralFeature eStructuralFeature : getEStructuralFeatureList()) {
-      eStructuralFeature.writeXMI(b, "eStructuralFeatures", indentationLevel + 1);
-    }
     for (EOperation eOperation : getEOperationList()) {
       eOperation.writeXMI(b, "eOperations", indentationLevel + 1);
     }
+    for (EStructuralFeature eStructuralFeature : getEStructuralFeatureList()) {
+      eStructuralFeature.writeXMI(b, "eStructuralFeatures", indentationLevel + 1);
+    }
     for (EGenericType eGenericSuperType : getEGenericSuperTypeList()) {
       eGenericSuperType.writeXMI(b, "eGenericSuperTypes", indentationLevel + 1);
     }
@@ -132,13 +136,30 @@ aspect XMIWriter {
     startXMIElement(b, "ecore:EDataType", context);
     // attributes
     b.append(" name=\"").append(getName()).append("\"");
+    // from EClassifier
+    if (getInstanceClassName() != "") {
+      b.append(" instanceClassName=\"").append(getInstanceClassName()).append("\"");
+    }
+    // from EDataType
+    if (getSerializable() == false) {
+      b.append(" serializable=\"false\"");
+    }
 
     if (numContainedChildren() == 0) {
       b.append("/>\n");
       return;
     }
     b.append(">\n");
+
     // child nodes
+    // from EModelElement
+    for (EAnnotation eAnnotation : getEAnnotationList()) {
+      eAnnotation.writeXMI(b, "eAnnotations", indentationLevel + 1);
+    }
+    // from EClassifier
+    for (ETypeParameter eTypeParameter : getETypeParameterList()) {
+      eTypeParameter.writeXMI(b, "eTypeParameters", indentationLevel + 1);
+    }
 
     // closing tag
     b.append(indentString(indentationLevel)).append("</");
@@ -147,13 +168,139 @@ aspect XMIWriter {
   }
 
   void EGenericType.writeXMI(StringBuilder b, String context, int indentationLevel) {
-    // TODO implement
+    b.append(indentString(indentationLevel)).append("<");
+    startXMIElement(b, "", context);
+    // attributes
+    if (hasETypeParameter()) {
+      b.append(" eTypeParameter=\"").append(getETypeParameter().xmiReference()).append("\"");
+    }
+    if (hasEClassifier()) {
+      b.append(" eClassifier=\"").append(getEClassifier().xmiReference()).append("\"");
+    }
+    if (numContainedChildren() == 0) {
+      b.append("/>\n");
+      return;
+    }
+    b.append(">\n");
+    // child nodes
+    // from EGenericType
+    if (hasEUpperBound()) {
+      getEUpperBound().writeXMI(b, "eUpperBound", indentationLevel + 1);
+    }
+    if (hasELowerBound()) {
+      getELowerBound().writeXMI(b, "eLowerBound", indentationLevel + 1);
+    }
+    for (EGenericType eTypeArgument : getETypeArgumentList()) {
+      eTypeArgument.writeXMI(b, "eTypeArguments", indentationLevel + 1);
+    }
+
+    // closing tag
+    b.append(indentString(indentationLevel)).append("</");
+    endXMIElement(b, "", context);
+    b.append(">\n");
   }
 
   void EOperation.writeXMI(StringBuilder b, String context, int indentationLevel) {
-    // TODO implement
+    b.append(indentString(indentationLevel)).append("<");
+    startXMIElement(b, "", context);
+    // attributes
+    // from ENamedElement
+    b.append(" name=\"").append(getName()).append("\"");
+    // from ETypedElement
+    if (getOrdered() == false) {
+      b.append(" ordered=\"false\"");
+    }
+    if (getUnique() == false) {
+      b.append(" unique=\"false\"");
+    }
+    if (getLowerBound() != 0) {
+      b.append(" lowerBound=\"" + getLowerBound() + "\"");
+    }
+    if (getUpperBound() != 1) {
+      b.append(" upperBound=\"" + getUpperBound() + "\"");
+    }
+    if (hasEType()) {
+      b.append(" eType=\"").append(getEType().xmiReference()).append("\"");
+    }
+    // from EOperation
+    for (EClassifier eException : getEExceptionsList()) {
+      b.append(" eExceptions=\"").append(eException.xmiReference()).append("\"");
+    }
+    if (numContainedChildren() == 0) {
+      b.append("/>\n");
+      return;
+    }
+    b.append(">\n");
+    // child nodes
+    // from EModelElement
+    for (EAnnotation eAnnotation : getEAnnotationList()) {
+      eAnnotation.writeXMI(b, "eAnnotations", indentationLevel + 1);
+    }
+    // from ETypedElement
+    if (hasEGenericType()) {
+      getEGenericType().writeXMI(b, "eGenericType", indentationLevel + 1);
+    }
+    // from EOperation
+    for (EGenericType eGenericException : getEGenericExceptionList()) {
+      eGenericException.writeXMI(b, "eGenericExceptions", indentationLevel + 1);
+    }
+    for (ETypeParameter eTypeParameter : getETypeParameterList()) {
+      eTypeParameter.writeXMI(b, "eTypeParameters", indentationLevel + 1);
+    }
+    for (EParameter eParameter : getEParameterList()) {
+      eParameter.writeXMI(b, "eParameters", indentationLevel + 1);
+    }
+
+    // closing tag
+    b.append(indentString(indentationLevel)).append("</");
+    endXMIElement(b, "", context);
+    b.append(">\n");
+  }
+
+  void EParameter.writeXMI(StringBuilder b, String context, int indentationLevel) {
+    b.append(indentString(indentationLevel)).append("<");
+    startXMIElement(b, "", context);
+    // attributes
+    // from ENamedElement
+    b.append(" name=\"").append(getName()).append("\"");
+    // from ETypedElement
+    if (getOrdered() == false) {
+      b.append(" ordered=\"false\"");
+    }
+    if (getUnique() == false) {
+      b.append(" unique=\"false\"");
+    }
+    if (getLowerBound() != 0) {
+      b.append(" lowerBound=\"" + getLowerBound() + "\"");
+    }
+    if (getUpperBound() != 1) {
+      b.append(" upperBound=\"" + getUpperBound() + "\"");
+    }
+    if (hasEType()) {
+      b.append(" eType=\"").append(getEType().xmiReference()).append("\"");
+    }
+    if (numContainedChildren() == 0) {
+      b.append("/>\n");
+      return;
+    }
+    b.append(">\n");
+    // child nodes
+    // from EModelElement
+    for (EAnnotation eAnnotation : getEAnnotationList()) {
+      eAnnotation.writeXMI(b, "eAnnotations", indentationLevel + 1);
+    }
+    // from ETypedElement
+    if (hasEGenericType()) {
+      getEGenericType().writeXMI(b, "eGenericType", indentationLevel + 1);
+    }
+
+    // closing tag
+    b.append(indentString(indentationLevel)).append("</");
+    endXMIElement(b, "", context);
+    b.append(">\n");
   }
 
+
   abstract void EStructuralFeature.writeXMI(StringBuilder b, String context, int indentationLevel);
 
   void EReference.writeXMI(StringBuilder b, String context, int indentationLevel) {
@@ -175,7 +322,9 @@ aspect XMIWriter {
     if (getUpperBound() != 1) {
       b.append(" upperBound=\"" + getUpperBound() + "\"");
     }
-    b.append(" eType=\"").append(getEType().xmiReference()).append("\"");
+    if (hasEType()) {
+      b.append(" eType=\"").append(getEType().xmiReference()).append("\"");
+    }
     // from EStructuralFEature
     if (getChangeable() == false) {
       b.append(" changeable=\"false\"");
@@ -205,21 +354,20 @@ aspect XMIWriter {
     if (hasEOpposite()) {
       b.append(" eOpposite=\"").append(getEOpposite().xmiReference()).append("\"");
     }
-    if (getNumChild() == 0) {
-      b.append("/>\n");
-      return;
-    }
     if (numContainedChildren() == 0) {
       b.append("/>\n");
       return;
     }
-    System.out.println(numContainedChildren());
     b.append(">\n");
     // child nodes
     // from EModelElement
     for (EAnnotation eAnnotation : getEAnnotationList()) {
       eAnnotation.writeXMI(b, "eAnnotations", indentationLevel + 1);
     }
+    // from ETypedElement
+    if (hasEGenericType()) {
+      getEGenericType().writeXMI(b, "eGenericType", indentationLevel + 1);
+    }
 
     // closing tag
     b.append(indentString(indentationLevel)).append("</");
@@ -246,7 +394,9 @@ aspect XMIWriter {
     if (getUpperBound() != 1) {
       b.append(" upperBound=\"" + getUpperBound() + "\"");
     }
-    b.append(" eType=\"").append(getEType().xmiReference()).append("\"");
+    if (hasEType()) {
+      b.append(" eType=\"").append(getEType().xmiReference()).append("\"");
+    }
     // from EStructuralFEature
     if (getChangeable() == false) {
       b.append(" changeable=\"false\"");
@@ -270,10 +420,6 @@ aspect XMIWriter {
     if (getID() == true) {
       b.append(" iD=\"true\"");
     }
-    if (getNumChild() == 0) {
-      b.append("/>\n");
-      return;
-    }
     if (numContainedChildren() == 0) {
       b.append("/>\n");
       return;
@@ -284,6 +430,10 @@ aspect XMIWriter {
     for (EAnnotation eAnnotation : getEAnnotationList()) {
       eAnnotation.writeXMI(b, "eAnnotations", indentationLevel + 1);
     }
+    // from ETypedElement
+    if (hasEGenericType()) {
+      getEGenericType().writeXMI(b, "eGenericType", indentationLevel + 1);
+    }
 
     // closing tag
     b.append(indentString(indentationLevel)).append("</");
@@ -292,10 +442,61 @@ aspect XMIWriter {
   }
 
   void EAnnotation.writeXMI(StringBuilder b, String context, int indentationLevel) {
-    // TODO implement
+    b.append(indentString(indentationLevel)).append("<");
+    startXMIElement(b, "", context);
+    // attributes
+    b.append(" source=\"").append(getSource()).append("\"");
+
+    if (numContainedChildren() == 0) {
+      b.append("/>\n");
+      return;
+    }
+    b.append(">\n");
+    // child nodes
+    for (EStringToStringMapEntry detail : getDetailList()) {
+      detail.writeXMI(b, "details", indentationLevel + 1);
+    }
+
+    // closing tag
+    b.append(indentString(indentationLevel)).append("</");
+    endXMIElement(b, "", context);
+    b.append(">\n");
+  }
+
+  void EStringToStringMapEntry.writeXMI(StringBuilder b, String context, int indentationLevel) {
+    b.append(indentString(indentationLevel)).append("<");
+    startXMIElement(b, "", context);
+    // attributes
+    b.append(" key=\"").append(getKey()).append("\"");
+    b.append(" value=\"").append(getValue()).append("\"");
+
+    b.append("/>\n");
   }
 
   void ETypeParameter.writeXMI(StringBuilder b, String context, int indentationLevel) {
-    // TODO implement
+    b.append(indentString(indentationLevel)).append("<");
+    startXMIElement(b, "", context);
+    // attributes
+    // from ENamedElement
+    b.append(" name=\"").append(getName()).append("\"");
+    if (numContainedChildren() == 0) {
+      b.append("/>\n");
+      return;
+    }
+    b.append(">\n");
+    // child nodes
+    // from EModelElement
+    for (EAnnotation eAnnotation : getEAnnotationList()) {
+      eAnnotation.writeXMI(b, "eAnnotations", indentationLevel + 1);
+    }
+    // from ETypeParameter
+    for (EGenericType eGenericType : getEBoundList()) {
+      eGenericType.writeXMI(b, "eTypeParameters", indentationLevel + 1);
+    }
+
+    // closing tag
+    b.append(indentString(indentationLevel)).append("</");
+    endXMIElement(b, "ecore:EPackage", context);
+    b.append(">\n");
   }
 }
diff --git a/src/main/jastadd/ecore.relast b/src/main/jastadd/ecore.relast
index bc96c0f..cb8a91d 100644
--- a/src/main/jastadd/ecore.relast
+++ b/src/main/jastadd/ecore.relast
@@ -15,13 +15,13 @@ EEnumLiteral : ENamedElement ::= <Value:int>;
 abstract ETypedElement : ENamedElement ::= [EGenericType] <Ordered:boolean> <Unique:boolean> <LowerBound:int> <UpperBound:int>;
 
 EClass : EClassifier ::= EStructuralFeature* EOperation* EGenericSuperType:EGenericType* <Abstract:boolean> <Interface:boolean>;
-EDataType : EClassifier;
+EDataType : EClassifier ::= <Serializable:boolean>;
 EEnum : EDataType ::= ELiteral:EEnumLiteral*;
 
 abstract EStructuralFeature : ETypedElement ::= <Changeable:boolean> <Volatile:boolean> <Transient:boolean> <DefaultValueLiteral:String> <Unsettable:boolean> <Derived:boolean>;
 EAttribute : EStructuralFeature ::= <ID:boolean>;
 EReference : EStructuralFeature ::= <Containment:boolean> <ResolveProxies:boolean>;
-EOperation : ETypedElement ::= GenericException:EGenericType* ETypeParameter* EParameter*;
+EOperation : ETypedElement ::= EGenericException:EGenericType* ETypeParameter* EParameter*;
 EParameter : ETypedElement;
 
 EStringToStringMapEntry ::= <Key:String> <Value:String>;
@@ -29,14 +29,12 @@ EStringToStringMapEntry ::= <Key:String> <Value:String>;
 ETypeParameter : ENamedElement ::= EBound:EGenericType*;
 EGenericType ::= [EUpperBound:EGenericType] [ELowerBound:EGenericType] ETypeArgument:EGenericType*;
 
-rel ETypedElement.EType -> EClassifier;
+rel ETypedElement.EType? -> EClassifier;
 rel EOperation.EExceptions* -> EClassifier;
 rel EClass.ESuperTypes* -> EClass;
 rel EReference.EOpposite? -> EReference;
 rel EAnnotation.References* -> EObject;
-rel ETypeParameter.GenericTypes* <-> EGenericType.ETypeParameter?;
-
-
+rel EGenericType.ETypeParameter? -> ETypeParameter;
 rel EGenericType.EClassifier? -> EClassifier;
 
 // transient stuff (the inverse directions of the containment relations are missing)
diff --git a/src/main/java/de/tudresden/inf/st/e2j/parser/EcoreParser.java b/src/main/java/de/tudresden/inf/st/e2j/parser/EcoreParser.java
index 16ff7ab..53a6c3e 100644
--- a/src/main/java/de/tudresden/inf/st/e2j/parser/EcoreParser.java
+++ b/src/main/java/de/tudresden/inf/st/e2j/parser/EcoreParser.java
@@ -105,21 +105,26 @@ public class EcoreParser {
     // parse the contained elements
     XMLEvent nextEvent = reader.peek();
     while (true) {
+//      logger.debug("looking at ({}:{}) :'{};.", nextEvent.getLocation().getLineNumber(), nextEvent.getLocation().getColumnNumber(), nextEvent);
       if (nextEvent.isStartElement()) {
         final StartElement nextElement = nextEvent.asStartElement();
         switch (nextElement.getName().getLocalPart()) {
           case "eClassifiers":
             ePackage.addEClassifier(parseEClassifier(reader, nameSpaces));
             break;
+          case "eAnnotations":
+            ePackage.addEAnnotation(parseEAnnotation(reader, nameSpaces));
+            break;
           default:
-            logger.warn("ignoring element {}:{}", nextElement.getName().getPrefix(), nextElement.getName().getLocalPart());
+            logger.warn("ignoring element at ({}:{}) {}:{}", nextEvent.getLocation().getLineNumber(), nextEvent.getLocation().getColumnNumber(), nextElement.getName().getPrefix(), nextElement.getName().getLocalPart());
         }
       } else if (nextEvent.isEndElement() && nextEvent.asEndElement().getName().equals(startElement.getName())) {
+        reader.nextEvent();
         return ePackage;
       } else {
         // ignore all other events
         if (nextEvent.getEventType() != XMLStreamConstants.CHARACTERS)
-          logger.warn("ignoring event {}: '{}'", nextEvent.getEventType(), nextEvent.toString().replace("\n", "\\n"));
+          logger.warn("ignoring event at ({}:{}) {}", nextEvent.getLocation().getLineNumber(), nextEvent.getLocation().getColumnNumber(), nextEvent);
       }
       reader.nextEvent();
       nextEvent = reader.peek();
@@ -222,11 +227,69 @@ public class EcoreParser {
           case "eGenericSuperTypes":
             eClass.addEGenericSuperType(parseEGenericSuperType(reader, nameSpaces));
             break;
+          case "eAnnotations":
+            eClass.addEAnnotation(parseEAnnotation(reader, nameSpaces));
+            break;
           default:
             logger.warn("ignoring element {}:{}", nextElement.getName().getPrefix(), nextElement.getName().getLocalPart());
         }
       } else if (nextEvent.isEndElement() && nextEvent.asEndElement().getName().equals(startElement.getName())) {
+        reader.nextEvent();
         return eClass;
+      } else {
+        // ignore all other events
+        if (nextEvent.getEventType() != XMLStreamConstants.CHARACTERS)
+          logger.warn("in start element {}: ignoring event {}: '{}'",startElement.getName(), nextEvent.getEventType(), nextEvent.toString().replace("\n", "\\n"));
+      }
+
+      reader.nextEvent();
+      nextEvent = reader.peek();
+    }
+  }
+
+  private EAnnotation parseEAnnotation(final XMLEventReader reader, Map<String, String> nameSpaces) throws XMLStreamException {
+
+    EAnnotation eAnnotation = new EAnnotation();
+
+    // get all the properties out of the element
+    StartElement startElement = reader.nextEvent().asStartElement();
+
+    nameSpaces = parseNameSpaces(startElement, nameSpaces);
+
+    for (Iterator attributeIterator = startElement.getAttributes(); attributeIterator.hasNext(); ) {
+      Attribute attribute = (Attribute) attributeIterator.next();
+
+      if (attribute.getName().equals(XSI_TYPE)) {
+        if (!attribute.getValue().equals("ecore:EAnnotation")) {
+          throw new XMIParseException("Expected ecore:EAnnotation but found " + attribute.getValue(), attribute.getLocation());
+        }
+      } else {
+        switch (attribute.getName().getLocalPart()) {
+          // from EAnnotation
+          case "source":
+            eAnnotation.setSource(attribute.getValue());
+            break;
+          default:
+            logger.warn("ignoring attribute {}:{}", attribute.getName().getPrefix(), attribute.getName().getLocalPart());
+        }
+      }
+    }
+
+    // parse the contained elements
+    XMLEvent nextEvent = reader.peek();
+    while (true) {
+      if (nextEvent.isStartElement()) {
+        final StartElement nextElement = nextEvent.asStartElement();
+        switch (nextElement.getName().getLocalPart()) {
+          case "details":
+            eAnnotation.addDetail(parseEStringToMapEntry(reader, nameSpaces));
+            break;
+          default:
+            logger.warn("ignoring element {}:{}", nextElement.getName().getPrefix(), nextElement.getName().getLocalPart());
+        }
+      } else if (nextEvent.isEndElement() && nextEvent.asEndElement().getName().equals(startElement.getName())) {
+        reader.nextEvent();
+        return eAnnotation;
       } else {
         // ignore all other events
         if (nextEvent.getEventType() != XMLStreamConstants.CHARACTERS)
@@ -239,8 +302,80 @@ public class EcoreParser {
   }
 
   private EClassifier parseEDataType(final XMLEventReader reader, Map<String, String> nameSpaces) throws XMLStreamException {
-    // TODO implement
-    return null;
+
+    EDataType eDataType = new EDataType();
+
+    // set default values
+    eDataType.setSerializable(true);
+
+    // get all the properties out of the element
+    StartElement startElement = reader.nextEvent().asStartElement();
+
+    nameSpaces = parseNameSpaces(startElement, nameSpaces);
+
+    for (Iterator attributeIterator = startElement.getAttributes(); attributeIterator.hasNext(); ) {
+      Attribute attribute = (Attribute) attributeIterator.next();
+
+      if (attribute.getName().equals(XSI_TYPE)) {
+        if (!attribute.getValue().equals("ecore:EDataType")) {
+          throw new XMIParseException("Expected ecore:EDataType but found " + attribute.getValue(), attribute.getLocation());
+        }
+      } else {
+        switch (attribute.getName().getLocalPart()) {
+          // from ENamedElement
+          case "name":
+            eDataType.setName(attribute.getValue());
+            break;
+          // from EClassifier
+          case "instanceClassName":
+            eDataType.setInstanceClassName(attribute.getValue());
+            break;
+          case "instanceClass":
+            logger.warn("ignoring transient attribute 'instanceClass' with value '{}'", attribute.getValue());
+            break;
+          case "defaultValue":
+            logger.warn("ignoring transient attribute 'defaultValue' with value '{}'", attribute.getValue());
+            break;
+          case "ePackage":
+            logger.warn("ignoring parent relation 'ePackage' with value '{}'", attribute.getValue());
+            break;
+          // from EDataType
+          case "serializable":
+            eDataType.setSerializable(Boolean.valueOf(attribute.getValue()));
+            break;
+          default:
+            logger.warn("ignoring attribute {}:{}", attribute.getName().getPrefix(), attribute.getName().getLocalPart());
+        }
+      }
+    }
+
+    // parse the contained elements
+    XMLEvent nextEvent = reader.peek();
+    while (true) {
+      if (nextEvent.isStartElement()) {
+        final StartElement nextElement = nextEvent.asStartElement();
+        switch (nextElement.getName().getLocalPart()) {
+          case "eTypeParameters":
+            eDataType.addETypeParameter(parseETypeParameter(reader, nameSpaces));
+            break;
+          case "eAnnotations":
+            eDataType.addEAnnotation(parseEAnnotation(reader, nameSpaces));
+            break;
+          default:
+            logger.warn("ignoring element {}:{}", nextElement.getName().getPrefix(), nextElement.getName().getLocalPart());
+        }
+      } else if (nextEvent.isEndElement() && nextEvent.asEndElement().getName().equals(startElement.getName())) {
+        reader.nextEvent();
+        return eDataType;
+      } else {
+        // ignore all other events
+        if (nextEvent.getEventType() != XMLStreamConstants.CHARACTERS)
+          logger.warn("in start element {}: ignoring event {}: '{}'",startElement.getName(), nextEvent.getEventType(), nextEvent.toString().replace("\n", "\\n"));
+      }
+
+      reader.nextEvent();
+      nextEvent = reader.peek();
+    }
   }
 
   private EClassifier parseEEnum(final XMLEventReader reader, Map<String, String> nameSpaces) throws XMLStreamException {
@@ -249,8 +384,199 @@ public class EcoreParser {
   }
 
   private ETypeParameter parseETypeParameter(final XMLEventReader reader, Map<String, String> nameSpaces) throws XMLStreamException {
-    // TODO implement
-    return null;
+
+    ETypeParameter eTypeParameter = new ETypeParameter();
+
+    // get all the properties out of the element
+    StartElement startElement = reader.nextEvent().asStartElement();
+
+    nameSpaces = parseNameSpaces(startElement, nameSpaces);
+
+    for (Iterator attributeIterator = startElement.getAttributes(); attributeIterator.hasNext(); ) {
+      Attribute attribute = (Attribute) attributeIterator.next();
+
+      if (attribute.getName().equals(XSI_TYPE)) {
+        if (!attribute.getValue().equals("ecore:ETypeParameter")) {
+          throw new XMIParseException("Expected ecore:ETypeParameter but found " + attribute.getValue(), attribute.getLocation());
+        }
+      } else {
+        switch (attribute.getName().getLocalPart()) {
+          // from ENamedElement
+          case "name":
+            eTypeParameter.setName(attribute.getValue());
+            break;
+          // from ETypeParameter
+          default:
+            logger.warn("ignoring attribute {}:{}", attribute.getName().getPrefix(), attribute.getName().getLocalPart());
+        }
+      }
+    }
+
+    // parse the contained elements
+    XMLEvent nextEvent = reader.peek();
+    while (true) {
+      if (nextEvent.isStartElement()) {
+        final StartElement nextElement = nextEvent.asStartElement();
+        switch (nextElement.getName().getLocalPart()) {
+          case "eBounds":
+            eTypeParameter.addEBound(parseEGenericType(reader, nameSpaces));
+            break;
+          case "eAnnotations":
+            eTypeParameter.addEAnnotation(parseEAnnotation(reader, nameSpaces));
+            break;
+          default:
+            logger.warn("ignoring element {}:{}", nextElement.getName().getPrefix(), nextElement.getName().getLocalPart());
+        }
+      } else if (nextEvent.isEndElement() && nextEvent.asEndElement().getName().equals(startElement.getName())) {
+        reader.nextEvent();
+        return eTypeParameter;
+      } else {
+        // ignore all other events
+        if (nextEvent.getEventType() != XMLStreamConstants.CHARACTERS)
+          logger.warn("ignoring event {}: '{}'", nextEvent.getEventType(), nextEvent.toString().replace("\n", "\\n"));
+      }
+
+      reader.nextEvent();
+      nextEvent = reader.peek();
+    }
+  }
+
+  private EParameter parseEParameter(final XMLEventReader reader, Map<String, String> nameSpaces) throws XMLStreamException {
+
+    EParameter eParameter = new EParameter();
+
+    // set default values
+    eParameter.setOrdered(true);
+    eParameter.setUnique(true);
+    eParameter.setUpperBound(1);
+
+    // get all the properties out of the element
+    StartElement startElement = reader.nextEvent().asStartElement();
+
+    nameSpaces = parseNameSpaces(startElement, nameSpaces);
+
+    for (Iterator attributeIterator = startElement.getAttributes(); attributeIterator.hasNext(); ) {
+      Attribute attribute = (Attribute) attributeIterator.next();
+
+      if (attribute.getName().equals(XSI_TYPE)) {
+        if (!attribute.getValue().equals("ecore:EParameter")) {
+          throw new XMIParseException("Expected ecore:EParameter but found " + attribute.getValue(), attribute.getLocation());
+        }
+      } else {
+        switch (attribute.getName().getLocalPart()) {
+          // from ENamedElement
+          case "name":
+            eParameter.setName(attribute.getValue());
+            break;
+          // from ETypedElement
+          case "ordered":
+            eParameter.setOrdered(Boolean.valueOf(attribute.getValue()));
+            break;
+          case "unique":
+            eParameter.setUnique(Boolean.valueOf(attribute.getValue()));
+            break;
+          case "lowerBound":
+            eParameter.setLowerBound(Integer.valueOf(attribute.getValue()));
+            break;
+          case "upperBound":
+            eParameter.setUpperBound(Integer.valueOf(attribute.getValue()));
+            break;
+          case "eType":
+            eParameter.setEType(EClassifier.createRefDirection(attribute.getValue()));
+            break;
+          default:
+            logger.warn("ignoring attribute {}:{}", attribute.getName().getPrefix(), attribute.getName().getLocalPart());
+        }
+      }
+    }
+
+    // parse the contained elements
+    XMLEvent nextEvent = reader.peek();
+    while (true) {
+      if (nextEvent.isStartElement()) {
+        final StartElement nextElement = nextEvent.asStartElement();
+        switch (nextElement.getName().getLocalPart()) {
+          case "eGenericType":
+            eParameter.setEGenericType(parseEGenericType(reader, nameSpaces));
+            break;
+          case "eAnnotations":
+            eParameter.addEAnnotation(parseEAnnotation(reader, nameSpaces));
+            break;
+          default:
+            logger.warn("ignoring element {}:{}", nextElement.getName().getPrefix(), nextElement.getName().getLocalPart());
+        }
+      } else if (nextEvent.isEndElement() && nextEvent.asEndElement().getName().equals(startElement.getName())) {
+        reader.nextEvent();
+        return eParameter;
+      } else {
+        // ignore all other events
+        if (nextEvent.getEventType() != XMLStreamConstants.CHARACTERS)
+          logger.warn("ignoring event {}: '{}'", nextEvent.getEventType(), nextEvent.toString().replace("\n", "\\n"));
+      }
+
+      reader.nextEvent();
+      nextEvent = reader.peek();
+    }
+  }
+
+  private EGenericType parseEGenericType(final XMLEventReader reader, Map<String, String> nameSpaces) throws XMLStreamException {
+
+    EGenericType eGenericType = new EGenericType();
+
+    // get all the properties out of the element
+    StartElement startElement = reader.nextEvent().asStartElement();
+
+    nameSpaces = parseNameSpaces(startElement, nameSpaces);
+
+    for (Iterator attributeIterator = startElement.getAttributes(); attributeIterator.hasNext(); ) {
+      Attribute attribute = (Attribute) attributeIterator.next();
+
+      if (attribute.getName().equals(XSI_TYPE)) {
+        if (!attribute.getValue().equals("ecore:EGenericType")) {
+          throw new XMIParseException("Expected ecore:EGenericType but found " + attribute.getValue(), attribute.getLocation());
+        }
+      } else {
+        switch (attribute.getName().getLocalPart()) {
+          // relations
+          case "eClassifier":
+            eGenericType.setEClassifier(EClassifier.createRefDirection(attribute.getValue()));
+            break;
+          default:
+            logger.warn("ignoring attribute {}:{}", attribute.getName().getPrefix(), attribute.getName().getLocalPart());
+        }
+      }
+    }
+
+    // parse the contained elements
+    XMLEvent nextEvent = reader.peek();
+    while (true) {
+      if (nextEvent.isStartElement()) {
+        final StartElement nextElement = nextEvent.asStartElement();
+        switch (nextElement.getName().getLocalPart()) {
+          case "eUpperBound":
+            eGenericType.setEUpperBound(parseEGenericType(reader, nameSpaces));
+            break;
+          case "eLowerBound":
+            eGenericType.setELowerBound(parseEGenericType(reader, nameSpaces));
+            break;
+          case "eTypeArguments":
+            eGenericType.addETypeArgument(parseEGenericType(reader, nameSpaces));
+            break;
+          default:
+            logger.warn("ignoring element {}:{}", nextElement.getName().getPrefix(), nextElement.getName().getLocalPart());
+        }
+      } else if (nextEvent.isEndElement() && nextEvent.asEndElement().getName().equals(startElement.getName())) {
+        reader.nextEvent();
+        return eGenericType;
+      } else {
+        // ignore all other events
+        if (nextEvent.getEventType() != XMLStreamConstants.CHARACTERS)
+          logger.warn("ignoring event {}: '{}'", nextEvent.getEventType(), nextEvent.toString().replace("\n", "\\n"));
+      }
+
+      reader.nextEvent();
+      nextEvent = reader.peek();
+    }
   }
 
   private EStructuralFeature parseEStructuralFeature(final XMLEventReader reader, Map<String, String> nameSpaces) throws XMLStreamException {
@@ -275,13 +601,102 @@ public class EcoreParser {
   }
 
   private EOperation parseEOperation(final XMLEventReader reader, Map<String, String> nameSpaces) throws XMLStreamException {
-    // TODO implement
-    return null;
+
+    EOperation eOperation = new EOperation();
+
+    // set default values
+    eOperation.setOrdered(true);
+    eOperation.setUnique(true);
+    eOperation.setUpperBound(1);
+
+    // get all the properties out of the element
+    StartElement startElement = reader.nextEvent().asStartElement();
+
+    nameSpaces = parseNameSpaces(startElement, nameSpaces);
+
+    for (Iterator attributeIterator = startElement.getAttributes(); attributeIterator.hasNext(); ) {
+      Attribute attribute = (Attribute) attributeIterator.next();
+
+      if (attribute.getName().equals(XSI_TYPE)) {
+        if (!attribute.getValue().equals("ecore:EOperation")) {
+          throw new XMIParseException("Expected ecore:EOperation but found " + attribute.getValue(), attribute.getLocation());
+        }
+      } else {
+        switch (attribute.getName().getLocalPart()) {
+          // from ENamedElement
+          case "name":
+            eOperation.setName(attribute.getValue());
+            break;
+          // from ETypedElement
+          case "ordered":
+            eOperation.setOrdered(Boolean.valueOf(attribute.getValue()));
+            break;
+          case "unique":
+            eOperation.setUnique(Boolean.valueOf(attribute.getValue()));
+            break;
+          case "lowerBound":
+            eOperation.setLowerBound(Integer.valueOf(attribute.getValue()));
+            break;
+          case "upperBound":
+            eOperation.setUpperBound(Integer.valueOf(attribute.getValue()));
+            break;
+          case "eType":
+            eOperation.setEType(EClassifier.createRefDirection(attribute.getValue()));
+            break;
+          // from EOperation
+          case "eExceptions":
+            for (String eException : attribute.getValue().split(" ")) {
+              eOperation.addEExceptions(EClassifier.createRefDirection(eException));
+            }
+            break;
+          default:
+            logger.warn("ignoring attribute {}:{}", attribute.getName().getPrefix(), attribute.getName().getLocalPart());
+        }
+      }
+    }
+
+    // parse the contained elements
+    XMLEvent nextEvent = reader.peek();
+    while (true) {
+      if (nextEvent.isStartElement()) {
+        final StartElement nextElement = nextEvent.asStartElement();
+        switch (nextElement.getName().getLocalPart()) {
+          case "eGenericType":
+            eOperation.setEGenericType(parseEGenericType(reader, nameSpaces));
+            logger.info("setting EGenericType in EOperation {} to {}", eOperation.getName(), eOperation.getEGenericType());
+            break;
+          case "eGenericExceptions":
+            eOperation.addEGenericException(parseEGenericType(reader, nameSpaces));
+            break;
+          case "eTypeParameters":
+            eOperation.addETypeParameter(parseETypeParameter(reader, nameSpaces));
+            break;
+          case "eParameters":
+            eOperation.addEParameter(parseEParameter(reader, nameSpaces));
+            break;
+          case "eAnnotations":
+            eOperation.addEAnnotation(parseEAnnotation(reader, nameSpaces));
+            break;
+          default:
+            logger.warn("ignoring element {}:{}", nextElement.getName().getPrefix(), nextElement.getName().getLocalPart());
+        }
+      } else if (nextEvent.isEndElement() && nextEvent.asEndElement().getName().equals(startElement.getName())) {
+        reader.nextEvent();
+        return eOperation;
+      } else {
+        // ignore all other events
+        if (nextEvent.getEventType() != XMLStreamConstants.CHARACTERS)
+          logger.warn("ignoring event {}: '{}'", nextEvent.getEventType(), nextEvent.toString().replace("\n", "\\n"));
+      }
+
+      reader.nextEvent();
+      nextEvent = reader.peek();
+    }
   }
 
   private EGenericType parseEGenericSuperType(final XMLEventReader reader, Map<String, String> nameSpaces) throws XMLStreamException {
     // TODO implement
-    return null;
+    throw new RuntimeException("This feature is not implemented yet!");
   }
 
   private EAttribute parseEAttribute(final XMLEventReader reader, Map<String, String> nameSpaces) throws XMLStreamException {
@@ -328,7 +743,7 @@ public class EcoreParser {
           case "eType":
             eAttribute.setEType(EClassifier.createRefDirection(attribute.getValue()));
             break;
-            // from EStructuralFeature
+          // from EStructuralFeature
           case "changeable":
             eAttribute.setChangeable(Boolean.valueOf(attribute.getValue()));
             break;
@@ -367,7 +782,20 @@ public class EcoreParser {
     // parse the contained elements
     XMLEvent nextEvent = reader.peek();
     while (true) {
-      if (nextEvent.isEndElement() && nextEvent.asEndElement().getName().equals(startElement.getName())) {
+      if (nextEvent.isStartElement()) {
+        final StartElement nextElement = nextEvent.asStartElement();
+        switch (nextElement.getName().getLocalPart()) {
+          case "eGenericType":
+            eAttribute.setEGenericType(parseEGenericType(reader, nameSpaces));
+            break;
+          case "eAnnotations":
+            eAttribute.addEAnnotation(parseEAnnotation(reader, nameSpaces));
+            break;
+          default:
+            logger.warn("ignoring element {}:{}", nextElement.getName().getPrefix(), nextElement.getName().getLocalPart());
+        }
+      } else if (nextEvent.isEndElement() && nextEvent.asEndElement().getName().equals(startElement.getName())) {
+        reader.nextEvent();
         return eAttribute;
       } else {
         // ignore all other events
@@ -426,7 +854,7 @@ public class EcoreParser {
           case "eType":
             eReference.setEType(EClassifier.createRefDirection(attribute.getValue()));
             break;
-            // from EStructuralFeature
+          // from EStructuralFeature
           case "changeable":
             eReference.setChangeable(Boolean.valueOf(attribute.getValue()));
             break;
@@ -467,7 +895,20 @@ public class EcoreParser {
     // parse the contained elements
     XMLEvent nextEvent = reader.peek();
     while (true) {
-      if (nextEvent.isEndElement() && nextEvent.asEndElement().getName().equals(startElement.getName())) {
+      if (nextEvent.isStartElement()) {
+        final StartElement nextElement = nextEvent.asStartElement();
+        switch (nextElement.getName().getLocalPart()) {
+          case "eGenericType":
+            eReference.setEGenericType(parseEGenericType(reader, nameSpaces));
+            break;
+          case "eAnnotations":
+            eReference.addEAnnotation(parseEAnnotation(reader, nameSpaces));
+            break;
+          default:
+            logger.warn("ignoring element {}:{}", nextElement.getName().getPrefix(), nextElement.getName().getLocalPart());
+        }
+      } else if (nextEvent.isEndElement() && nextEvent.asEndElement().getName().equals(startElement.getName())) {
+        reader.nextEvent();
         return eReference;
       } else {
         // ignore all other events
@@ -479,4 +920,56 @@ public class EcoreParser {
       nextEvent = reader.peek();
     }
   }
+
+  // special methods
+
+  private EStringToStringMapEntry parseEStringToMapEntry(final XMLEventReader reader, Map<String, String> nameSpaces) throws XMLStreamException {
+    EStringToStringMapEntry eStringToStringMapEntry = new EStringToStringMapEntry();
+
+
+    // get all the properties out of the element
+    StartElement startElement = reader.nextEvent().asStartElement();
+
+    nameSpaces = parseNameSpaces(startElement, nameSpaces);
+
+    for (Iterator attributeIterator = startElement.getAttributes(); attributeIterator.hasNext(); ) {
+      Attribute attribute = (Attribute) attributeIterator.next();
+
+      if (attribute.getName().equals(XSI_TYPE)) {
+        if (!attribute.getValue().equals("ecore:EStringToStringMapEntry")) {
+          throw new XMIParseException("Expected ecore:EReference but found " + attribute.getValue(), attribute.getLocation());
+        }
+      } else {
+
+        switch (attribute.getName().getLocalPart()) {
+          // from EStringToStringMapEntry
+          case "key":
+            eStringToStringMapEntry.setKey(attribute.getValue());
+            break;
+          // from EStringToStringMapEntry
+          case "value":
+            eStringToStringMapEntry.setValue(attribute.getValue());
+            break;
+          default:
+            logger.warn("ignoring attribute {}:{}", attribute.getName().getPrefix(), attribute.getName().getLocalPart());
+        }
+      }
+    }
+
+    // parse the contained elements
+    XMLEvent nextEvent = reader.peek();
+    while (true) {
+      if (nextEvent.isEndElement() && nextEvent.asEndElement().getName().equals(startElement.getName())) {
+        reader.nextEvent();
+        return eStringToStringMapEntry;
+      } else {
+        // ignore all other events
+        if (nextEvent.getEventType() != XMLStreamConstants.CHARACTERS)
+          logger.warn("ignoring event {}: '{}'", nextEvent.getEventType(), nextEvent.toString().replace("\n", "\\n"));
+      }
+
+      reader.nextEvent();
+      nextEvent = reader.peek();
+    }
+  }
 }
-- 
GitLab