Merge pull request #1 from eMoflon/feature/deserializer

Feature/deserializer
This commit is contained in:
Janik 2024-01-05 14:45:50 +01:00 committed by GitHub
commit 784e2c538e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
57 changed files with 2025 additions and 1053 deletions

View file

@ -32,3 +32,44 @@ Enter the path to the Jar file in the field provided.
🎉 You can now use the Modeling Language to its full extent directly in VSCode!
### Stand-alone use
The CLI can also be used independently of the VSCode plugin. In this case, input and output are usually via files,
the path of which is also specified in the command.
In general, the CLI can be called by executing the JAR file.
```shell
java -jar ./target/model-modeling-language-cli-1.0-SNAPSHOT.jar
```
In the following we use the alias `mmlcli`.
```text
Usage: mmlcli [-hV] [COMMAND]
-h, --help Show this help message and exit.
-V, --version Print version information and exit.
```
#### Generate
The `generate` command is used to generate Ecore and XMI files from the serialized MML format. By default, the input
is via STDIN, the output is in the form of the generated files in a specified directory. Optionally, a file whose
content is in serialized MML format can be specified instead of the STDIN input.
```text
Usage: mmlcli generate [-f[=SERIALIZED]] <projectName> <outputDirectory>
<projectName> Name of the entire project/workspace
<outputDirectory> Path to the output directory
-f, --file[=SERIALIZED] Path to the serialized workspace as json file
```
#### Serialize
The serialize command is used to serialize Ecore files into the MML format. The entry is made by specifying a path
to an Ecore file. The output is via STDOUT by default, but optionally a path to an output file can also be specified
in which the serialized model is to be saved.
```text
Usage: mmlcli serialize [-o[=SERIALIZED]] <ecoreFile>
<ecoreFile> Path to an Ecore file
-o, --out[=SERIALIZED] Path to the output directory
```

View file

@ -45,7 +45,6 @@
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>META-INF/**</exclude>
<exclude>about.html</exclude>
<exclude>about.ini</exclude>
<exclude>about.mappings</exclude>
@ -55,6 +54,30 @@
<exclude>plugin.xml</exclude>
</excludes>
</filter>
<filter>
<artifact>com.google.code.gson:gson</artifact>
<excludes>
<exclude>META-INF/**</exclude>
</excludes>
</filter>
<filter>
<artifact>org.eclipse.emf:*</artifact>
<excludes>
<exclude>META-INF/**</exclude>
</excludes>
</filter>
<filter>
<artifact>info.picocli:picocli</artifact>
<excludes>
<exclude>META-INF/**</exclude>
</excludes>
</filter>
<filter>
<artifact>commons-io:commons-io</artifact>
<excludes>
<exclude>META-INF/**</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
@ -62,6 +85,28 @@
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.1</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>junit-jupiter-api</artifactId>
<groupId>org.junit.jupiter</groupId>
</exclusion>
<exclusion>
<artifactId>junit-jupiter-params</artifactId>
<groupId>org.junit.jupiter</groupId>
</exclusion>
<exclusion>
<artifactId>junit-jupiter-engine</artifactId>
<groupId>org.junit.jupiter</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<properties>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>

52
pom.xml
View file

@ -58,7 +58,6 @@
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>META-INF/**</exclude>
<exclude>about.html</exclude>
<exclude>about.ini</exclude>
<exclude>about.mappings</exclude>
@ -68,6 +67,30 @@
<exclude>plugin.xml</exclude>
</excludes>
</filter>
<filter>
<artifact>com.google.code.gson:gson</artifact>
<excludes>
<exclude>META-INF/**</exclude>
</excludes>
</filter>
<filter>
<artifact>org.eclipse.emf:*</artifact>
<excludes>
<exclude>META-INF/**</exclude>
</excludes>
</filter>
<filter>
<artifact>info.picocli:picocli</artifact>
<excludes>
<exclude>META-INF/**</exclude>
</excludes>
</filter>
<filter>
<artifact>commons-io:commons-io</artifact>
<excludes>
<exclude>META-INF/**</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
@ -82,6 +105,17 @@
<artifactId>picocli</artifactId>
<version>4.7.5</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.15.1</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.emf</groupId>
<artifactId>org.eclipse.emf.ecore</artifactId>
@ -96,11 +130,27 @@
<groupId>org.eclipse.emf</groupId>
<artifactId>org.eclipse.emf.ecore.xmi</artifactId>
<version>2.36.0</version>
<exclusions>
<exclusion>
<groupId>org.eclipse.emf</groupId>
<artifactId>org.eclipse.emf.ecore</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.emf</groupId>
<artifactId>org.eclipse.emf.edit</artifactId>
<version>2.20.0</version>
<exclusions>
<exclusion>
<groupId>org.eclipse.emf</groupId>
<artifactId>org.eclipse.emf.common</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.emf</groupId>
<artifactId>org.eclipse.emf.ecore</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>

View file

@ -1,9 +1,10 @@
package de.nexus.mmlcli;
import de.nexus.mmlcli.generator.GeneratorCommand;
import de.nexus.mmlcli.serializer.SerializeCommand;
import picocli.CommandLine;
@CommandLine.Command(name = "mmlcli", mixinStandardHelpOptions = true, version = "v1.0.0", description = "CLI for interaction between MML, EMF and HiPE", subcommands = {GeneratorCommand.class})
@CommandLine.Command(name = "mmlcli", mixinStandardHelpOptions = true, version = "v1.0.0", description = "CLI for interaction between MML, EMF and HiPE", subcommands = {GeneratorCommand.class, SerializeCommand.class})
public class ModelModelingLanguageCLI {
public static void main(String... args) {
int exitCode = new CommandLine(new ModelModelingLanguageCLI()).execute(args);

View file

@ -0,0 +1,27 @@
package de.nexus.mmlcli.entities.instance;
/**
* Dataclass for an instance attribute
*/
public class AttributeEntry<T> {
private String name;
private String typeId;
private T value;
private boolean isEnumType;
public String getTypeId() {
return typeId;
}
public String getName() {
return name;
}
public T getValue() {
return value;
}
public boolean isEnumType() {
return isEnumType;
}
}

View file

@ -0,0 +1,19 @@
package de.nexus.mmlcli.entities.instance;
import java.util.ArrayList;
/**
* Dataclass for a generator of a single XMI file
*/
public class GeneratorInstance {
private String instanceName;
private ArrayList<ObjectInstance> instances;
public String getInstanceName() {
return instanceName;
}
public ArrayList<ObjectInstance> getInstances() {
return instances;
}
}

View file

@ -0,0 +1,14 @@
package de.nexus.mmlcli.entities.instance;
import java.util.ArrayList;
/**
* Dataclass for list of generators for multiple XMI files
*/
public class GeneratorInstanceWrapper {
private final ArrayList<GeneratorInstance> serializedInstances = new ArrayList<>();
public ArrayList<GeneratorInstance> getSerializedInstances() {
return serializedInstances;
}
}

View file

@ -0,0 +1,34 @@
package de.nexus.mmlcli.entities.instance;
import java.util.ArrayList;
/**
* Dataclass for an instance
*/
public class ObjectInstance {
private String referenceId;
private String referenceTypeId;
private String name;
private ArrayList<AttributeEntry<?>> attributes;
private ArrayList<ReferenceEntry> references;
public String getReferenceId() {
return referenceId;
}
public String getReferenceTypeId() {
return referenceTypeId;
}
public String getName() {
return name;
}
public ArrayList<AttributeEntry<?>> getAttributes() {
return attributes;
}
public ArrayList<ReferenceEntry> getReferences() {
return references;
}
}

View file

@ -0,0 +1,24 @@
package de.nexus.mmlcli.entities.instance;
import java.util.ArrayList;
/**
* Dataclass for an instance reference
*/
public class ReferenceEntry {
private String name;
private String typeId;
private ArrayList<String> referencedIds;
public String getName() {
return name;
}
public String getTypeId() {
return typeId;
}
public ArrayList<String> getReferencedIds() {
return referencedIds;
}
}

View file

@ -0,0 +1,105 @@
package de.nexus.mmlcli.entities.model;
import de.nexus.mmlcli.serializer.EcoreIdResolver;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EReference;
import java.util.ArrayList;
import java.util.UUID;
import java.util.stream.Collectors;
/***
* Dataclass for a class-like (Abstract class, class or interface)
*/
public class AbstractClassEntity {
private String referenceId;
private String name;
private boolean isAbstract;
private boolean isInterface;
private final ArrayList<AttributeEntity<?>> attributes = new ArrayList<>();
private final ArrayList<CReferenceEntity> references = new ArrayList<>();
private final ArrayList<String> extendsIds = new ArrayList<>();
private final ArrayList<String> implementsIds = new ArrayList<>();
public String getReferenceId() {
return referenceId;
}
public String getName() {
return name;
}
public boolean isAbstract() {
return isAbstract;
}
public boolean isInterface() {
return isInterface;
}
public ArrayList<AttributeEntity<?>> getAttributes() {
return attributes;
}
public ArrayList<CReferenceEntity> getReferences() {
return references;
}
public ArrayList<String> getExtendsIds() {
return extendsIds;
}
public ArrayList<String> getImplementsIds() {
return implementsIds;
}
private void setReferenceId(String referenceId) {
this.referenceId = referenceId;
}
private void setName(String name) {
this.name = name;
}
private void setAbstract(boolean anAbstract) {
isAbstract = anAbstract;
}
private void setInterface(boolean anInterface) {
isInterface = anInterface;
}
@Override
public String toString() {
String attributeString = attributes.isEmpty() ? ""
: "\n" + attributes.stream().map(AttributeEntity::toString).collect(Collectors.joining(",")) + "\n";
String referenceString = references.isEmpty() ? ""
: "\n" + references.stream().map(CReferenceEntity::toString).collect(Collectors.joining(",")) + "\n";
return String.format("%s(isAbstract:%b|isInterface:%b||%s||%s)", name, isAbstract, isInterface, attributeString,
referenceString);
}
public static AbstractClassEntity fromEClass(EClass clazz, EcoreIdResolver idResolver) {
AbstractClassEntity classEntity = new AbstractClassEntity();
UUID uuid = idResolver.resolveId(clazz);
classEntity.setReferenceId(uuid.toString());
classEntity.setName(clazz.getName());
classEntity.setAbstract(clazz.isAbstract());
classEntity.setInterface(clazz.isInterface());
clazz.getEStructuralFeatures().forEach(sFeat -> {
if (sFeat instanceof EAttribute attr) {
classEntity.attributes.add(AttributeEntity.fromEAttribute(attr, idResolver));
} else if (sFeat instanceof EReference ref) {
classEntity.references.add(CReferenceEntity.fromEReference(ref, idResolver));
}
});
classEntity.extendsIds.addAll(clazz.getESuperTypes().stream().map(sClazz -> idResolver.resolveId(sClazz).toString()).toList());
return classEntity;
}
}

View file

