diff --git a/build.sbt b/build.sbt index e2e7600bc881b0dd50aebf685b2d5fbb90450811..13a20cca573a7a4da9c725045f83be66ee9a7f82 100644 --- a/build.sbt +++ b/build.sbt @@ -34,7 +34,7 @@ lazy val generator = (project in file(".")) "org.junit.vintage" % "junit-vintage-engine" % "4.12.0" % "test", "org.assertj" % "assertj-core" % "3.12.2" % "test", - "com.novocode" % "junit-interface" % "0.11" % "test" + "net.aichler" % "jupiter-interface" % JupiterKeys.jupiterVersion.value % Test ), scalacOptions ++= Seq( "-language:implicitConversions" diff --git a/project/plugins.sbt b/project/plugins.sbt index 45a6f02d1880ea4c9bff889a194ab87adf467611..4a65abadaf7e792155072db22cce49a4abbf4d54 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,2 +1,6 @@ + +resolvers += Resolver.jcenterRepo + +addSbtPlugin("net.aichler" % "sbt-jupiter-interface" % "0.8.2") addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "5.2.4") addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "1.0.0") diff --git a/src/main/java/org/rosi_project/model_sync/model_join/representation/grammar/CompoundKeepExpression.java b/src/main/java/org/rosi_project/model_sync/model_join/representation/grammar/CompoundKeepExpression.java new file mode 100644 index 0000000000000000000000000000000000000000..1e253dafd1feee21a329b2a2f65b4cb47488627f --- /dev/null +++ b/src/main/java/org/rosi_project/model_sync/model_join/representation/grammar/CompoundKeepExpression.java @@ -0,0 +1,20 @@ +package org.rosi_project.model_sync.model_join.representation.grammar; + +import java.util.List; +import javax.annotation.Nonnull; + +/** + * A {@code CompoundKeepExpression} is a special kind of {@code KeepExpression} which in turn may + * contain a number of other {@code KeepExpression}s. + * + * @author Rico Bergmann + */ +public abstract class CompoundKeepExpression extends KeepExpression { + + /** + * Provides all the keep expressions that this expression is build of. + */ + @Nonnull + public abstract List<KeepExpression> getKeeps(); + +} diff --git a/src/main/java/org/rosi_project/model_sync/model_join/representation/grammar/KeepReferenceExpression.java b/src/main/java/org/rosi_project/model_sync/model_join/representation/grammar/KeepReferenceExpression.java index b111ca3e3bead3cc6299424bd930c31a74dc841b..5d6f0f32924f2f495b21b9ab3107dcbf15c044b2 100644 --- a/src/main/java/org/rosi_project/model_sync/model_join/representation/grammar/KeepReferenceExpression.java +++ b/src/main/java/org/rosi_project/model_sync/model_join/representation/grammar/KeepReferenceExpression.java @@ -28,7 +28,7 @@ import org.rosi_project.model_sync.model_join.representation.util.Assert; * * @author Rico Bergmann */ -public class KeepReferenceExpression extends KeepExpression { +public class KeepReferenceExpression extends CompoundKeepExpression { /** * The {@code ReferenceDirection} defines whether a reference is owned by the containing model @@ -188,6 +188,7 @@ public class KeepReferenceExpression extends KeepExpression { * the referenced instances. */ @Nonnull + @Override public List<KeepExpression> getKeeps() { return new ArrayList<>(keeps); } diff --git a/src/main/java/org/rosi_project/model_sync/model_join/representation/grammar/KeepSubTypeExpression.java b/src/main/java/org/rosi_project/model_sync/model_join/representation/grammar/KeepSubTypeExpression.java index fd732649f7fdb5337a49007a18edea44d04f3947..76e8d6027462a00d341906020ca57c638b1ed074 100644 --- a/src/main/java/org/rosi_project/model_sync/model_join/representation/grammar/KeepSubTypeExpression.java +++ b/src/main/java/org/rosi_project/model_sync/model_join/representation/grammar/KeepSubTypeExpression.java @@ -17,7 +17,7 @@ import org.rosi_project.model_sync.model_join.representation.util.Assert; * * @author Rico Bergmann */ -public class KeepSubTypeExpression extends KeepExpression { +public class KeepSubTypeExpression extends CompoundKeepExpression { /** * The {@code KeepSubTypeBuilder} enables the construction of new {@code KeepSubTypeExpression} @@ -132,6 +132,7 @@ public class KeepSubTypeExpression extends KeepExpression { * the instances of the subclass instances. */ @Nonnull + @Override public List<KeepExpression> getKeeps() { return new ArrayList<>(keeps); } diff --git a/src/main/java/org/rosi_project/model_sync/model_join/representation/grammar/KeepSuperTypeExpression.java b/src/main/java/org/rosi_project/model_sync/model_join/representation/grammar/KeepSuperTypeExpression.java index 703adefd8e24e401f39d7d68c20fa8d1449150ab..6c72a3d9a42fdef32eafe621ceba232af271b803 100644 --- a/src/main/java/org/rosi_project/model_sync/model_join/representation/grammar/KeepSuperTypeExpression.java +++ b/src/main/java/org/rosi_project/model_sync/model_join/representation/grammar/KeepSuperTypeExpression.java @@ -16,7 +16,7 @@ import org.rosi_project.model_sync.model_join.representation.util.Assert; * * @author Rico Bergmann */ -public class KeepSuperTypeExpression extends KeepExpression { +public class KeepSuperTypeExpression extends CompoundKeepExpression { /** * The {@code KeepSuperTypeBuilder} enables the construction of new {@code KeepSuperTypeExpression} @@ -131,6 +131,7 @@ public class KeepSuperTypeExpression extends KeepExpression { * the instances of the superclass instances. */ @Nonnull + @Override public List<KeepExpression> getKeeps() { return new ArrayList<>(keeps); } diff --git a/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/FileBasedModelJoinWriter.java b/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/FileBasedModelJoinWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..79cc9ee12917ec4384333b595f7656cf172f0981 --- /dev/null +++ b/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/FileBasedModelJoinWriter.java @@ -0,0 +1,34 @@ +package org.rosi_project.model_sync.model_join.representation.writer; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.StandardOpenOption; +import org.rosi_project.model_sync.model_join.representation.grammar.ModelJoinExpression; + +class FileBasedModelJoinWriter implements ModelJoinWriter<Boolean> { + + private final File outputFile; + private final StringBasedModelJoinWriter toStringWriter; + + FileBasedModelJoinWriter(File outputFile) { + this.outputFile = outputFile; + this.toStringWriter = StringBasedModelJoinWriter.withNewlines(); + } + + @Override + public Boolean write(ModelJoinExpression modelJoin) { + try (BufferedWriter writer = Files.newBufferedWriter( + outputFile.toPath(), + StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) { + + String modelJoinTextRepresentation = toStringWriter.write(modelJoin); + writer.write(modelJoinTextRepresentation); + + return true; + } catch (IOException ioe) { + return false; + } + } +} diff --git a/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/ModelJoinWriterFactory.java b/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/ModelJoinWriterFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..0fe9dff02df00c925616403303f8dccb5fd75174 --- /dev/null +++ b/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/ModelJoinWriterFactory.java @@ -0,0 +1,16 @@ +package org.rosi_project.model_sync.model_join.representation.writer; + +import java.io.File; +import org.rosi_project.model_sync.model_join.representation.grammar.ModelJoinExpression; + +public class ModelJoinWriterFactory { + + public static String writeToString(ModelJoinExpression modelJoin) { + return StringBasedModelJoinWriter.createDefault().write(modelJoin); + } + + public static boolean writeToFile(File outputFile, ModelJoinExpression modelJoin) { + return new FileBasedModelJoinWriter(outputFile).write(modelJoin); + } + +} diff --git a/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/StringBasedModelJoinWriter.java b/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/StringBasedModelJoinWriter.java index aca5a8a5869661ac49cc4c8a766d2d552331a7b3..15870e0275f2a39e95390e77f51882f2c309d2cb 100644 --- a/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/StringBasedModelJoinWriter.java +++ b/src/main/java/org/rosi_project/model_sync/model_join/representation/writer/StringBasedModelJoinWriter.java @@ -2,6 +2,7 @@ package org.rosi_project.model_sync.model_join.representation.writer; import java.util.StringJoiner; import javax.annotation.Nonnull; +import org.rosi_project.model_sync.model_join.representation.grammar.CompoundKeepExpression; import org.rosi_project.model_sync.model_join.representation.grammar.JoinExpression; import org.rosi_project.model_sync.model_join.representation.grammar.KeepAggregateExpression; import org.rosi_project.model_sync.model_join.representation.grammar.KeepAttributesExpression; @@ -17,11 +18,37 @@ import org.rosi_project.model_sync.model_join.representation.grammar.ThetaJoinEx import org.rosi_project.model_sync.model_join.representation.util.Functional; import org.rosi_project.model_sync.model_join.representation.util.Nothing; -public class StringBasedModelJoinWriter implements ModelJoinWriter<String> { +class StringBasedModelJoinWriter implements ModelJoinWriter<String> { + + private static final String NEWLINE_DELIM = "\n"; + private static final String EMPTY_DELIM = " "; + private static final String DEFAULT_INDENTATION = " "; + + public static StringBasedModelJoinWriter createDefault() { + return new StringBasedModelJoinWriter(false); + } + + public static StringBasedModelJoinWriter withNewlines() { + return new StringBasedModelJoinWriter(true); + } + + private final String delimiter; + private final boolean useNewlineDelimiters; + + private String currentIndentation = ""; + + private StringBasedModelJoinWriter(boolean useNewlinesDelimiters) { + if (useNewlinesDelimiters) { + this.delimiter = NEWLINE_DELIM; + } else { + this.delimiter = EMPTY_DELIM; + } + this.useNewlineDelimiters = useNewlinesDelimiters; + } @Override public String write(@Nonnull ModelJoinExpression modelJoin) { - StringJoiner joiner = new StringJoiner("\n\n"); + StringJoiner joiner = new StringJoiner(delimiter + delimiter); modelJoin.getJoins().forEach( joinStatement -> joiner.merge(writeJoin(joinStatement)) @@ -47,7 +74,7 @@ public class StringBasedModelJoinWriter implements ModelJoinWriter<String> { } }) .runOrThrow(); - joiner.add(String.format("%s %s with %s as %s", joinHeader, join.getLeft(), join.getRight(), join.getTarget())); + joiner.add(String.format(currentIndentation + "%s %s with %s as %s", joinHeader, join.getLeft(), join.getRight(), join.getTarget())); Functional.<Nothing>match(join) .caseOf(ThetaJoinExpression.class, thetaJoin -> { joiner.add("where").add(thetaJoin.getCondition().toString()); @@ -57,8 +84,10 @@ public class StringBasedModelJoinWriter implements ModelJoinWriter<String> { .runOrThrow(); joiner.add("{\n"); - join.getKeeps().forEach(keep -> joiner.add(writeKeep(keep) + "\n")); - joiner.add("}"); + increaseIndentationIfNecessary(); + join.getKeeps().forEach(keep -> joiner.add(currentIndentation + writeKeep(keep) + delimiter)); + decreaseIndentationIfNecessary(); + joiner.add(currentIndentation + "}"); return joiner; } @@ -81,47 +110,45 @@ public class StringBasedModelJoinWriter implements ModelJoinWriter<String> { keepAggregate.getAggregatedAttribute(), keepAggregate.getSource(), keepAggregate.getTarget()) ) - .caseOf(KeepReferenceExpression.class, this::writeKeepReference) - .caseOf(KeepSubTypeExpression.class, this::writeKeepSubType) - .caseOf(KeepSuperTypeExpression.class, this::writeKeepSuperType) + .caseOf(KeepReferenceExpression.class, keepRef -> { + String header = String.format("keep %s %s as type %s {" + delimiter, keepRef.getReferenceDirection().name().toLowerCase(), keepRef.getAttribute(), keepRef.getTarget()); + return writeCompound(keepRef, header); + }) + .caseOf(KeepSubTypeExpression.class, keepSubtype -> { + String header = String.format("keep subtype %s as type %s {" + delimiter, keepSubtype.getType(), keepSubtype.getTarget()); + return writeCompound(keepSubtype, header); + }) + .caseOf(KeepSuperTypeExpression.class, keepSupertype -> { + String header = String.format("keep supertype %s as type %s {" + delimiter, keepSupertype.getType(), keepSupertype.getTarget()); + return writeCompound(keepSupertype, header); + }) .runOrThrow(); } - private String writeKeepReference(KeepReferenceExpression keepRef) { + private String writeCompound(CompoundKeepExpression compoundKeep, String header) { StringJoiner refJoiner = new StringJoiner(" "); - String header = String.format("keep %s %s as type %s {\n", keepRef.getReferenceDirection().name().toLowerCase(), keepRef.getAttribute(), keepRef.getTarget()); - refJoiner.add(header); - keepRef.getKeeps().forEach(keep -> refJoiner.add(writeKeep(keep) + "\n")); - refJoiner.add("}"); + increaseIndentationIfNecessary(); + compoundKeep.getKeeps().forEach(keep -> refJoiner.add(currentIndentation + writeKeep(keep) + delimiter)); + decreaseIndentationIfNecessary(); + refJoiner.add(currentIndentation + "}"); return refJoiner.toString(); } - private String writeKeepSubType(KeepSubTypeExpression keepSubType) { - StringJoiner refJoiner = new StringJoiner(" "); - - String header = String.format("keep subtype %s as type %s {\n", keepSubType.getType(), keepSubType.getTarget()); - - refJoiner.add(header); - keepSubType.getKeeps().forEach(keep -> refJoiner.add(writeKeep(keep) + "\n")); - refJoiner.add("}"); - - return refJoiner.toString(); + private void increaseIndentationIfNecessary() { + if (useNewlineDelimiters) { + currentIndentation += DEFAULT_INDENTATION; + } } - private String writeKeepSuperType(KeepSuperTypeExpression keepSuperType) { - StringJoiner refJoiner = new StringJoiner(" "); - - String header = String.format("keep supertype %s as type %s {\n", keepSuperType.getType(), keepSuperType.getTarget()); - - refJoiner.add(header); - keepSuperType.getKeeps().forEach(keep -> refJoiner.add(writeKeep(keep) + "\n")); - refJoiner.add("}"); - - return refJoiner.toString(); + private void decreaseIndentationIfNecessary() { + if (useNewlineDelimiters) { + int deletionIdx = currentIndentation.lastIndexOf(DEFAULT_INDENTATION); + currentIndentation = currentIndentation.substring(0, deletionIdx); + } } } diff --git a/src/test/java/org/rosi_project/model_sync/model_join/representation/writer/FileBasedModelJoinWriterAcceptanceTests.java b/src/test/java/org/rosi_project/model_sync/model_join/representation/writer/FileBasedModelJoinWriterAcceptanceTests.java new file mode 100644 index 0000000000000000000000000000000000000000..115556369bdefdd7850579d6c81c04ceb348390f --- /dev/null +++ b/src/test/java/org/rosi_project/model_sync/model_join/representation/writer/FileBasedModelJoinWriterAcceptanceTests.java @@ -0,0 +1,64 @@ +package org.rosi_project.model_sync.model_join.representation.writer; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.rosi_project.model_sync.model_join.representation.writer.TestedModels.Listing2; + +class FileBasedModelJoinWriterAcceptanceTests { + + private static final Logger log = Logger.getLogger(FileBasedModelJoinWriterAcceptanceTests.class.getSimpleName()); + + private List<File> createdFiles = new ArrayList<>(); + + @AfterEach + void tearDown() { + createdFiles.forEach(file -> { + boolean result = file.delete(); + log.info(String.format("Deleting file %s, successfull=%s", file, result)); + }); + createdFiles.clear(); + } + + /** + * Depends on {@link StringBasedModelJoinWriterAcceptanceTests#writeProducesListing2FromTechnicalReport()} + */ + @Test + void writeProducesListing2FromTechnicalReport() { + File outputFile = new File("listing2.modeljoin"); + registerCreatedFile(outputFile); + + FileBasedModelJoinWriter writer = new FileBasedModelJoinWriter(outputFile); + writer.write(Listing2.asModelJoin); + + logFileContent(outputFile); + + String expectedContent = StringBasedModelJoinWriter.withNewlines().write(Listing2.asModelJoin); + assertThat(outputFile).exists(); + assertThat(outputFile).hasContent(expectedContent); + } + + private void registerCreatedFile(File newFile) { + createdFiles.add(newFile); + log.info(String.format("Created new file %s", newFile)); + } + + private void logFileContent(File file) { + try { + String contents = Files.lines(file.toPath()).collect(Collectors.joining()); + log.info(String.format("Content of file %s:", file)); + log.info(contents); + } catch (IOException e) { + log.severe(String.format("Could not read file %s", file)); + } + } + +} diff --git a/src/test/java/org/rosi_project/model_sync/model_join/representation/writer/StringBasedModelJoinWriterAcceptanceTests.java b/src/test/java/org/rosi_project/model_sync/model_join/representation/writer/StringBasedModelJoinWriterAcceptanceTests.java index 3136ebe15d60112418cf4ecfd7bf70fd426c730f..716d8d54e9644a7c9eaa2e0f0c6e9e09d8c9e474 100644 --- a/src/test/java/org/rosi_project/model_sync/model_join/representation/writer/StringBasedModelJoinWriterAcceptanceTests.java +++ b/src/test/java/org/rosi_project/model_sync/model_join/representation/writer/StringBasedModelJoinWriterAcceptanceTests.java @@ -1,84 +1,31 @@ package org.rosi_project.model_sync.model_join.representation.writer; -import static org.rosi_project.model_sync.model_join.representation.util.ModelJoinBuilder.attributes; -import static org.rosi_project.model_sync.model_join.representation.util.ModelJoinBuilder.outgoing; -import static org.rosi_project.model_sync.model_join.representation.util.ModelJoinBuilder.supertype; -import static org.rosi_project.model_sync.model_join.representation.util.ModelJoinBuilder.thetaJoin; import static org.assertj.core.api.Assertions.*; +import java.util.logging.Logger; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; -import org.rosi_project.model_sync.model_join.representation.core.AttributePath; -import org.rosi_project.model_sync.model_join.representation.core.ClassResource; -import org.rosi_project.model_sync.model_join.representation.grammar.ModelJoinExpression; -import org.rosi_project.model_sync.model_join.representation.util.ModelJoinBuilder; -import org.rosi_project.model_sync.util.NeedsCleanup; +import org.rosi_project.model_sync.model_join.representation.writer.TestedModels.Listing2; -@RunWith(JUnitPlatform.class) -@NeedsCleanup class StringBasedModelJoinWriterAcceptanceTests { - /* - * The technical report which forms the basis of the tests here is - * - * Burger, E. et Al.: "ModelJoin - A Textual Domain-Specific Language for the Combination of - * Heterogeneous Models"; Karslruhe Institute of Technololgy 2014 - */ + private static final Logger log = Logger.getLogger(StringBasedModelJoinWriterAcceptanceTests.class.getSimpleName()); + /** + * @see TestedModels + */ @Test void writeProducesListing2FromTechnicalReport() { + StringBasedModelJoinWriter writer = StringBasedModelJoinWriter.withNewlines(); + String writerOutput = writer.write(Listing2.asModelJoin); - ClassResource imdbFilm = ClassResource.from("imdb", "Film"); - ClassResource libraryVideoCassette = ClassResource.from("library", "VideoCassette"); - ClassResource libraryAudioVisualItem = ClassResource.from("library", "AudioVisualItem"); - ClassResource jointargetMovie = ClassResource.from("jointarget", "Movie"); - ClassResource jointargetVote = ClassResource.from("jointarget", "Vote"); - ClassResource jointargetMediaItem = ClassResource.from("jointarget", "MediaItem"); - - AttributePath imdbFilmYear = AttributePath.from(imdbFilm, "year"); - AttributePath imdbFilmVotes = AttributePath.from(imdbFilm, "votes"); - AttributePath imdbVoteScore = AttributePath.from("imdb.Vote", "score"); - AttributePath libraryAudioVisualItemMinutesLength = AttributePath.from(libraryAudioVisualItem, "minutesLength"); - - ModelJoinExpression listing2 = ModelJoinBuilder.createNewModelJoin() - .add(thetaJoin() - .join(imdbFilm) - .with(libraryVideoCassette) - .as(jointargetMovie) - .where( - "library.VideoCassette.cast->forAll (p | imdb.Film.figures->playedBy->exists (a | p.firstname.concat(\" \") .concat(p.lastName) == a.name))") - .keep(attributes(imdbFilmYear)) - .keep(outgoing(imdbFilmVotes) - .as(jointargetVote) - .keep(attributes(imdbVoteScore)) - .buildExpression() - ) - .keep(supertype(libraryAudioVisualItem) - .as(jointargetMediaItem) - .keep(attributes(libraryAudioVisualItemMinutesLength)) - .buildExpression() - ) - .done()) - .build(); - - StringBasedModelJoinWriter writer = new StringBasedModelJoinWriter(); - String writerOutput = writer.write(listing2); + log.info("Writer output:"); + log.info(writerOutput); String sanitizedWriterOutput = sanitize(writerOutput); - String expectedOutput = sanitize("theta join imdb.Film with library.VideoCassette as jointarget.Movie\n" - + "where library.VideoCassette.cast->forAll (p | imdb.Film.figures->playedBy->exists (a | p.\n" - + "firstname.concat(\" \") .concat(p.lastName) == a.name)) {\n" - + "keep attributes imdb.Film.year\n" - + "keep outgoing imdb.Film.votes as type jointarget.Vote {\n" - + "keep attributes imdb.Vote.score\n" - + "}\n" - + "keep supertype library.AudioVisualItem as type jointarget.MediaItem {\n" - + " keep attributes library.AudioVisualItem.minutesLength\n" - + "}\n" - + "}"); - + String expectedOutput = sanitize(Listing2.asString); // Although AssertJ provides an isEqualToIgnoringWhitespace as well as an // isEqualToIgnoringNewline method, there's none that ignores both. Thus we reside to manual diff --git a/src/test/java/org/rosi_project/model_sync/model_join/representation/writer/TestedModels.java b/src/test/java/org/rosi_project/model_sync/model_join/representation/writer/TestedModels.java new file mode 100644 index 0000000000000000000000000000000000000000..4a00cb911e8d353c283070350f6c26dd96f0f310 --- /dev/null +++ b/src/test/java/org/rosi_project/model_sync/model_join/representation/writer/TestedModels.java @@ -0,0 +1,69 @@ +package org.rosi_project.model_sync.model_join.representation.writer; + +import static org.rosi_project.model_sync.model_join.representation.util.ModelJoinBuilder.attributes; +import static org.rosi_project.model_sync.model_join.representation.util.ModelJoinBuilder.outgoing; +import static org.rosi_project.model_sync.model_join.representation.util.ModelJoinBuilder.supertype; +import static org.rosi_project.model_sync.model_join.representation.util.ModelJoinBuilder.thetaJoin; + +import org.rosi_project.model_sync.model_join.representation.core.AttributePath; +import org.rosi_project.model_sync.model_join.representation.core.ClassResource; +import org.rosi_project.model_sync.model_join.representation.grammar.ModelJoinExpression; +import org.rosi_project.model_sync.model_join.representation.util.ModelJoinBuilder; + +class TestedModels { + + /* + * The technical report which forms the basis of the tests here is + * + * Burger, E. et Al.: "ModelJoin - A Textual Domain-Specific Language for the Combination of + * Heterogeneous Models"; Karslruhe Institute of Technololgy 2014 + */ + + static class Listing2 { + static final ClassResource imdbFilm = ClassResource.from("imdb", "Film"); + static final ClassResource libraryVideoCassette = ClassResource.from("library", "VideoCassette"); + static final ClassResource libraryAudioVisualItem = ClassResource.from("library", "AudioVisualItem"); + static final ClassResource jointargetMovie = ClassResource.from("jointarget", "Movie"); + static final ClassResource jointargetVote = ClassResource.from("jointarget", "Vote"); + static final ClassResource jointargetMediaItem = ClassResource.from("jointarget", "MediaItem"); + + static final AttributePath imdbFilmYear = AttributePath.from(imdbFilm, "year"); + static final AttributePath imdbFilmVotes = AttributePath.from(imdbFilm, "votes"); + static final AttributePath imdbVoteScore = AttributePath.from("imdb.Vote", "score"); + static final AttributePath libraryAudioVisualItemMinutesLength = AttributePath.from(libraryAudioVisualItem, "minutesLength"); + + static final ModelJoinExpression asModelJoin = ModelJoinBuilder.createNewModelJoin() + .add(thetaJoin() + .join(imdbFilm) + .with(libraryVideoCassette) + .as(jointargetMovie) + .where( + "library.VideoCassette.cast->forAll (p | imdb.Film.figures->playedBy->exists (a | p.firstname.concat(\" \") .concat(p.lastName) == a.name))") + .keep(attributes(imdbFilmYear)) + .keep(outgoing(imdbFilmVotes) + .as(jointargetVote) + .keep(attributes(imdbVoteScore)) + .buildExpression() + ) + .keep(supertype(libraryAudioVisualItem) + .as(jointargetMediaItem) + .keep(attributes(libraryAudioVisualItemMinutesLength)) + .buildExpression() + ) + .done()) + .build(); + + static final String asString = "theta join imdb.Film with library.VideoCassette as jointarget.Movie\n" + + "where library.VideoCassette.cast->forAll (p | imdb.Film.figures->playedBy->exists (a | p.\n" + + "firstname.concat(\" \") .concat(p.lastName) == a.name)) {\n" + + "keep attributes imdb.Film.year\n" + + "keep outgoing imdb.Film.votes as type jointarget.Vote {\n" + + "keep attributes imdb.Vote.score\n" + + "}\n" + + "keep supertype library.AudioVisualItem as type jointarget.MediaItem {\n" + + " keep attributes library.AudioVisualItem.minutesLength\n" + + "}\n" + + "}"; + } + +}