@ -0,0 +1,120 @@
package de.nexus.mmlcli.entities.model;
import de.nexus.mmlcli.generator.EmfGraphBuilderUtils;
import de.nexus.mmlcli.serializer.EcoreIdResolver;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EEnumLiteral;
import java.util.UUID;
/**
* Dataclass for a class attribute
*/
public class AttributeEntity<T> {
private String referenceId;
private String name;
private String type;
private boolean isEnumType;
private boolean hasDefaultValue;
private T defaultValue;
private ClassElementModifiers modifiers;
public String getReferenceId() {
return referenceId;
}
public String getName() {
return name;
}
public String getType() {
return type;
}
public boolean isEnumType() {
return isEnumType;
}
public boolean isHasDefaultValue() {
return hasDefaultValue;
}
public T getDefaultValue() {
return defaultValue;
}
public ClassElementModifiers getModifiers() {
return modifiers;
}
private void setReferenceId(String referenceId) {
this.referenceId = referenceId;
}
private void setName(String name) {
this.name = name;
}
private void setType(String type) {
this.type = type;
}
private void setEnumType(boolean enumType) {
isEnumType = enumType;
}
private void setHasDefaultValue(boolean hasDefaultValue) {
this.hasDefaultValue = hasDefaultValue;
}
private void setDefaultValue(T defaultValue) {
this.defaultValue = defaultValue;
}
private void setModifiers(ClassElementModifiers modifiers) {
this.modifiers = modifiers;
}
@Override
public String toString() {
return String.format("%s<%s>", name, type);
}
public static AttributeEntity<?> fromEAttribute(EAttribute eAttribute, EcoreIdResolver idResolver) {
AttributeEntity<?> attribute = new AttributeEntity<>();
UUID uuid = idResolver.resolveId(eAttribute);
attribute.setReferenceId(uuid.toString());
attribute.setName(eAttribute.getName());
if (eAttribute.getEType() instanceof EEnum) {
attribute.setEnumType(true);
attribute.setType(idResolver.resolveId(eAttribute.getEType()).toString());
if (eAttribute.getDefaultValue() != null && !EmfGraphBuilderUtils.isETypeDefaultValue((EDataType) eAttribute.getEType(), eAttribute.getDefaultValue())) {
attribute.setHasDefaultValue(true);
attribute.setDefaultValue(EmfGraphBuilderUtils.mapVals("-", idResolver.resolveId((EEnumLiteral) eAttribute.getDefaultValue())));
} else {
attribute.setHasDefaultValue(false);
}
} else if (eAttribute.getEType() instanceof EDataType) {
attribute.setEnumType(false);
attribute.setType(EmfGraphBuilderUtils.mapETypes((EDataType) eAttribute.getEType()));
if (eAttribute.getDefaultValue() != null && !EmfGraphBuilderUtils.isETypeDefaultValue((EDataType) eAttribute.getEType(), eAttribute.getDefaultValue())) {
attribute.setHasDefaultValue(true);
attribute.setDefaultValue(EmfGraphBuilderUtils.mapVals(eAttribute.getEAttributeType(), eAttribute.getDefaultValue()));
} else {
attribute.setHasDefaultValue(false);
}
} else {
throw new IllegalArgumentException("Unexpected attribute type: " + eAttribute.getEType().toString());
}
ClassElementModifiers modifiers = new ClassElementModifiers(!eAttribute.isChangeable(), eAttribute.isVolatile(), eAttribute.isTransient(), eAttribute.isUnsettable(), eAttribute.isDerived(), eAttribute.isUnique(), eAttribute.isOrdered(), false, false, eAttribute.isID());
attribute.setModifiers(modifiers);
return attribute;
}
}

View file

@ -0,0 +1,128 @@
package de.nexus.mmlcli.entities.model;
import de.nexus.mmlcli.serializer.EcoreIdResolver;
import org.eclipse.emf.ecore.EReference;
import java.util.UUID;
/**
* Dataclass for class references
*/
public class CReferenceEntity {
private String referenceId;
private String name;
private MultiplicityEntity multiplicity;
private String type;
private ClassElementModifiers modifiers;
private boolean hasOpposite;
private String opposite;
public String getReferenceId() {
return referenceId;
}
public String getName() {
return name;
}
public MultiplicityEntity getMultiplicity() {
return multiplicity;
}
public String getType() {
return type;
}
public ClassElementModifiers getModifiers() {
return modifiers;
}
public boolean isHasOpposite() {
return hasOpposite;
}
public String getOpposite() {
return opposite;
}
private void setReferenceId(String referenceId) {
this.referenceId = referenceId;
}
private void setName(String name) {
this.name = name;
}
private void setMultiplicity(MultiplicityEntity multiplicity) {
this.multiplicity = multiplicity;
}
private void setType(String type) {
this.type = type;
}
private void setModifiers(ClassElementModifiers modifiers) {
this.modifiers = modifiers;
}
private void setHasOpposite(boolean hasOpposite) {
this.hasOpposite = hasOpposite;
}
private void setOpposite(String opposite) {
this.opposite = opposite;
}
@Override
public String toString() {
return String.format("(%s -> %s)", name, type);
}
public static CReferenceEntity fromEReference(EReference eRef, EcoreIdResolver idResolver) {
CReferenceEntity refEntity = new CReferenceEntity();
UUID uuid = idResolver.resolveId(eRef);
refEntity.setReferenceId(uuid.toString());
refEntity.setName(eRef.getName());
refEntity.setType(idResolver.resolveId(eRef.getEType()).toString());
MultiplicityEntity mult;
if (eRef.getLowerBound() != 0) {
if (eRef.getUpperBound() != 1) {
if (eRef.getLowerBound() == 1 && eRef.getUpperBound() == -1) {
mult = new MultiplicityEntity(false, true, false, false, false, 0, 0);
} else {
if (eRef.getUpperBound() == -1) {
mult = new MultiplicityEntity(true, false, false, false, true, eRef.getLowerBound(), 0);
} else {
mult = new MultiplicityEntity(false, false, false, false, false, eRef.getLowerBound(), eRef.getUpperBound());
}
}
} else {
mult = new MultiplicityEntity(false, false, false, false, false, eRef.getLowerBound(), 0);
}
} else {
if (eRef.getUpperBound() != 1) {
if (eRef.getUpperBound() == -1) {
mult = new MultiplicityEntity(false, false, true, false, false, 0, 0);
} else {
mult = new MultiplicityEntity(true, false, false, false, false, 0, eRef.getUpperBound());
}
} else {
mult = new MultiplicityEntity(true, false, false, false, false, 0, 1);
}
}
refEntity.setMultiplicity(mult);
if (eRef.getEOpposite() != null) {
refEntity.setHasOpposite(true);
refEntity.setOpposite(idResolver.resolveId(eRef.getEOpposite()).toString());
} else {
refEntity.setHasOpposite(false);
}
ClassElementModifiers modifiers = new ClassElementModifiers(!eRef.isChangeable(), eRef.isVolatile(), eRef.isTransient(), eRef.isUnsettable(), eRef.isDerived(), eRef.isUnique(), eRef.isOrdered(), eRef.isResolveProxies(), eRef.isContainment(), false);
refEntity.setModifiers(modifiers);
return refEntity;
}
}

View file

@ -0,0 +1,74 @@
package de.nexus.mmlcli.entities.model;
import com.google.gson.annotations.SerializedName;
/**
* Dataclass for class modifiers
*/
public class ClassElementModifiers {
private boolean readonly;
@SerializedName("volatile")
private boolean _volatile;
@SerializedName("transient")
private boolean _transient;
private boolean unsettable;
private boolean derived;
private boolean unique;
private boolean ordered;
private boolean resolve;
private boolean containment;
private boolean id;
public boolean isReadonly() {
return readonly;
}
public boolean isVolatile() {
return _volatile;
}
public boolean isTransient() {
return _transient;
}
public boolean isUnsettable() {
return unsettable;
}
public boolean isDerived() {
return derived;
}
public boolean isUnique() {
return unique;
}
public boolean isOrdered() {
return ordered;
}
public boolean isResolve() {
return resolve;
}
public boolean isContainment() {
return containment;
}
public boolean isId() {
return id;
}
public ClassElementModifiers(boolean readonly, boolean _volatile, boolean _transient, boolean unsettable, boolean derived, boolean unique, boolean ordered, boolean resolve, boolean containment, boolean id) {
this.readonly = readonly;
this._volatile = _volatile;
this._transient = _transient;
this.unsettable = unsettable;
this.derived = derived;
this.unique = unique;
this.ordered = ordered;
this.resolve = resolve;
this.containment = containment;
this.id = id;
}
}

View file

@ -0,0 +1,62 @@
package de.nexus.mmlcli.entities.model;
import de.nexus.mmlcli.serializer.EcoreIdResolver;
import org.eclipse.emf.ecore.EEnum;
import java.util.ArrayList;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* Dataclass for a enum
*/
public class EnumEntity<T> {
private String referenceId;
private String name;
private String type;
private final ArrayList<EnumEntryEntity<T>> entries = new ArrayList<>();
public String getReferenceId() {
return referenceId;
}
public String getName() {
return name;
}
public String getType() {
return type;
}
public ArrayList<EnumEntryEntity<T>> getEntries() {
return entries;
}
private void setReferenceId(String referenceId) {
this.referenceId = referenceId;
}
private void setName(String name) {
this.name = name;
}
private void setType(String type) {
this.type = type;
}
@Override
public String toString() {
return String.format("ENUM<%s,%s>{%s}", name, type, entries.isEmpty() ? ""
: entries.stream().map(EnumEntryEntity::toString).collect(Collectors.joining(",")));
}
public static EnumEntity<Integer> fromEEnum(EEnum eenum, EcoreIdResolver idResolver) {
EnumEntity<Integer> enumEntity = new EnumEntity<>();
UUID uuid = idResolver.resolveId(eenum);
enumEntity.setName(eenum.getName());
enumEntity.setReferenceId(uuid.toString());
enumEntity.setType("int"); // we assume type int since EMF does not allow other enum types
enumEntity.entries.addAll(eenum.getELiterals().stream().map(lit -> EnumEntryEntity.fromEEnumLiteral(lit, idResolver)).toList());
return enumEntity;
}
}

View file

@ -0,0 +1,63 @@
package de.nexus.mmlcli.entities.model;
import de.nexus.mmlcli.serializer.EcoreIdResolver;
import org.eclipse.emf.ecore.EEnumLiteral;
import java.util.UUID;
/**
* Dataclass for a single enum entry
*/
public class EnumEntryEntity<T> {
private String referenceId;
private String name;
private boolean hasDefaultValue;
private T defaultValue;
public String getReferenceId() {
return referenceId;
}
public String getName() {
return name;
}
public boolean isHasDefaultValue() {
return hasDefaultValue;
}
public T getDefaultValue() {
return defaultValue;
}
private void setReferenceId(String referenceId) {
this.referenceId = referenceId;
}
private void setName(String name) {
this.name = name;
}
private void setHasDefaultValue(boolean hasDefaultValue) {
this.hasDefaultValue = hasDefaultValue;
}
private void setDefaultValue(T defaultValue) {
this.defaultValue = defaultValue;
}
@Override
public String toString() {
return String.format("[%s|%b]", name, hasDefaultValue);
}
public static EnumEntryEntity<Integer> fromEEnumLiteral(EEnumLiteral eLit, EcoreIdResolver idResolver) {
EnumEntryEntity<Integer> enumEntry = new EnumEntryEntity<>();
UUID uuid = idResolver.resolveId(eLit);
enumEntry.setName(eLit.getName());
enumEntry.setReferenceId(uuid.toString());
enumEntry.setDefaultValue(eLit.getValue());
enumEntry.setHasDefaultValue(true);
return enumEntry;
}
}

View file

@ -0,0 +1,27 @@
package de.nexus.mmlcli.entities.model;
import java.util.ArrayList;
import java.util.stream.Collectors;
/**
* Dataclass for a complete metamodel
*/
public class ModelEntity {
private final ArrayList<PackageEntity> packages = new ArrayList<>();
public ArrayList<PackageEntity> getPackages() {
return packages;
}
@Override
public String toString() {
return packages.isEmpty() ? ""
: "\n" + packages.stream().map(PackageEntity::toString).collect(Collectors.joining(",")) + "\n";
}
public static ModelEntity fromPackageEntity(PackageEntity pckgEntity) {
ModelEntity model = new ModelEntity();
model.packages.add(pckgEntity);
return model;
}
}

View file

@ -0,0 +1,52 @@
package de.nexus.mmlcli.entities.model;
/**
* Dataclass for reference multiplicities
*/
public class MultiplicityEntity {
private boolean hasUpperBound;
private boolean lowerIsN;
private boolean lowerIsN0;
private boolean upperIsN;
private boolean upperIsN0;
private int lower;
private int upper;
public boolean isHasUpperBound() {
return hasUpperBound;
}
public boolean isLowerIsN() {
return lowerIsN;
}
public boolean isLowerIsN0() {
return lowerIsN0;
}
public boolean isUpperIsN() {
return upperIsN;
}
public boolean isUpperIsN0() {
return upperIsN0;
}
public int getLower() {
return lower;
}
public int getUpper() {
return upper;
}
public MultiplicityEntity(boolean hasUpperBound, boolean lowerIsN, boolean lowerIsN0, boolean upperIsN, boolean upperIsN0, int lower, int upper) {
this.hasUpperBound = hasUpperBound;
this.lowerIsN = lowerIsN;
this.lowerIsN0 = lowerIsN0;
this.upperIsN = upperIsN;
this.upperIsN0 = upperIsN0;
this.lower = lower;
this.upper = upper;
}
}

View file

@ -0,0 +1,72 @@
package de.nexus.mmlcli.entities.model;
import de.nexus.mmlcli.serializer.EcoreIdResolver;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EPackage;
import java.util.ArrayList;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* Dataclass for a package
*/
public class PackageEntity {
private String referenceId;
private String name;
private final ArrayList<AbstractClassEntity> abstractClasses = new ArrayList<>();
private final ArrayList<EnumEntity<?>> enums = new ArrayList<>();
private final ArrayList<PackageEntity> subPackages = new ArrayList<>();
public String getReferenceId() {
return referenceId;
}
public String getName() {
return name;
}
public ArrayList<AbstractClassEntity> getAbstractClasses() {
return abstractClasses;
}
public ArrayList<EnumEntity<?>> getEnums() {
return enums;
}
public ArrayList<PackageEntity> getSubPackages() {
return subPackages;
}
private void setReferenceId(String referenceId) {
this.referenceId = referenceId;
}
private void setName(String name) {
this.name = name;
}
@Override
public String toString() {
String classesString = abstractClasses.isEmpty() ? ""
: "\n" + abstractClasses.stream().map(AbstractClassEntity::toString).collect(Collectors.joining(","))
+ "\n";
String enumsString = enums.isEmpty() ? ""
: "\n" + enums.stream().map(EnumEntity::toString).collect(Collectors.joining(",")) + "\n";
String subPackagesString = subPackages.isEmpty() ? ""
: "\n" + subPackages.stream().map(PackageEntity::toString).collect(Collectors.joining(",")) + "\n";
return String.format("%s{%s %s %s}", name, classesString, enumsString, subPackagesString);
}
public static PackageEntity fromEPackage(EPackage ePackage, EcoreIdResolver idResolver) {
PackageEntity packageEntity = new PackageEntity();
UUID uuid = idResolver.resolveId(ePackage);
packageEntity.setName(ePackage.getName());
packageEntity.setReferenceId(uuid.toString());
packageEntity.subPackages.addAll(ePackage.getESubpackages().stream().map(subPackage -> fromEPackage(subPackage, idResolver)).toList());
packageEntity.enums.addAll(ePackage.getEClassifiers().stream().filter(classifier -> classifier instanceof EEnum).map(eenum -> EnumEntity.fromEEnum((EEnum) eenum, idResolver)).toList());
packageEntity.abstractClasses.addAll(ePackage.getEClassifiers().stream().filter(classifier -> classifier instanceof EClass).map(eclass -> AbstractClassEntity.fromEClass((EClass) eclass, idResolver)).toList());
return packageEntity;
}
}

View file

@ -2,8 +2,8 @@ package de.nexus.mmlcli.generator;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import de.nexus.mmlcli.generator.entities.instance.GeneratorInstanceWrapper;
import de.nexus.mmlcli.generator.entities.model.ModelEntity;
import de.nexus.mmlcli.entities.instance.GeneratorInstanceWrapper;
import de.nexus.mmlcli.entities.model.ModelEntity;
public class DeserializedDocument {
private ModelEntity typegraph;
@ -17,9 +17,24 @@ public class DeserializedDocument {
return this.instancegraph;
}
private void setTypegraph(ModelEntity typegraph) {
this.typegraph = typegraph;
}
private void setInstancegraph(GeneratorInstanceWrapper instancegraph) {
this.instancegraph = instancegraph;
}
public static DeserializedDocument build(String json) {
GsonBuilder builder = new GsonBuilder();
Gson gson = builder.create();
return gson.fromJson(json, DeserializedDocument.class);
}
public static DeserializedDocument build(ModelEntity model) {
DeserializedDocument doc = new DeserializedDocument();
doc.setTypegraph(model);
doc.setInstancegraph(new GeneratorInstanceWrapper());
return doc;
}
}

View file

@ -1,256 +1,242 @@
package de.nexus.mmlcli.generator;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;
import de.nexus.mmlcli.entities.model.*;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.*;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.xmi.impl.EcoreResourceFactoryImpl;
import de.nexus.mmlcli.generator.entities.model.AbstractClassEntity;
import de.nexus.mmlcli.generator.entities.model.AttributeEntity;
import de.nexus.mmlcli.generator.entities.model.CReferenceEntity;
import de.nexus.mmlcli.generator.entities.model.EnumEntity;
import de.nexus.mmlcli.generator.entities.model.EnumEntryEntity;
import de.nexus.mmlcli.generator.entities.model.PackageEntity;
import java.io.IOException;
import java.util.*;
import java.util.stream.Stream;
/**
* The EcoreTypeGraphBuilder contains all functions to generate a metamodel from a PackageEntity as an Ecore file.
*/
public class EcoreTypeGraphBuilder {
private final EPackage ePackage;
private final String exportPath;
private final EcoreTypeResolver resolver;
private final EPackage ePackage;
private final String exportPath;
private final EcoreTypeResolver resolver;
/**
* Build an Ecore Package
* @param pckg PackageEntity
* @param targetUri Package URI
* @param exportPath Path for the Ecore export
* @param resolver EcoreTypeResolver
*/
public EcoreTypeGraphBuilder(PackageEntity pckg, String targetUri, String exportPath, EcoreTypeResolver resolver) {
// create a new package
this.ePackage = createPackage(pckg.getName(), pckg.getName(), targetUri);
this.exportPath = exportPath;
this.resolver = resolver;
/**
* Build an Ecore Package
*
* @param pckg PackageEntity
* @param targetUri Package URI
* @param exportPath Path for the Ecore export
* @param resolver EcoreTypeResolver
*/
public EcoreTypeGraphBuilder(PackageEntity pckg, String targetUri, String exportPath, EcoreTypeResolver resolver) {
// create a new package
this.ePackage = createPackage(pckg.getName(), pckg.getName(), targetUri);
this.exportPath = exportPath;
this.resolver = resolver;
// store package in the resolver
resolver.store(pckg.getReferenceId(), this.ePackage);
// store package in the resolver
resolver.store(pckg.getReferenceId(), this.ePackage);
// build subpackages
pckg.getSubPackages().forEach(subPckg -> {
EPackage subPackage = new EcoreTypeGraphBuilder(subPckg, targetUri, resolver).getAsSubpackage();
this.ePackage.getESubpackages().add(subPackage);
});
// build classes
pckg.getAbstractClasses().forEach(ab -> {
EClass clss = createEClass(ab);
ab.getAttributes().forEach(attr -> addAttribute(clss, attr));
ab.getReferences().forEach(cref -> addReference(clss, cref));
});
// build enums
pckg.getEnums().forEach(enm -> {
EEnum enmm = createEEnum(enm);
enm.getEntries().forEach(ee -> addEEnumLiteral(enmm, ee, enm));
});
}
// build subpackages
pckg.getSubPackages().forEach(subPckg -> {
EPackage subPackage = new EcoreTypeGraphBuilder(subPckg, targetUri, resolver).getAsSubpackage();
this.ePackage.getESubpackages().add(subPackage);
});
/**
* /**
* Build an Ecore Package
* <p>
* Does not include an export path, since this is used for subpackages
*
* @param pckg PackageEntity
* @param targetUri Package URI
* @param resolver EcoreTypeResolver
*/
public EcoreTypeGraphBuilder(PackageEntity pckg, String targetUri, EcoreTypeResolver resolver) {
this(pckg, targetUri, null, resolver);
}
// build classes
pckg.getAbstractClasses().forEach(ab -> {
EClass clss = createEClass(ab);
ab.getAttributes().forEach(attr -> addAttribute(clss, attr));
ab.getReferences().forEach(cref -> addReference(clss, cref));
});
/**
* Export EPackages to Ecore files
*
* @param graphBuilderList List of EcoreTypeGraphBuilders
* @param resolver EcoreTypeResolver
* @param resSet Common resource set
*/
public static void buildEcoreFile(List<EcoreTypeGraphBuilder> graphBuilderList, EcoreTypeResolver resolver,
ResourceSet resSet) {
// resolve all unresolved types
resolver.resolveUnresovedTypes();
// build enums
pckg.getEnums().forEach(enm -> {
EEnum enmm = createEEnum(enm);
enm.getEntries().forEach(ee -> addEEnumLiteral(enmm, ee, enm));
});
}
for (EcoreTypeGraphBuilder builder : graphBuilderList) {
builder.ePackage.eClass();
}
Resource.Factory.Registry reg = Resource.Factory.Registry.INSTANCE;
Map<String, Object> m = reg.getExtensionToFactoryMap();
m.put(EcorePackage.eNAME, new EcoreResourceFactoryImpl());
/**
* /**
* Build an Ecore Package
* <p>
* Does not include an export path, since this is used for subpackages
*
* @param pckg PackageEntity
* @param targetUri Package URI
* @param resolver EcoreTypeResolver
*/
public EcoreTypeGraphBuilder(PackageEntity pckg, String targetUri, EcoreTypeResolver resolver) {
this(pckg, targetUri, null, resolver);
}
List<Resource> resources = new ArrayList<>();
// create a resource
try {
for (EcoreTypeGraphBuilder builder : graphBuilderList) {
Resource resource = resSet
.createResource(URI.createFileURI(Objects.requireNonNull(builder.exportPath)));
/*
* add your EPackage as root, everything is hierarchical included in this first
* node
*/
resource.getContents().add(builder.ePackage);
resources.add(resource);
}
} catch (NullPointerException e) {
e.printStackTrace();
}
/**
* Export EPackages to Ecore files
*
* @param graphBuilderList List of EcoreTypeGraphBuilders
* @param resolver EcoreTypeResolver
* @param resSet Common resource set
*/
public static void buildEcoreFile(List<EcoreTypeGraphBuilder> graphBuilderList, EcoreTypeResolver resolver,
ResourceSet resSet) {
// resolve all unresolved types
resolver.resolveUnresovedTypes();
// now save the content.
for (Resource resource : resources) {
try {
resource.save(Collections.EMPTY_MAP);
} catch (IOException e) {
e.printStackTrace();
}
}
}
for (EcoreTypeGraphBuilder builder : graphBuilderList) {
builder.ePackage.eClass();
}
Resource.Factory.Registry reg = Resource.Factory.Registry.INSTANCE;
Map<String, Object> m = reg.getExtensionToFactoryMap();
m.put(EcorePackage.eNAME, new EcoreResourceFactoryImpl());
public EPackage getAsSubpackage() {
return this.ePackage;
}
List<Resource> resources = new ArrayList<>();
// create a resource
try {
for (EcoreTypeGraphBuilder builder : graphBuilderList) {
Resource resource = resSet
.createResource(URI.createFileURI(Objects.requireNonNull(builder.exportPath)));
/*
* add your EPackage as root, everything is hierarchical included in this first
* node
*/
resource.getContents().add(builder.ePackage);
resources.add(resource);
}
} catch (NullPointerException e) {
e.printStackTrace();
}
@SuppressWarnings("unchecked")
private void addAttribute(EClass containerClass, AttributeEntity<?> attr) {
final EAttribute attribute = EcoreFactory.eINSTANCE.createEAttribute();
resolver.store(attr.getReferenceId(), attribute);
// always add to container first
containerClass.getEStructuralFeatures().add(attribute);
attribute.setName(attr.getName());
attribute.setLowerBound(0);
attribute.setUpperBound(1);
// now save the content.
for (Resource resource : resources) {
try {
resource.save(Collections.EMPTY_MAP);
} catch (IOException e) {
e.printStackTrace();
}
}
}
if (attr.isEnumType()) {
resolver.resolveAttributeEnum(attribute, (AttributeEntity<String>) attr);
} else {
attribute.setEType(EmfGraphBuilderUtils.mapETypes(attr.getType()));
public EPackage getAsSubpackage() {
return this.ePackage;
}
if (attr.isHasDefaultValue()) {
Object val = EmfGraphBuilderUtils.mapVals(attr.getType(), attr.getDefaultValue());
attribute.setDefaultValue(val);
}
}
attribute.setDerived(attr.getModifiers().isDerived());
attribute.setOrdered(attr.getModifiers().isOrdered());
attribute.setTransient(attr.getModifiers().isTransient());
attribute.setUnique(attr.getModifiers().isUnique());
attribute.setUnsettable(attr.getModifiers().isUnsettable());
attribute.setVolatile(attr.getModifiers().isVolatile());
attribute.setChangeable(!attr.getModifiers().isReadonly());
attribute.setID(attr.getModifiers().isId());
}
@SuppressWarnings("unchecked")
private void addAttribute(EClass containerClass, AttributeEntity<?> attr) {
final EAttribute attribute = EcoreFactory.eINSTANCE.createEAttribute();
resolver.store(attr.getReferenceId(), attribute);
// always add to container first
containerClass.getEStructuralFeatures().add(attribute);
attribute.setName(attr.getName());
attribute.setLowerBound(0);
attribute.setUpperBound(1);
private void addReference(EClass containerClass, CReferenceEntity cref) {
final EReference reference = EcoreFactory.eINSTANCE.createEReference();
resolver.store(cref.getReferenceId(), reference);
// always add to container first
containerClass.getEStructuralFeatures().add(reference);
reference.setName(cref.getName());
if (attr.isEnumType()) {
resolver.resolveAttributeEnum(attribute, (AttributeEntity<String>) attr);
} else {
attribute.setEType(EmfGraphBuilderUtils.mapETypes(attr.getType()));
resolver.resolveReference(reference, cref);
if (attr.isHasDefaultValue()) {
Object val = EmfGraphBuilderUtils.mapVals(attr.getType(), attr.getDefaultValue());
attribute.setDefaultValue(val);
}
}
if (cref.getMultiplicity().isLowerIsN0()) {
reference.setLowerBound(0);
} else if (cref.getMultiplicity().isLowerIsN()) {
reference.setLowerBound(1);
} else {
reference.setLowerBound(cref.getMultiplicity().getLower());
}
attribute.setDerived(attr.getModifiers().isDerived());
attribute.setOrdered(attr.getModifiers().isOrdered());
attribute.setTransient(attr.getModifiers().isTransient());
attribute.setUnique(attr.getModifiers().isUnique());
attribute.setUnsettable(attr.getModifiers().isUnsettable());
attribute.setVolatile(attr.getModifiers().isVolatile());
attribute.setChangeable(!attr.getModifiers().isReadonly());
attribute.setID(attr.getModifiers().isId());
}
if (cref.getMultiplicity().isHasUpperBound()) {
if (cref.getMultiplicity().isUpperIsN0() || cref.getMultiplicity().isUpperIsN()) {
reference.setUpperBound(-1);
} else {
reference.setUpperBound(cref.getMultiplicity().getUpper());
}
} else {
if (cref.getMultiplicity().isLowerIsN0() || cref.getMultiplicity().isLowerIsN()) {
reference.setUpperBound(-1);
} else {
reference.setUpperBound(1);
}
}
private void addReference(EClass containerClass, CReferenceEntity cref) {
final EReference reference = EcoreFactory.eINSTANCE.createEReference();
resolver.store(cref.getReferenceId(), reference);
// always add to container first
containerClass.getEStructuralFeatures().add(reference);
reference.setName(cref.getName());
reference.setDerived(cref.getModifiers().isDerived());
reference.setChangeable(!cref.getModifiers().isReadonly());
reference.setVolatile(cref.getModifiers().isVolatile());
reference.setUnsettable(cref.getModifiers().isUnsettable());
reference.setUnique(cref.getModifiers().isUnique());
reference.setTransient(cref.getModifiers().isTransient());
reference.setOrdered(cref.getModifiers().isOrdered());
reference.setResolveProxies(cref.getModifiers().isResolve());
}
resolver.resolveReference(reference, cref);
private EPackage createPackage(final String name, final String prefix, final String uri) {
final EPackage epackage = EcoreFactory.eINSTANCE.createEPackage();
epackage.setName(name);
epackage.setNsPrefix(prefix);
epackage.setNsURI(uri);
return epackage;
if (cref.getMultiplicity().isLowerIsN0()) {
reference.setLowerBound(0);
} else if (cref.getMultiplicity().isLowerIsN()) {
reference.setLowerBound(1);
} else {
reference.setLowerBound(cref.getMultiplicity().getLower());
}
}
if (cref.getMultiplicity().isHasUpperBound()) {
if (cref.getMultiplicity().isUpperIsN0() || cref.getMultiplicity().isUpperIsN()) {
reference.setUpperBound(-1);
} else {
reference.setUpperBound(cref.getMultiplicity().getUpper());
}
} else {
if (cref.getMultiplicity().isLowerIsN0() || cref.getMultiplicity().isLowerIsN()) {
reference.setUpperBound(-1);
} else {
reference.setUpperBound(1);
}
}
private EClass createEClass(final AbstractClassEntity ace) {
final EClass eClass = EcoreFactory.eINSTANCE.createEClass();
resolver.store(ace.getReferenceId(), eClass);
eClass.setName(ace.getName());
eClass.setAbstract(ace.isAbstract());
eClass.setInterface(ace.isInterface());
if (!ace.getExtendsIds().isEmpty() || !ace.getImplementsIds().isEmpty()) {
List<String> allSupertypes = Stream.concat(ace.getExtendsIds().stream(), ace.getImplementsIds().stream())
.toList();
resolver.resolveSupertypes(eClass, allSupertypes);
}
this.ePackage.getEClassifiers().add(eClass);
return eClass;
}
reference.setDerived(cref.getModifiers().isDerived());
reference.setChangeable(!cref.getModifiers().isReadonly());
reference.setVolatile(cref.getModifiers().isVolatile());
reference.setUnsettable(cref.getModifiers().isUnsettable());
reference.setUnique(cref.getModifiers().isUnique());
reference.setTransient(cref.getModifiers().isTransient());
reference.setOrdered(cref.getModifiers().isOrdered());
reference.setResolveProxies(cref.getModifiers().isResolve());
reference.setContainment(cref.getModifiers().isContainment());
}
private EEnum createEEnum(final EnumEntity<?> ee) {
final EEnum eenum = EcoreFactory.eINSTANCE.createEEnum();
resolver.store(ee.getReferenceId(), eenum);
eenum.setName(ee.getName());
this.ePackage.getEClassifiers().add(eenum);
return eenum;
}
private EPackage createPackage(final String name, final String prefix, final String uri) {
final EPackage epackage = EcoreFactory.eINSTANCE.createEPackage();
epackage.setName(name);
epackage.setNsPrefix(prefix);
epackage.setNsURI(uri);
return epackage;
private EEnumLiteral addEEnumLiteral(final EEnum ee, final EnumEntryEntity<?> eee, final EnumEntity<?> eentity) {
final EEnumLiteral eenumLit = EcoreFactory.eINSTANCE.createEEnumLiteral();
resolver.store(eee.getReferenceId(), eenumLit);
eenumLit.setName(eee.getName());
if (eee.isHasDefaultValue()) {
if (eentity.getType().equals("int") || eentity.getType().equals("float")
|| eentity.getType().equals("double")) {
int val = Double.valueOf(eee.getDefaultValue().toString()).intValue();
eenumLit.setValue(val);
}
}
ee.getELiterals().add(eenumLit);
return eenumLit;
}
}
private EClass createEClass(final AbstractClassEntity ace) {
final EClass eClass = EcoreFactory.eINSTANCE.createEClass();
resolver.store(ace.getReferenceId(), eClass);
eClass.setName(ace.getName());
eClass.setAbstract(ace.isAbstract());
eClass.setInterface(ace.isInterface());
if (!ace.getExtendsIds().isEmpty() || !ace.getImplementsIds().isEmpty()) {
List<String> allSupertypes = Stream.concat(ace.getExtendsIds().stream(), ace.getImplementsIds().stream())
.toList();
resolver.resolveSupertypes(eClass, allSupertypes);
}
this.ePackage.getEClassifiers().add(eClass);
return eClass;
}
private EEnum createEEnum(final EnumEntity<?> ee) {
final EEnum eenum = EcoreFactory.eINSTANCE.createEEnum();
resolver.store(ee.getReferenceId(), eenum);
eenum.setName(ee.getName());
this.ePackage.getEClassifiers().add(eenum);
return eenum;
}
private EEnumLiteral addEEnumLiteral(final EEnum ee, final EnumEntryEntity<?> eee, final EnumEntity<?> eentity) {
final EEnumLiteral eenumLit = EcoreFactory.eINSTANCE.createEEnumLiteral();
resolver.store(eee.getReferenceId(), eenumLit);
eenumLit.setName(eee.getName());
if (eee.isHasDefaultValue()) {
if (eentity.getType().equals("int") || eentity.getType().equals("float")
|| eentity.getType().equals("double")) {
int val = Double.valueOf(eee.getDefaultValue().toString()).intValue();
eenumLit.setValue(val);
}
}
ee.getELiterals().add(eenumLit);
return eenumLit;
}
}

View file

@ -1,208 +1,201 @@
package de.nexus.mmlcli.generator;
import de.nexus.mmlcli.entities.instance.AttributeEntry;
import de.nexus.mmlcli.entities.instance.ObjectInstance;
import de.nexus.mmlcli.entities.instance.ReferenceEntry;
import de.nexus.mmlcli.entities.model.AttributeEntity;
import de.nexus.mmlcli.entities.model.CReferenceEntity;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.*;
import org.eclipse.emf.ecore.util.EcoreUtil;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.util.EcoreUtil;
import de.nexus.mmlcli.generator.entities.instance.AttributeEntry;
import de.nexus.mmlcli.generator.entities.instance.ObjectInstance;
import de.nexus.mmlcli.generator.entities.instance.ReferenceEntry;
import de.nexus.mmlcli.generator.entities.model.AttributeEntity;
import de.nexus.mmlcli.generator.entities.model.CReferenceEntity;
/**
* The EcoreTypeResolver helps to resolve and link references in metamodels. MML replaces
* all references with unique ids. To be independent of the execution order when constructing
* the metamodel, the TypeResolver tries to resolve a reference using the id and stores the
* missing reference otherwise. When exporting the metamodel (and thus after successful complete
* The EcoreTypeResolver helps to resolve and link references in metamodels. MML replaces
* all references with unique ids. To be independent of the execution order when constructing
* the metamodel, the TypeResolver tries to resolve a reference using the id and stores the
* missing reference otherwise. When exporting the metamodel (and thus after successful complete
* construction) all missing references are finally resolved.
*/
public class EcoreTypeResolver {
private final Map<String, EClassifier> classifiers = new HashMap<>();
private final Map<String, EReference> references = new HashMap<>();
private final Map<String, EAttribute> attributes = new HashMap<>();
private final Map<String, EPackage> packages = new HashMap<>();
private final Map<String, EEnumLiteral> elits = new HashMap<>();
private final Map<EReference, String> unresolvedReferenceTypes = new HashMap<>();
private final Map<EReference, String> unresolvedReferenceOpposites = new HashMap<>();
private final Map<EAttribute, String> unresolvedAttributeEnumTypes = new HashMap<>();
private final Map<EAttribute, String> unresolvedAttributeEnumValues = new HashMap<>();
private final Map<EClass, List<String>> unresolvedSupertypes = new HashMap<>();
private final Map<String, EClassifier> classifiers = new HashMap<>();
private final Map<String, EReference> references = new HashMap<>();
private final Map<String, EAttribute> attributes = new HashMap<>();
private final Map<String, EPackage> packages = new HashMap<>();
private final Map<String, EEnumLiteral> elits = new HashMap<>();
private final Map<EReference, String> unresolvedReferenceTypes = new HashMap<>();
private final Map<EReference, String> unresolvedReferenceOpposites = new HashMap<>();
private final Map<EAttribute, String> unresolvedAttributeEnumTypes = new HashMap<>();
private final Map<EAttribute, String> unresolvedAttributeEnumValues = new HashMap<>();
private final Map<EClass, List<String>> unresolvedSupertypes = new HashMap<>();
public void store(String classId, EClassifier classifier) {
this.classifiers.put(classId, classifier);
}
public void store(String classId, EClassifier classifier) {
this.classifiers.put(classId, classifier);
}
public void store(String refId, EReference reference) {
this.references.put(refId, reference);
}
public void store(String refId, EReference reference) {
this.references.put(refId, reference);
}
public void store(String attrId, EAttribute attibute) {
this.attributes.put(attrId, attibute);
}
public void store(String attrId, EAttribute attibute) {
this.attributes.put(attrId, attibute);
}
public void store(String pckgId, EPackage packagee) {
this.packages.put(pckgId, packagee);
}
public void store(String pckgId, EPackage packagee) {
this.packages.put(pckgId, packagee);
}
public void store(String elitId, EEnumLiteral enumLiteral) {
this.elits.put(elitId, enumLiteral);
}
public void store(String elitId, EEnumLiteral enumLiteral) {
this.elits.put(elitId, enumLiteral);
}
public void dumpResolverStorage() {
System.out.println("================[ERROR]================");
System.out.println("==========[EcoreTypeResolver]==========");
System.out.println("### Packages");
for (String key : this.packages.keySet()) {
System.out.printf("\t- %s%n", key);
}
System.out.println("### Classifiers");
for (String key : this.classifiers.keySet()) {
System.out.printf("\t- %s%n", key);
}
System.out.println("### Attributes");
for (String key : this.attributes.keySet()) {
System.out.printf("\t- %s%n", key);
}
System.out.println("### References");
for (String key : this.references.keySet()) {
System.out.printf("\t- %s%n", key);
}
System.out.println("### ELits");
for (String key : this.elits.keySet()) {
System.out.printf("\t- %s%n", key);
}
System.out.println("================[ERROR]================");
}
public void dumpResolverStorage() {
System.out.println("================[ERROR]================");
System.out.println("==========[EcoreTypeResolver]==========");
System.out.println("### Packages");
for (String key : this.packages.keySet()) {
System.out.printf("\t- %s%n", key);
}
System.out.println("### Classifiers");
for (String key : this.classifiers.keySet()) {
System.out.printf("\t- %s%n", key);
}
System.out.println("### Attributes");
for (String key : this.attributes.keySet()) {
System.out.printf("\t- %s%n", key);
}
System.out.println("### References");
for (String key : this.references.keySet()) {
System.out.printf("\t- %s%n", key);
}
System.out.println("### ELits");
for (String key : this.elits.keySet()) {
System.out.printf("\t- %s%n", key);
}
System.out.println("================[ERROR]================");
}
public void resolveSupertypes(EClass clazz, List<String> classIds) {
this.unresolvedSupertypes.put(clazz, classIds);
}
public void resolveSupertypes(EClass clazz, List<String> classIds) {
this.unresolvedSupertypes.put(clazz, classIds);
}
public void resolveReference(EReference ref, CReferenceEntity refEntity) {
String typeId = refEntity.getType();
if (classifiers.containsKey(typeId)) {
ref.setEType(classifiers.get(typeId));
} else {
unresolvedReferenceTypes.put(ref, typeId);
}
public void resolveReference(EReference ref, CReferenceEntity refEntity) {
String typeId = refEntity.getType();
if (classifiers.containsKey(typeId)) {
ref.setEType(classifiers.get(typeId));
} else {
unresolvedReferenceTypes.put(ref, typeId);
}
if (refEntity.isHasOpposite()) {
String oppositeId = refEntity.getOpposite();
if (classifiers.containsKey(oppositeId)) {
ref.setEType(classifiers.get(oppositeId));
} else {
unresolvedReferenceOpposites.put(ref, oppositeId);
}
}
}
if (refEntity.isHasOpposite()) {
String oppositeId = refEntity.getOpposite();
if (classifiers.containsKey(oppositeId)) {
ref.setEType(classifiers.get(oppositeId));
} else {
unresolvedReferenceOpposites.put(ref, oppositeId);
}
}
}
public void resolveAttributeEnum(EAttribute attr, AttributeEntity<String> attrEntity) {
if (!attrEntity.isEnumType()) {
return;
}
public void resolveAttributeEnum(EAttribute attr, AttributeEntity<String> attrEntity) {
if (!attrEntity.isEnumType()) {
return;
}
String typeId = attrEntity.getType();
if (classifiers.containsKey(typeId)) {
attr.setEType(classifiers.get(typeId));
} else {
unresolvedAttributeEnumTypes.put(attr, typeId);
}
String typeId = attrEntity.getType();
if (classifiers.containsKey(typeId)) {
attr.setEType(classifiers.get(typeId));
} else {
unresolvedAttributeEnumTypes.put(attr, typeId);
}
if (attrEntity.isHasDefaultValue()) {
String valueId = attrEntity.getDefaultValue();
if (elits.containsKey(valueId)) {
attr.setDefaultValue(elits.get(valueId).getName());
} else {
unresolvedAttributeEnumValues.put(attr, valueId);
}
}
}
if (attrEntity.isHasDefaultValue()) {
String valueId = attrEntity.getDefaultValue();
if (elits.containsKey(valueId)) {
attr.setDefaultValue(elits.get(valueId).getName());
} else {
unresolvedAttributeEnumValues.put(attr, valueId);
}
}
}
public EObject resolveObjectInstance(ObjectInstance objInst) {
EClass clazz = (EClass) this.classifiers.get(objInst.getReferenceTypeId());
return EcoreUtil.create(clazz);
}
public EObject resolveObjectInstance(ObjectInstance objInst) {
EClass clazz = (EClass) this.classifiers.get(objInst.getReferenceTypeId());
return EcoreUtil.create(clazz);
}
public EAttribute resolveAttribute(AttributeEntry<?> attr) {
return this.attributes.get(attr.getTypeId());
}
public EAttribute resolveAttribute(AttributeEntry<?> attr) {
return this.attributes.get(attr.getTypeId());
}
public EReference resolveReference(ReferenceEntry ref) {
return this.references.get(ref.getTypeId());
}
public EReference resolveReference(ReferenceEntry ref) {
return this.references.get(ref.getTypeId());
}
public EEnumLiteral resolveAttributeEnum(AttributeEntry<?> attr) {
return this.elits.get(attr.getValue());
}
public EEnumLiteral resolveAttributeEnum(AttributeEntry<?> attr) {
return this.elits.get(attr.getValue());
}
public void resolveUnresovedTypes() {
for (Map.Entry<EReference, String> refEntry : this.unresolvedReferenceTypes.entrySet()) {
EReference ref = refEntry.getKey();
String typeId = refEntry.getValue();
if (classifiers.containsKey(typeId)) {
ref.setEType(classifiers.get(typeId));
} else {
dumpResolverStorage();
throw new IllegalArgumentException("Could not resolve classId: " + typeId);
}
}
public void resolveUnresovedTypes() {
for (Map.Entry<EReference, String> refEntry : this.unresolvedReferenceTypes.entrySet()) {
EReference ref = refEntry.getKey();
String typeId = refEntry.getValue();
if (classifiers.containsKey(typeId)) {
ref.setEType(classifiers.get(typeId));
} else {
dumpResolverStorage();
throw new IllegalArgumentException("Could not resolve classId: " + typeId);
}
}
for (Map.Entry<EReference, String> refEntry : this.unresolvedReferenceOpposites.entrySet()) {
EReference ref = refEntry.getKey();
String typeId = refEntry.getValue();
if (references.containsKey(typeId)) {
ref.setEOpposite(references.get(typeId));
} else {
dumpResolverStorage();
throw new IllegalArgumentException("Could not resolve referenceId: " + typeId);
}
}
for (Map.Entry<EReference, String> refEntry : this.unresolvedReferenceOpposites.entrySet()) {
EReference ref = refEntry.getKey();
String typeId = refEntry.getValue();
if (references.containsKey(typeId)) {
ref.setEOpposite(references.get(typeId));
} else {
dumpResolverStorage();
throw new IllegalArgumentException("Could not resolve referenceId: " + typeId);
}
}
for (Map.Entry<EAttribute, String> attrEntry : this.unresolvedAttributeEnumTypes.entrySet()) {
EAttribute attr = attrEntry.getKey();
String typeId = attrEntry.getValue();
if (classifiers.containsKey(typeId)) {
attr.setEType(classifiers.get(typeId));
} else {
dumpResolverStorage();
throw new IllegalArgumentException("Could not resolve enumId: " + typeId);
}
}
for (Map.Entry<EAttribute, String> attrEntry : this.unresolvedAttributeEnumTypes.entrySet()) {
EAttribute attr = attrEntry.getKey();
String typeId = attrEntry.getValue();
if (classifiers.containsKey(typeId)) {
attr.setEType(classifiers.get(typeId));
} else {
dumpResolverStorage();
throw new IllegalArgumentException("Could not resolve enumId: " + typeId);
}
}
for (Map.Entry<EAttribute, String> attrEntry : this.unresolvedAttributeEnumValues.entrySet()) {
EAttribute attr = attrEntry.getKey();
String typeId = attrEntry.getValue();
if (elits.containsKey(typeId)) {
attr.setDefaultValue(elits.get(typeId).getName());
} else {
dumpResolverStorage();
throw new IllegalArgumentException("Could not resolve enum valueId: " + typeId);
}
}
for (Map.Entry<EAttribute, String> attrEntry : this.unresolvedAttributeEnumValues.entrySet()) {
EAttribute attr = attrEntry.getKey();
String typeId = attrEntry.getValue();
if (elits.containsKey(typeId)) {
attr.setDefaultValue(elits.get(typeId).getName());
} else {
dumpResolverStorage();
throw new IllegalArgumentException("Could not resolve enum valueId: " + typeId);
}
}
for (Map.Entry<EClass, List<String>> supertypeEntry : this.unresolvedSupertypes.entrySet()) {
EClass clazz = supertypeEntry.getKey();
List<String> supertypeIds = supertypeEntry.getValue();
EList<EClass> superTypes = clazz.getESuperTypes();
for (String supertypeId : supertypeIds) {
if (classifiers.containsKey(supertypeId)) {
superTypes.add((EClass) classifiers.get(supertypeId));
} else {
dumpResolverStorage();
throw new IllegalArgumentException("Could not resolve supertype classId: " + supertypeId);
}
}
}
}
for (Map.Entry<EClass, List<String>> supertypeEntry : this.unresolvedSupertypes.entrySet()) {
EClass clazz = supertypeEntry.getKey();
List<String> supertypeIds = supertypeEntry.getValue();
EList<EClass> superTypes = clazz.getESuperTypes();
for (String supertypeId : supertypeIds) {
if (classifiers.containsKey(supertypeId)) {
superTypes.add((EClass) classifiers.get(supertypeId));
} else {
dumpResolverStorage();
throw new IllegalArgumentException("Could not resolve supertype classId: " + supertypeId);
}
}
}
}
}

View file

@ -4,30 +4,44 @@ import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EcorePackage;
public class EmfGraphBuilderUtils {
public static EDataType mapETypes(String mmlType) {
return switch (mmlType) {
case "string" -> EcorePackage.Literals.ESTRING;
case "float" -> EcorePackage.Literals.EFLOAT;
case "double" -> EcorePackage.Literals.EDOUBLE;
case "int" -> EcorePackage.Literals.EINT;
case "boolean" -> EcorePackage.Literals.EBOOLEAN;
default -> EcorePackage.Literals.ESTRING;
};
}
public static <T,R> R mapVals(String mmlType, T value) {
return mapVals(mapETypes(mmlType), value);
}
@SuppressWarnings("unchecked")
public static <T,R> R mapVals(EDataType type, T value) {
return switch (type.getClassifierID()) {
case EcorePackage.ESTRING -> (R) value;
case EcorePackage.EFLOAT -> (R) Float.valueOf(value.toString());
case EcorePackage.EDOUBLE -> (R) Double.valueOf(value.toString());
case EcorePackage.EINT -> (R) Integer.valueOf(Double.valueOf(value.toString()).intValue());
case EcorePackage.EBOOLEAN -> (R) Boolean.valueOf(value.toString());
default -> (R) value.toString();
};
}
public static EDataType mapETypes(String mmlType) {
return switch (mmlType) {
case "string" -> EcorePackage.Literals.ESTRING;
case "float" -> EcorePackage.Literals.EFLOAT;
case "double" -> EcorePackage.Literals.EDOUBLE;
case "int" -> EcorePackage.Literals.EINT;
case "boolean" -> EcorePackage.Literals.EBOOLEAN;
default -> EcorePackage.Literals.ESTRING;
};
}
public static String mapETypes(EDataType dataType) {
return switch (dataType.getClassifierID()) {
case EcorePackage.EFLOAT -> "float";
case EcorePackage.EDOUBLE -> "double";
case EcorePackage.EINT -> "int";
case EcorePackage.EBOOLEAN -> "boolean";
default -> "string";
};
}
public static <T> boolean isETypeDefaultValue(EDataType type, T value) {
return type.getDefaultValue().equals(value);
}
public static <T, R> R mapVals(String mmlType, T value) {
return mapVals(mapETypes(mmlType), value);
}
@SuppressWarnings("unchecked")
public static <T, R> R mapVals(EDataType type, T value) {
return switch (type.getClassifierID()) {
case EcorePackage.ESTRING -> (R) value;
case EcorePackage.EFLOAT -> (R) Float.valueOf(value.toString());
case EcorePackage.EDOUBLE -> (R) Double.valueOf(value.toString());
case EcorePackage.EINT -> (R) Integer.valueOf(Double.valueOf(value.toString()).intValue());
case EcorePackage.EBOOLEAN -> (R) Boolean.valueOf(value.toString());
default -> (R) value.toString();
};
}
}

View file

@ -1,8 +1,10 @@
package de.nexus.mmlcli.generator;
import de.nexus.mmlcli.generator.entities.instance.GeneratorInstance;
import de.nexus.mmlcli.generator.entities.model.ModelEntity;
import de.nexus.mmlcli.generator.entities.model.PackageEntity;
import de.nexus.mmlcli.entities.instance.GeneratorInstance;
import de.nexus.mmlcli.entities.model.ModelEntity;
import de.nexus.mmlcli.entities.model.PackageEntity;
import de.nexus.mmlcli.generator.diagnostic.DocumentDiagnostic;
import de.nexus.mmlcli.generator.diagnostic.DocumentPoint;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
@ -13,6 +15,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* The EmfResourceBuilder coordinates the export of metamodels and instance graphs.
@ -36,9 +39,20 @@ public class EmfResourceBuilder {
} catch (IOException ex) {
System.out.println("[MODEL PATH BUILDER] Could not create models directory: " + modelsDir);
}
ArrayList<DocumentDiagnostic> seriousDiagnostics = doc.getDiagnostics().stream().filter(x -> x.getSeverity() == 1).collect(Collectors.toCollection(ArrayList::new));
if (!seriousDiagnostics.isEmpty()) {
System.err.printf("[DOCUMENT SKIP] Skipping %s due to the following errors%n", doc.uri.toString());
System.err.flush();
seriousDiagnostics.forEach(diag -> {
DocumentPoint startPoint = diag.getRange().getStart();
System.err.printf("%s (%s) starting in line %d:%d%n", diag.getMessage(), diag.getCode(), startPoint.getLine(), startPoint.getCharacter());
System.err.flush();
});
continue;
}
ModelEntity model = doc.getParsedGenerator().getTypegraph();
for (PackageEntity pckgEntity : model.getPackages()) {
String fileName = Path.of(doc.uri).getFileName().toString().replace(".mml", "") + "_"
String fileName = Path.of(doc.uri).getFileName().toString().replace(".mml", "").replace(".ecore", "") + "_"
+ pckgEntity.getName() + ".ecore";
Path filePath = Paths.get(modelsDir.toString(), fileName);
String packageUri = String.format("platform:/resource/%s/model/%s", projectName, fileName);

View file

@ -2,20 +2,39 @@ package de.nexus.mmlcli.generator;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import de.nexus.mmlcli.generator.diagnostic.DocumentDiagnostic;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
public class SerializedDocument {
URI uri;
String content;
ArrayList<DocumentDiagnostic> diagnostics = new ArrayList<>();
public DeserializedDocument getParsedGenerator(){
public DeserializedDocument getParsedGenerator() {
return DeserializedDocument.build(this.content);
}
public SerializedDocument(URI uri, DeserializedDocument dDoc) {
Gson gson = new Gson();
this.content = gson.toJson(dDoc);
this.uri = uri;
}
public String serialize() {
Gson gson = new Gson();
return gson.toJson(List.of(this));
}
public static SerializedDocument[] deserialize(String json) {
GsonBuilder builder = new GsonBuilder();
Gson gson = builder.create();
return gson.fromJson(json, SerializedDocument[].class);
}
public ArrayList<DocumentDiagnostic> getDiagnostics() {
return diagnostics;
}
}

View file

@ -1,11 +1,7 @@
package de.nexus.mmlcli.generator;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import de.nexus.mmlcli.entities.instance.AttributeEntry;
import de.nexus.mmlcli.entities.instance.ObjectInstance;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
@ -14,77 +10,77 @@ import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.xmi.XMIResource;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
import de.nexus.mmlcli.generator.entities.instance.AttributeEntry;
import de.nexus.mmlcli.generator.entities.instance.ObjectInstance;
import java.io.IOException;
import java.util.*;
/**
* The XMIInstanceGraphBuilder contains all functions to generate a model instance from a ObjectInstance as an XMI file.
*/
public class XMIInstanceGraphBuilder {
private final ArrayList<EObject> objects = new ArrayList<>();
private final String exportPath;
private final ArrayList<EObject> objects = new ArrayList<>();
private final String exportPath;
public XMIInstanceGraphBuilder(List<ObjectInstance> objInsts, String exportPath, EcoreTypeResolver typeResolver,
XMIInstanceResolver instResolver) {
objInsts.forEach(objInst -> {
EObject obj = typeResolver.resolveObjectInstance(objInst);
for (AttributeEntry<?> attr : objInst.getAttributes()) {
setAttribute(obj, attr, typeResolver, instResolver);
}
instResolver.registerReference(objInst.getReferenceId(), objInst.getReferences());
this.objects.add(obj);
instResolver.store(objInst.getReferenceId(), obj);
});
this.exportPath = exportPath;
}
public XMIInstanceGraphBuilder(List<ObjectInstance> objInsts, String exportPath, EcoreTypeResolver typeResolver,
XMIInstanceResolver instResolver) {
objInsts.forEach(objInst -> {
EObject obj = typeResolver.resolveObjectInstance(objInst);
for (AttributeEntry<?> attr : objInst.getAttributes()) {
setAttribute(obj, attr, typeResolver, instResolver);
}
instResolver.registerReference(objInst.getReferenceId(), objInst.getReferences());
public static void buildXmiFile(List<XMIInstanceGraphBuilder> graphBuilderList, EcoreTypeResolver typeResolver,
XMIInstanceResolver instanceResolver, ResourceSet resSet) {
instanceResolver.resolveUnresolvedReferences(typeResolver);
Resource.Factory.Registry reg = Resource.Factory.Registry.INSTANCE;
Map<String, Object> m = reg.getExtensionToFactoryMap();
m.put(XMIResource.XMI_NS, new XMIResourceFactoryImpl());
this.objects.add(obj);
instResolver.store(objInst.getReferenceId(), obj);
});
List<Resource> resources = new ArrayList<>();
// create a resource
try {
for (XMIInstanceGraphBuilder builder : graphBuilderList) {
Resource resource = resSet
.createResource(URI.createFileURI(Objects.requireNonNull(builder.exportPath)));
/*
* add your EPackage as root, everything is hierarchical included in this first
* node
*/
builder.objects.forEach(obj -> resource.getContents().add(obj));
//System.out.println("[XMIBuilder DEBUG] Resource contains: "+resource.getContents().size());
resources.add(resource);
}
} catch (NullPointerException e) {
e.printStackTrace();
}
this.exportPath = exportPath;
}
// now save the content.
for (Resource resource : resources) {
try {
resource.save(Collections.EMPTY_MAP);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void setAttribute(EObject obj, AttributeEntry<?> attr,EcoreTypeResolver typeResolver,
XMIInstanceResolver instResolver) {
EAttribute eattr = typeResolver.resolveAttribute(attr);
if (attr.isEnumType()) {
obj.eSet(eattr, typeResolver.resolveAttributeEnum(attr));
}else {
obj.eSet(eattr, EmfGraphBuilderUtils.mapVals(eattr.getEAttributeType(), attr.getValue()));
}
}
public static void buildXmiFile(List<XMIInstanceGraphBuilder> graphBuilderList, EcoreTypeResolver typeResolver,
XMIInstanceResolver instanceResolver, ResourceSet resSet) {
instanceResolver.resolveUnresolvedReferences(typeResolver);
Resource.Factory.Registry reg = Resource.Factory.Registry.INSTANCE;
Map<String, Object> m = reg.getExtensionToFactoryMap();
m.put(XMIResource.XMI_NS, new XMIResourceFactoryImpl());
List<Resource> resources = new ArrayList<>();
// create a resource
try {
for (XMIInstanceGraphBuilder builder : graphBuilderList) {
Resource resource = resSet
.createResource(URI.createFileURI(Objects.requireNonNull(builder.exportPath)));
/*
* add your EPackage as root, everything is hierarchical included in this first
* node
*/
builder.objects.forEach(obj -> resource.getContents().add(obj));
//System.out.println("[XMIBuilder DEBUG] Resource contains: "+resource.getContents().size());
resources.add(resource);
}
} catch (NullPointerException e) {
e.printStackTrace();
}
// now save the content.
for (Resource resource : resources) {
try {
resource.save(Collections.EMPTY_MAP);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void setAttribute(EObject obj, AttributeEntry<?> attr, EcoreTypeResolver typeResolver,
XMIInstanceResolver instResolver) {
EAttribute eattr = typeResolver.resolveAttribute(attr);
if (attr.isEnumType()) {
obj.eSet(eattr, typeResolver.resolveAttributeEnum(attr));
} else {
obj.eSet(eattr, EmfGraphBuilderUtils.mapVals(eattr.getEAttributeType(), attr.getValue()));
}
}
}

View file

@ -1,54 +1,55 @@
package de.nexus.mmlcli.generator;
import de.nexus.mmlcli.entities.instance.ReferenceEntry;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import de.nexus.mmlcli.generator.entities.instance.ReferenceEntry;
/**
* XMIInstanceResolver helps to resolve and link references in instance graphs. MML replaces
* all references with unique ids. To be independent of the execution order when constructing
* the instance graph, the TypeResolver stores all missing references. Finally, when exporting
* the instance graph (and thus after successful complete construction), all missing references
* XMIInstanceResolver helps to resolve and link references in instance graphs. MML replaces
* all references with unique ids. To be independent of the execution order when constructing
* the instance graph, the TypeResolver stores all missing references. Finally, when exporting
* the instance graph (and thus after successful complete construction), all missing references
* are resolved.
*/
public class XMIInstanceResolver {
private final Map<String, EObject> objects = new HashMap<>();
private final Map<String, List<ReferenceEntry>> unresolvedReferences = new HashMap<>();
private final Map<String, EObject> objects = new HashMap<>();
private final Map<String, List<ReferenceEntry>> unresolvedReferences = new HashMap<>();
public void store(String objId, EObject obj) {
this.objects.put(objId, obj);
}
public void store(String objId, EObject obj) {
this.objects.put(objId, obj);
}
public void registerReference(String objId, List<ReferenceEntry> references) {
this.unresolvedReferences.put(objId, references);
}
public void registerReference(String objId, List<ReferenceEntry> references) {
this.unresolvedReferences.put(objId, references);
}
public void resolveUnresolvedReferences(EcoreTypeResolver typeResolver) {
for (Map.Entry<String, List<ReferenceEntry>> entry : this.unresolvedReferences.entrySet()) {
EObject base = this.objects.get(entry.getKey());
for (ReferenceEntry ref : entry.getValue()) {
EReference eref = typeResolver.resolveReference(ref);
if (eref.isMany()) {
@SuppressWarnings("unchecked")
EList<EObject> oldVals = (EList<EObject>) base.eGet(eref);
for (String refId : ref.getReferencedIds()) {
EObject target = this.objects.get(refId);
oldVals.add(target);
}
} else {
if (!ref.getReferencedIds().isEmpty()) {
String refId = ref.getReferencedIds().get(0);
EObject target = this.objects.get(refId);
base.eSet(eref, target);
}
}
}
}
}
public void resolveUnresolvedReferences(EcoreTypeResolver typeResolver) {
for (Map.Entry<String, List<ReferenceEntry>> entry : this.unresolvedReferences.entrySet()) {
EObject base = this.objects.get(entry.getKey());
for (ReferenceEntry ref : entry.getValue()) {
EReference eref = typeResolver.resolveReference(ref);
if (eref.isMany()) {
@SuppressWarnings("unchecked")
EList<EObject> oldVals = (EList<EObject>) base.eGet(eref);
for (String refId : ref.getReferencedIds()) {
EObject target = this.objects.get(refId);
if (!oldVals.contains(target)) {
oldVals.add(target);
}
}
} else {
if (!ref.getReferencedIds().isEmpty()) {
String refId = ref.getReferencedIds().get(0);
EObject target = this.objects.get(refId);
base.eSet(eref, target);
}
}
}
}
}
}

View file

@ -0,0 +1,29 @@
package de.nexus.mmlcli.generator.diagnostic;
public class DocumentDiagnostic {
private String message;
private DocumentRange range;
private int severity;
private String code;
private String source;
public String getMessage() {
return message;
}
public DocumentRange getRange() {
return range;
}
public int getSeverity() {
return severity;
}
public String getCode() {
return code;
}
public String getSource() {
return source;
}
}

View file

@ -0,0 +1,14 @@
package de.nexus.mmlcli.generator.diagnostic;
public class DocumentPoint {
private int character;
private int line;
public int getCharacter() {
return character;
}
public int getLine() {
return line;
}
}

View file

@ -0,0 +1,14 @@
package de.nexus.mmlcli.generator.diagnostic;
public class DocumentRange {
private DocumentPoint start;
private DocumentPoint end;
public DocumentPoint getStart() {
return start;
}
public DocumentPoint getEnd() {
return end;
}
}

View file

@ -1,27 +0,0 @@
package de.nexus.mmlcli.generator.entities.instance;
/**
* Dataclass for an instance attribute
*/
public class AttributeEntry<T> {
private String name;
private String typeId;
private T value;
private boolean isEnumType;
public String getTypeId() {
return typeId;
}
public String getName() {
return name;
}
public T getValue() {
return value;
}
public boolean isEnumType() {
return isEnumType;
}
}

View file

@ -1,19 +0,0 @@
package de.nexus.mmlcli.generator.entities.instance;
import java.util.ArrayList;
/**
* Dataclass for a generator of a single XMI file
*/
public class GeneratorInstance {
private String instanceName;
private ArrayList<ObjectInstance> instances;
public String getInstanceName() {
return instanceName;
}
public ArrayList<ObjectInstance> getInstances() {
return instances;
}
}

View file

@ -1,14 +0,0 @@
package de.nexus.mmlcli.generator.entities.instance;
import java.util.ArrayList;
/**
* Dataclass for list of generators for multiple XMI files
*/
public class GeneratorInstanceWrapper {
private ArrayList<GeneratorInstance> serializedInstances;
public ArrayList<GeneratorInstance> getSerializedInstances() {
return serializedInstances;
}
}

View file

@ -1,34 +0,0 @@
package de.nexus.mmlcli.generator.entities.instance;
import java.util.ArrayList;
/**
* Dataclass for an instance
*/
public class ObjectInstance {
private String referenceId;
private String referenceTypeId;
private String name;
private ArrayList<AttributeEntry<?>> attributes;
private ArrayList<ReferenceEntry> references;
public String getReferenceId() {
return referenceId;
}
public String getReferenceTypeId() {
return referenceTypeId;
}
public String getName() {
return name;
}
public ArrayList<AttributeEntry<?>> getAttributes() {
return attributes;
}
public ArrayList<ReferenceEntry> getReferences() {
return references;
}
}

View file

@ -1,24 +0,0 @@
package de.nexus.mmlcli.generator.entities.instance;
import java.util.ArrayList;
/**
* Dataclass for an instance reference
*/
public class ReferenceEntry {
private String name;
private String typeId;
private ArrayList<String> referencedIds;
public String getName() {
return name;
}
public String getTypeId() {
return typeId;
}
public ArrayList<String> getReferencedIds() {
return referencedIds;
}
}

View file

@ -1,60 +0,0 @@
package de.nexus.mmlcli.generator.entities.model;
import java.util.ArrayList;
import java.util.stream.Collectors;
/***
* Dataclass for a class-like (Abstract class, class or interface)
*/
public class AbstractClassEntity {
private String referenceId;
private String name;
private boolean isAbstract;
private boolean isInterface;
private final ArrayList<AttributeEntity<?>> attributes = new ArrayList<>();
private final ArrayList<CReferenceEntity> references = new ArrayList<>();
private final ArrayList<String> extendsIds = new ArrayList<>();
private final ArrayList<String> implementsIds = new ArrayList<>();
public String getReferenceId() {
return referenceId;
}
public String getName() {
return name;
}
public boolean isAbstract() {
return isAbstract;
}
public boolean isInterface() {
return isInterface;
}
public ArrayList<AttributeEntity<?>> getAttributes() {
return attributes;
}
public ArrayList<CReferenceEntity> getReferences() {
return references;
}
public ArrayList<String> getExtendsIds() {
return extendsIds;
}
public ArrayList<String> getImplementsIds() {
return implementsIds;
}
@Override
public String toString() {
String attributeString = attributes.isEmpty() ? ""
: "\n" + attributes.stream().map(AttributeEntity::toString).collect(Collectors.joining(",")) + "\n";
String referenceString = references.isEmpty() ? ""
: "\n" + references.stream().map(CReferenceEntity::toString).collect(Collectors.joining(",")) + "\n";
return String.format("%s(isAbstract:%b|isInterface:%b||%s||%s)", name, isAbstract, isInterface, attributeString,
referenceString);
}
}

View file

@ -1,47 +0,0 @@
package de.nexus.mmlcli.generator.entities.model;
/**
* Dataclass for a class attribute
*/
public class AttributeEntity<T> {
private String referenceId;
private String name;
private String type;
private boolean isEnumType;
private boolean hasDefaultValue;
private T defaultValue;
private ClassElementModifiers modifiers;
public String getReferenceId() {
return referenceId;
}
public String getName() {
return name;
}
public String getType() {
return type;
}
public boolean isEnumType() {
return isEnumType;
}
public boolean isHasDefaultValue() {
return hasDefaultValue;
}
public T getDefaultValue() {
return defaultValue;
}
public ClassElementModifiers getModifiers() {
return modifiers;
}
@Override
public String toString() {
return String.format("%s<%s>", name, type);
}
}

View file

@ -1,48 +0,0 @@
package de.nexus.mmlcli.generator.entities.model;
/**
* Dataclass for class references
*/
public class CReferenceEntity {
private String referenceId;
private String name;
private MultiplicityEntity multiplicity;
private String type;
private ClassElementModifiers modifiers;
private boolean hasOpposite;
private String opposite;
public String getReferenceId() {
return referenceId;
}
public String getName() {
return name;
}
public MultiplicityEntity getMultiplicity() {
return multiplicity;
}
public String getType() {
return type;
}
public ClassElementModifiers getModifiers() {
return modifiers;
}
public boolean isHasOpposite() {
return hasOpposite;
}
public String getOpposite() {
return opposite;
}
@Override
public String toString() {
return String.format("(%s -> %s)", name, type);
}
}

View file

@ -1,57 +0,0 @@
package de.nexus.mmlcli.generator.entities.model;
import com.google.gson.annotations.SerializedName;
/**
* Dataclass for class modifiers
*/
public class ClassElementModifiers {
private boolean readonly;
@SerializedName("volatile")
private boolean _volatile;
@SerializedName("transient")
private boolean _transient;
private boolean unsettable;
private boolean derived;
private boolean unique;
private boolean ordered;
private boolean resolve;
private boolean id;
public boolean isReadonly() {
return readonly;
}
public boolean isVolatile() {
return _volatile;
}
public boolean isTransient() {
return _transient;
}
public boolean isUnsettable() {
return unsettable;
}
public boolean isDerived() {
return derived;
}
public boolean isUnique() {
return unique;
}
public boolean isOrdered() {
return ordered;
}
public boolean isResolve() {
return resolve;
}
public boolean isId() {
return id;
}
}

View file

@ -1,36 +0,0 @@
package de.nexus.mmlcli.generator.entities.model;
import java.util.ArrayList;
import java.util.stream.Collectors;
/**
* Dataclass for a enum
*/
public class EnumEntity<T> {
private String referenceId;
private String name;
private String type;
private ArrayList<EnumEntryEntity<T>> entries;
public String getReferenceId() {
return referenceId;
}
public String getName() {
return name;
}
public String getType() {
return type;
}
public ArrayList<EnumEntryEntity<T>> getEntries() {
return entries;
}
@Override
public String toString() {
return String.format("ENUM<%s,%s>{%s}", name, type, entries.isEmpty() ? ""
: entries.stream().map(EnumEntryEntity::toString).collect(Collectors.joining(",")));
}
}

View file

@ -1,32 +0,0 @@
package de.nexus.mmlcli.generator.entities.model;
/**
* Dataclass for a single enum entry
*/
public class EnumEntryEntity<T> {
private String referenceId;
private String name;
private boolean hasDefaultValue;
private T defaultValue;
public String getReferenceId() {
return referenceId;
}
public String getName() {
return name;
}
public boolean isHasDefaultValue() {
return hasDefaultValue;
}
public T getDefaultValue() {
return defaultValue;
}
@Override
public String toString() {
return String.format("[%s|%b]", name, hasDefaultValue);
}
}

View file

@ -1,21 +0,0 @@
package de.nexus.mmlcli.generator.entities.model;
import java.util.ArrayList;
import java.util.stream.Collectors;
/**
* Dataclass for a complete metamodel
*/
public class ModelEntity {
private final ArrayList<PackageEntity> packages = new ArrayList<>();
public ArrayList<PackageEntity> getPackages() {
return packages;
}
@Override
public String toString() {
return packages.isEmpty() ? ""
: "\n" + packages.stream().map(PackageEntity::toString).collect(Collectors.joining(",")) + "\n";
}
}

View file

@ -1,43 +0,0 @@
package de.nexus.mmlcli.generator.entities.model;
/**
* Dataclass for reference multiplicities
*/
public class MultiplicityEntity {
private boolean hasUpperBound;
private boolean lowerIsN;
private boolean lowerIsN0;
private boolean upperIsN;
private boolean upperIsN0;
private int lower;
private int upper;
public boolean isHasUpperBound() {
return hasUpperBound;
}
public boolean isLowerIsN() {
return lowerIsN;
}
public boolean isLowerIsN0() {
return lowerIsN0;
}
public boolean isUpperIsN() {
return upperIsN;
}
public boolean isUpperIsN0() {
return upperIsN0;
}
public int getLower() {
return lower;
}
public int getUpper() {
return upper;
}
}

View file

@ -1,47 +0,0 @@
package de.nexus.mmlcli.generator.entities.model;
import java.util.ArrayList;
import java.util.stream.Collectors;
/**
* Dataclass for a package
*/
public class PackageEntity {
private String referenceId;
private String name;
private ArrayList<AbstractClassEntity> abstractClasses;
private ArrayList<EnumEntity<?>> enums;
private ArrayList<PackageEntity> subPackages;
public String getReferenceId() {
return referenceId;
}
public String getName() {
return name;
}
public ArrayList<AbstractClassEntity> getAbstractClasses() {
return abstractClasses;
}
public ArrayList<EnumEntity<?>> getEnums() {
return enums;
}
public ArrayList<PackageEntity> getSubPackages() {
return subPackages;
}
@Override
public String toString() {
String classesString = abstractClasses.isEmpty() ? ""
: "\n" + abstractClasses.stream().map(AbstractClassEntity::toString).collect(Collectors.joining(","))
+ "\n";
String enumsString = enums.isEmpty() ? ""
: "\n" + enums.stream().map(EnumEntity::toString).collect(Collectors.joining(",")) + "\n";
String subPackagesString = subPackages.isEmpty() ? ""
: "\n" + subPackages.stream().map(PackageEntity::toString).collect(Collectors.joining(",")) + "\n";
return String.format("%s{%s %s %s}", name, classesString, enumsString, subPackagesString);
}
}

View file

@ -0,0 +1,21 @@
package de.nexus.mmlcli.serializer;
import org.eclipse.emf.ecore.ENamedElement;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class EcoreIdResolver {
private final Map<ENamedElement, UUID> elementMapping = new HashMap<>();
public UUID resolveId(ENamedElement classifier) {
UUID resolved = this.elementMapping.get(classifier);
if (resolved == null) {
resolved = UUID.randomUUID();
this.elementMapping.put(classifier, resolved);
}
return resolved;
}
}

View file

@ -0,0 +1,51 @@
package de.nexus.mmlcli.serializer;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.BasicExtendedMetaData;
import org.eclipse.emf.ecore.util.ExtendedMetaData;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.ecore.xmi.impl.EcoreResourceFactoryImpl;
import java.io.File;
public class EmfResourceLoader {
public static EPackage loadEmfResources(File ecoreFile) {
System.out.println("Serialize model");
System.out.println("==========[Loading resources]==========");
// Create a resource set.
ResourceSet resourceSet = new ResourceSetImpl();
// Register the default resource factory -- only needed for stand-alone!
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(
"ecore", new EcoreResourceFactoryImpl());
// enable extended metadata
final ExtendedMetaData extendedMetaData = new BasicExtendedMetaData(resourceSet.getPackageRegistry());
resourceSet.getLoadOptions().put(XMLResource.OPTION_EXTENDED_META_DATA,
extendedMetaData);
// Register the package -- only needed for stand-alone!
EcorePackage ecorePackage = EcorePackage.eINSTANCE;
// Get the URI of the model file.
URI fileURI = URI.createFileURI(ecoreFile.getAbsolutePath());
// Demand load the resource for this file.
Resource resource = resourceSet.getResource(fileURI, true);
EObject eObject = resource.getContents().get(0);
if (eObject instanceof EPackage ePackage) {
resourceSet.getPackageRegistry().put(ePackage.getNsURI(), ePackage);
System.out.println("[Package ]" + ePackage.getName() + " | " + ePackage.getNsURI());
return ePackage;
}
throw new IllegalStateException("Could not load package from ecore!");
}
}

View file

@ -0,0 +1,23 @@
package de.nexus.mmlcli.serializer;
import de.nexus.mmlcli.entities.model.ModelEntity;
import de.nexus.mmlcli.entities.model.PackageEntity;
import de.nexus.mmlcli.generator.DeserializedDocument;
import de.nexus.mmlcli.generator.SerializedDocument;
import org.eclipse.emf.ecore.EPackage;
import java.net.URI;
public class MmlSerializedGenerator {
public static PackageEntity buildEntities(EPackage pckg) {
EcoreIdResolver resolver = new EcoreIdResolver();
return PackageEntity.fromEPackage(pckg, resolver);
}
public static String serializeEntities(PackageEntity pckgEntity, URI uri) {
ModelEntity model = ModelEntity.fromPackageEntity(pckgEntity);
DeserializedDocument dDoc = DeserializedDocument.build(model);
SerializedDocument sDoc = new SerializedDocument(uri, dDoc);
return sDoc.serialize();
}
}

View file

@ -0,0 +1,49 @@
package de.nexus.mmlcli.serializer;
import de.nexus.mmlcli.entities.model.PackageEntity;
import org.eclipse.emf.ecore.EPackage;
import picocli.CommandLine;
import java.io.File;
import java.nio.file.Files;
import java.util.concurrent.Callable;
@CommandLine.Command(name = "serialize", mixinStandardHelpOptions = true, version = "v1.0.0", description = "Serializes a given Ecore")
public class SerializeCommand implements Callable<Integer> {
@CommandLine.Option(names = {"-o", "--out"}, paramLabel = "SERIALIZED", description = "path where the serialized model should be stored", arity = "0..1")
File serializedTarget;
@CommandLine.Parameters(index = "0")
File ecoreFile;
@Override
public Integer call() throws Exception {
if (!ecoreFile.exists()) {
System.err.println("Inputfile does not exist: " + ecoreFile.getAbsolutePath());
return 2;
}
if (!ecoreFile.canRead()) {
System.err.println("Could not read inputfile: " + ecoreFile.getAbsolutePath());
return 2;
}
EPackage ePackage = EmfResourceLoader.loadEmfResources(ecoreFile);
PackageEntity packageEntity = MmlSerializedGenerator.buildEntities(ePackage);
String serialized = MmlSerializedGenerator.serializeEntities(packageEntity, ecoreFile.toURI());
if (serializedTarget == null) {
System.out.println("=$MML-CONTENT-START$=");
System.out.println(serialized);
} else {
try {
Files.writeString(serializedTarget.toPath(), serialized);
System.out.println("Written serialized file to: " + serializedTarget.getAbsolutePath());
} catch (Exception e) {
e.printStackTrace();
System.err.println("Could not write serialized file: " + serializedTarget.getAbsolutePath());
return 2;
}
}
return 0;
}
}

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="CarOwnership" nsURI="platform:/resource/CarOwnership/model/CarOwnership_CarOwnership.ecore"
nsPrefix="CarOwnership">
<eClassifiers xsi:type="ecore:EClass" name="Person">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="alter" ordered="false"
unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="kinder" ordered="false"
unique="false" upperBound="-1" eType="#//Person" resolveProxies="false" eOpposite="#//Person/eltern"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="eltern" ordered="false"
unique="false" lowerBound="2" eType="#//Person" resolveProxies="false" eOpposite="#//Person/kinder"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="autos" ordered="false"
unique="false" upperBound="-1" eType="#//Auto" resolveProxies="false" eOpposite="#//Auto/besitzer"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="Auto">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="kennzeichen" ordered="false"
unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
<eStructuralFeatures xsi:type="ecore:EAttribute" name="marke" ordered="false"
unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
<eStructuralFeatures xsi:type="ecore:EAttribute" name="erstzulassung" ordered="false"
unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
<eStructuralFeatures xsi:type="ecore:EAttribute" name="baujahr" ordered="false"
unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="besitzer" ordered="false"
unique="false" lowerBound="1" eType="#//Person" resolveProxies="false" eOpposite="#//Person/autos"/>
</eClassifiers>
</ecore:EPackage>

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="FerrymanProblem" nsURI="platform:/resource/FerrymanProblem/model/FerrymanProblem_FerrymanProblem.ecore"
nsPrefix="FerrymanProblem">
<eClassifiers xsi:type="ecore:EClass" name="Model">
<eStructuralFeatures xsi:type="ecore:EReference" name="leftBank" ordered="false"
unique="false" lowerBound="1" eType="#//Bank" resolveProxies="false"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="rightBank" ordered="false"
unique="false" lowerBound="1" eType="#//Bank" resolveProxies="false"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="subjects" ordered="false"
unique="false" upperBound="-1" eType="#//Subject" resolveProxies="false"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="Bank"/>
<eClassifiers xsi:type="ecore:EClass" name="Subject" abstract="true">
<eStructuralFeatures xsi:type="ecore:EReference" name="isAt" ordered="false" unique="false"
lowerBound="1" eType="#//Bank" resolveProxies="false"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="FerryMan" eSuperTypes="#//Subject"/>
<eClassifiers xsi:type="ecore:EClass" name="Thing" abstract="true" eSuperTypes="#//Subject">
<eStructuralFeatures xsi:type="ecore:EReference" name="likesToEat" ordered="false"
unique="false" upperBound="-1" eType="#//Thing" resolveProxies="false"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="Wolf" eSuperTypes="#//Thing"/>
<eClassifiers xsi:type="ecore:EClass" name="Goat" eSuperTypes="#//Thing"/>
<eClassifiers xsi:type="ecore:EClass" name="Cabbage" eSuperTypes="#//Thing"/>
</ecore:EPackage>

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="HospitalExample" nsURI="platform:/resource/HospitalExample/model/HospitalExample_HospitalExample.ecore"
nsPrefix="HospitalExample">
<eClassifiers xsi:type="ecore:EClass" name="Hospital">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="id" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="reception" lowerBound="1"
eType="#//Reception" containment="true"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="staff" ordered="false"
upperBound="-1" eType="#//Staff" containment="true"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="department" upperBound="-1"
eType="#//Department" containment="true"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="Reception">
<eStructuralFeatures xsi:type="ecore:EReference" name="waits" upperBound="-1"
eType="#//Patient" containment="true"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="Staff" abstract="true">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="staffID" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"
defaultValueLiteral="-1"/>
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="works" eType="#//Department"
eOpposite="#//Department/staff"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="Doctor" eSuperTypes="#//Staff">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="patientCapacity" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="caresfor" upperBound="-1"
eType="#//Patient" eOpposite="#//Patient/doctor"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="Nurse" eSuperTypes="#//Staff">
<eStructuralFeatures xsi:type="ecore:EReference" name="responsible" eType="#//Room"
eOpposite="#//Room/nurses"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="Department">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="dID" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"
defaultValueLiteral="-1" iD="true"/>
<eStructuralFeatures xsi:type="ecore:EAttribute" name="maxRoomCount" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="rooms" upperBound="-1"
eType="#//Room" containment="true"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="staff" upperBound="-1"
eType="#//Staff" eOpposite="#//Staff/works"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="Patient">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="patientID" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"
defaultValueLiteral="-1"/>
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
<eStructuralFeatures xsi:type="ecore:EAttribute" name="level" eType="#//Carelevel"
defaultValueLiteral="PENDING"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="doctor" lowerBound="1"
eType="#//Doctor" eOpposite="#//Doctor/caresfor"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="Room">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="capacity" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
<eStructuralFeatures xsi:type="ecore:EAttribute" name="level" eType="#//Carelevel"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="lies" upperBound="-1" eType="#//Patient"
containment="true"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="nurses" upperBound="-1"
eType="#//Nurse" eOpposite="#//Nurse/responsible"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EEnum" name="Carelevel">
<eLiterals name="WEAK"/>
<eLiterals name="MEDIUM" value="1"/>
<eLiterals name="STRONG" value="2"/>
<eLiterals name="PENDING" value="3"/>
</eClassifiers>
</ecore:EPackage>

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,40 @@
import de.nexus.mmlcli.generator.EmfResourceBuilder;
import de.nexus.mmlcli.generator.SerializedDocument;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import java.util.stream.Stream;
public class GeneratorTests {
private final static File TEST_DATA_FOLDER = new File("src/test/data");
static Stream<TestBundle> testBundleProvider() {
return TestUtils.getTestBundles(TEST_DATA_FOLDER).stream();
}
@ParameterizedTest
@MethodSource("testBundleProvider")
void testCorrectEcoreGeneration(TestBundle bundle, @TempDir File workingDir) throws IOException {
String serializedContent = Files.readString(bundle.getSerializedFile().toPath());
SerializedDocument[] result = Objects.requireNonNull(SerializedDocument.deserialize(serializedContent));
EmfResourceBuilder.buildEmfResources(result, bundle.getBundleName(), workingDir);
String ecoreContent = Files.readString(bundle.getEcoreFile().toPath());
Path generatedEcore = workingDir.toPath().resolve("model").resolve(bundle.getBundleName() + "_" + bundle.getBundleName() + ".ecore");
Assertions.assertTrue(generatedEcore.toFile().exists());
String generatedEcoreContent = Files.readString(generatedEcore);
Assertions.assertEquals(ecoreContent, generatedEcoreContent);
}
}

View file

@ -0,0 +1,25 @@
import java.io.File;
public class TestBundle {
private final String bundleName;
private final File ecoreFile;
private final File serializedFile;
public TestBundle(String bundleName, File ecoreFile, File serializedFile) {
this.bundleName = bundleName;
this.ecoreFile = ecoreFile;
this.serializedFile = serializedFile;
}
public String getBundleName() {
return bundleName;
}
public File getEcoreFile() {
return ecoreFile;
}
public File getSerializedFile() {
return serializedFile;
}
}

View file

@ -0,0 +1,51 @@
import org.apache.commons.io.FilenameUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public class TestUtils {
public static ArrayList<TestBundle> getTestBundles(File dataFolder) {
if (dataFolder == null || !dataFolder.exists() || !dataFolder.isDirectory()) {
throw new IllegalArgumentException("Data folder is not directory or does not exist!");
}
Set<String> knownFilenames = new HashSet<>();
ArrayList<TestBundle> testBundles = new ArrayList<>();
for (File file : Objects.requireNonNull(dataFolder.listFiles())) {
String fileName = FilenameUtils.getBaseName(file.getName());
String extension = FilenameUtils.getExtension(file.getName());
if (knownFilenames.contains(fileName)) {
continue;
}
knownFilenames.add(fileName);
File ecoreFile, jsonFile;
if (extension.equals("ecore")) {
ecoreFile = file;
jsonFile = file.toPath().resolveSibling(fileName + ".json").toFile();
if (!jsonFile.exists() || jsonFile.isDirectory()) {
System.err.println("Skipping file due to missing json match: " + file.getAbsolutePath());
continue;
}
} else if (extension.equals("json")) {
ecoreFile = file.toPath().resolveSibling(fileName + ".ecore").toFile();
jsonFile = file;
if (!ecoreFile.exists() || ecoreFile.isDirectory()) {
System.err.println("Skipping file due to missing ecore match: " + file.getAbsolutePath());
continue;
}
} else {
System.err.println("Skipping file due to extension mismatch: " + file.getAbsolutePath());
continue;
}
testBundles.add(new TestBundle(fileName, ecoreFile, jsonFile));
}
return testBundles;
}
}

View file

@ -0,0 +1,46 @@
import de.nexus.mmlcli.entities.model.PackageEntity;
import de.nexus.mmlcli.generator.EmfResourceBuilder;
import de.nexus.mmlcli.generator.SerializedDocument;
import de.nexus.mmlcli.serializer.EmfResourceLoader;
import de.nexus.mmlcli.serializer.MmlSerializedGenerator;
import org.eclipse.emf.ecore.EPackage;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import java.util.stream.Stream;
public class TwoWayEcoreTests {
private final static File TEST_DATA_FOLDER = new File("src/test/data");
static Stream<TestBundle> testBundleProvider() {
return TestUtils.getTestBundles(TEST_DATA_FOLDER).stream();
}
@ParameterizedTest
@MethodSource("testBundleProvider")
void testCorrectTwowaySerialization(TestBundle bundle, @TempDir File workingDir) throws IOException {
EPackage ePackage = EmfResourceLoader.loadEmfResources(bundle.getEcoreFile());
PackageEntity packageEntity = MmlSerializedGenerator.buildEntities(ePackage);
String generatedSerialization = MmlSerializedGenerator.serializeEntities(packageEntity, bundle.getEcoreFile().toURI());
SerializedDocument[] result = Objects.requireNonNull(SerializedDocument.deserialize(generatedSerialization));
EmfResourceBuilder.buildEmfResources(result, bundle.getBundleName(), workingDir);
String ecoreContent = Files.readString(bundle.getEcoreFile().toPath());
Path generatedEcore = workingDir.toPath().resolve("model").resolve(bundle.getBundleName() + "_" + bundle.getBundleName() + ".ecore");
Assertions.assertTrue(generatedEcore.toFile().exists());
String generatedEcoreContent = Files.readString(generatedEcore);
Assertions.assertEquals(ecoreContent, generatedEcoreContent);
}
}