From 663274763e56b228efe07363b8ede4ce7bebc251 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Tue, 19 Oct 2021 03:36:26 +0200 Subject: chore: remove builtin library xtext dependency --- language-model/build.gradle | 2 + language-model/plugin.xml | 7 + .../refinery/language/model/ProblemEMFSetup.java | 34 +++++ .../tools/refinery/language/model/ProblemUtil.java | 102 ++++++++++++++ .../src/main/resources/model/builtin.problem_xmi | 67 +++++++++ .../src/main/resources/model/problem.ecore | 3 +- .../src/main/resources/model/problem.genmodel | 4 +- .../language/model/tests/ProblemTestUtil.java | 154 +++++++++++++++++++++ 8 files changed, 369 insertions(+), 4 deletions(-) create mode 100644 language-model/src/main/java/tools/refinery/language/model/ProblemEMFSetup.java create mode 100644 language-model/src/main/java/tools/refinery/language/model/ProblemUtil.java create mode 100644 language-model/src/main/resources/model/builtin.problem_xmi create mode 100644 language-model/src/testFixtures/java/tools/refinery/language/model/tests/ProblemTestUtil.java (limited to 'language-model') diff --git a/language-model/build.gradle b/language-model/build.gradle index d01f8dfc..46ae839b 100644 --- a/language-model/build.gradle +++ b/language-model/build.gradle @@ -1,9 +1,11 @@ apply plugin: 'java-library' +apply plugin: 'java-test-fixtures' apply from: "${rootDir}/gradle/java-common.gradle" apply from: "${rootDir}/gradle/mwe2.gradle" dependencies { api "org.eclipse.emf:org.eclipse.emf.ecore:${ecoreVersion}" + api "org.eclipse.emf:org.eclipse.emf.ecore.xmi:${ecoreXmiVersion}" mwe2 "org.eclipse.emf:org.eclipse.emf.codegen.ecore:${ecoreCodegenVersion}" mwe2 "org.eclipse.emf:org.eclipse.emf.mwe.utils:${mweVersion}" mwe2 "org.eclipse.emf:org.eclipse.emf.mwe2.lib:${mwe2Version}" diff --git a/language-model/plugin.xml b/language-model/plugin.xml index 1e1a246e..4ca005a8 100644 --- a/language-model/plugin.xml +++ b/language-model/plugin.xml @@ -14,4 +14,11 @@ genModel="src/main/resources/model/problem.genmodel"/> + + + + + diff --git a/language-model/src/main/java/tools/refinery/language/model/ProblemEMFSetup.java b/language-model/src/main/java/tools/refinery/language/model/ProblemEMFSetup.java new file mode 100644 index 00000000..9383098b --- /dev/null +++ b/language-model/src/main/java/tools/refinery/language/model/ProblemEMFSetup.java @@ -0,0 +1,34 @@ +package tools.refinery.language.model; + +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.resource.Resource; + +import tools.refinery.language.model.problem.ProblemPackage; +import tools.refinery.language.model.problem.impl.ProblemFactoryImpl; + +public class ProblemEMFSetup { + public static final String XMI_RESOURCE_EXTENSION = "problem_xmi"; + + private ProblemEMFSetup() { + throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); + } + + // Here we can't rely on java.util.HashMap#putIfAbsent, because + // org.eclipse.emf.ecore.impl.EPackageRegistryImpl#containsKey is overridden + // without also overriding putIfAbsent. We must make sure to call the + // overridden containsKey implementation. + @SuppressWarnings("squid:S3824") + public static void doEMFRegistration() { + if (!EPackage.Registry.INSTANCE.containsKey(ProblemPackage.eNS_URI)) { + EPackage.Registry.INSTANCE.put(ProblemPackage.eNS_URI, ProblemPackage.eINSTANCE); + } + + // This Resource.Factory is not actually used once + // tools.refinery.language.ProblemStandaloneSetup.createInjectorAndDoEMFRegistration() + // is called, because if will be replaced by + // tools.refinery.language.resource.ProblemXmiResourceFactory, which implements + // org.eclipse.xtext.resource.IResourceFactory as required by Xtext. + Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().putIfAbsent(XMI_RESOURCE_EXTENSION, + new ProblemFactoryImpl()); + } +} diff --git a/language-model/src/main/java/tools/refinery/language/model/ProblemUtil.java b/language-model/src/main/java/tools/refinery/language/model/ProblemUtil.java new file mode 100644 index 00000000..b6b199f8 --- /dev/null +++ b/language-model/src/main/java/tools/refinery/language/model/ProblemUtil.java @@ -0,0 +1,102 @@ +package tools.refinery.language.model; + +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Deque; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; + +import tools.refinery.language.model.problem.ClassDeclaration; +import tools.refinery.language.model.problem.Node; +import tools.refinery.language.model.problem.Problem; +import tools.refinery.language.model.problem.ProblemPackage; +import tools.refinery.language.model.problem.ReferenceDeclaration; +import tools.refinery.language.model.problem.Relation; +import tools.refinery.language.model.problem.Variable; + +public final class ProblemUtil { + public static final String BUILTIN_LIBRARY_NAME = "builtin"; + + public static final URI BUILTIN_LIBRARY_URI = getLibraryUri(BUILTIN_LIBRARY_NAME); + + public static final String NODE_CLASS_NAME = "node"; + + private ProblemUtil() { + throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); + } + + public static boolean isSingletonVariable(Variable variable) { + return variable.eContainingFeature() == ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__SINGLETON_VARIABLE; + } + + public static boolean isUniqueNode(Node node) { + var containingFeature = node.eContainingFeature(); + return containingFeature == ProblemPackage.Literals.UNIQUE_DECLARATION__NODES + || containingFeature == ProblemPackage.Literals.ENUM_DECLARATION__LITERALS; + } + + public static boolean isNewNode(Node node) { + return node.eContainingFeature() == ProblemPackage.Literals.CLASS_DECLARATION__NEW_NODE; + } + + public static Optional getBuiltInLibrary(EObject context) { + return Optional.ofNullable(context.eResource()).map(Resource::getResourceSet) + .map(resourceSet -> resourceSet.getResource(BUILTIN_LIBRARY_URI, true)).map(Resource::getContents) + .filter(contents -> !contents.isEmpty()).map(contents -> contents.get(0)) + .filter(Problem.class::isInstance).map(Problem.class::cast); + } + + public static boolean isBuiltIn(EObject eObject) { + if (eObject != null) { + var eResource = eObject.eResource(); + if (eResource != null) { + return BUILTIN_LIBRARY_URI.equals(eResource.getURI()); + } + } + return false; + } + + public static Optional getNodeClassDeclaration(EObject context) { + return getBuiltInLibrary(context).flatMap(problem -> problem.getStatements().stream() + .filter(ClassDeclaration.class::isInstance).map(ClassDeclaration.class::cast) + .filter(declaration -> NODE_CLASS_NAME.equals(declaration.getName())).findFirst()); + } + + public static Collection getSuperclassesAndSelf(ClassDeclaration classDeclaration) { + Set found = new HashSet<>(); + getNodeClassDeclaration(classDeclaration).ifPresent(found::add); + Deque queue = new ArrayDeque<>(); + queue.addLast(classDeclaration); + while (!queue.isEmpty()) { + ClassDeclaration current = queue.removeFirst(); + if (!found.contains(current)) { + found.add(current); + for (Relation superType : current.getSuperTypes()) { + if (superType instanceof ClassDeclaration superDeclaration) { + queue.addLast(superDeclaration); + } + } + } + } + return found; + } + + public static Collection getAllReferenceDeclarations(ClassDeclaration classDeclaration) { + Set referenceDeclarations = new HashSet<>(); + for (ClassDeclaration superclass : getSuperclassesAndSelf(classDeclaration)) { + referenceDeclarations.addAll(superclass.getReferenceDeclarations()); + } + return referenceDeclarations; + } + + private static URI getLibraryUri(String libraryName) { + return URI.createURI(ProblemUtil.class.getClassLoader() + .getResource("model/" + libraryName + "." + ProblemEMFSetup.XMI_RESOURCE_EXTENSION) + .toString()); + } +} diff --git a/language-model/src/main/resources/model/builtin.problem_xmi b/language-model/src/main/resources/model/builtin.problem_xmi new file mode 100644 index 00000000..9255ab66 --- /dev/null +++ b/language-model/src/main/resources/model/builtin.problem_xmi @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/language-model/src/main/resources/model/problem.ecore b/language-model/src/main/resources/model/problem.ecore index a62590ac..e86f0afd 100644 --- a/language-model/src/main/resources/model/problem.ecore +++ b/language-model/src/main/resources/model/problem.ecore @@ -1,7 +1,6 @@ + xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="problem" nsURI="https://refinery.tools/emf/2021/Problem" nsPrefix="problem"> diff --git a/language-model/src/main/resources/model/problem.genmodel b/language-model/src/main/resources/model/problem.genmodel index e529977f..057b4917 100644 --- a/language-model/src/main/resources/model/problem.genmodel +++ b/language-model/src/main/resources/model/problem.genmodel @@ -8,8 +8,8 @@ copyrightFields="false" operationReflection="true" importOrganizing="true"> problem.ecore - + diff --git a/language-model/src/testFixtures/java/tools/refinery/language/model/tests/ProblemTestUtil.java b/language-model/src/testFixtures/java/tools/refinery/language/model/tests/ProblemTestUtil.java new file mode 100644 index 00000000..b412ed1f --- /dev/null +++ b/language-model/src/testFixtures/java/tools/refinery/language/model/tests/ProblemTestUtil.java @@ -0,0 +1,154 @@ +package tools.refinery.language.model.tests; + +import java.util.List; +import java.util.stream.Stream; + +import org.eclipse.emf.ecore.resource.Resource.Diagnostic; +import org.eclipse.emf.ecore.util.EcoreUtil; + +import tools.refinery.language.model.ProblemUtil; +import tools.refinery.language.model.problem.Argument; +import tools.refinery.language.model.problem.Assertion; +import tools.refinery.language.model.problem.AssertionArgument; +import tools.refinery.language.model.problem.Atom; +import tools.refinery.language.model.problem.ClassDeclaration; +import tools.refinery.language.model.problem.Conjunction; +import tools.refinery.language.model.problem.EnumDeclaration; +import tools.refinery.language.model.problem.Literal; +import tools.refinery.language.model.problem.NamedElement; +import tools.refinery.language.model.problem.NegativeLiteral; +import tools.refinery.language.model.problem.Node; +import tools.refinery.language.model.problem.NodeAssertionArgument; +import tools.refinery.language.model.problem.NodeValueAssertion; +import tools.refinery.language.model.problem.Parameter; +import tools.refinery.language.model.problem.PredicateDefinition; +import tools.refinery.language.model.problem.Problem; +import tools.refinery.language.model.problem.ReferenceDeclaration; +import tools.refinery.language.model.problem.Relation; +import tools.refinery.language.model.problem.Statement; +import tools.refinery.language.model.problem.UniqueDeclaration; +import tools.refinery.language.model.problem.Variable; +import tools.refinery.language.model.problem.VariableOrNode; +import tools.refinery.language.model.problem.VariableOrNodeArgument; + +public class ProblemTestUtil { + public Problem builtin(Problem problem) { + return ProblemUtil.getBuiltInLibrary(problem).get(); + } + + public List errors(Problem problem) { + EcoreUtil.resolveAll(problem); + return problem.eResource().getErrors(); + } + + public List nodeNames(Problem problem) { + return problem.getNodes().stream().map(node -> node.getName()).toList(); + } + + public PredicateDefinition pred(Problem problem, String name) { + return namedStatementOfType(problem, PredicateDefinition.class, name); + } + + public Parameter param(PredicateDefinition definition, int i) { + return definition.getParameters().get(i); + } + + public Conjunction conj(PredicateDefinition definition, int i) { + return definition.getBodies().get(i); + } + + public Literal lit(Conjunction conjunction, int i) { + return conjunction.getLiterals().get(i); + } + + public Atom negated(Literal literal) { + return ((NegativeLiteral) literal).getAtom(); + } + + public Relation relation(Literal literal) { + return ((Atom) literal).getRelation(); + } + + public Argument arg(Atom atom, int i) { + return atom.getArguments().get(i); + } + + public Argument arg(Literal literal, int i) { + return arg((Atom) literal, i); + } + + public VariableOrNode variableOrNode(Argument argument) { + return ((VariableOrNodeArgument) argument).getVariableOrNode(); + } + + public Variable variable(Argument argument) { + return (Variable) variableOrNode(argument); + } + + public Node node(Argument argument) { + return (Node) variableOrNode(argument); + } + + public Assertion assertion(Problem problem, int i) { + return nthStatementOfType(problem, Assertion.class, i); + } + + public AssertionArgument arg(Assertion assertion, int i) { + return assertion.getArguments().get(i); + } + + public Node node(AssertionArgument argument) { + return ((NodeAssertionArgument) argument).getNode(); + } + + public Node node(Problem problem, String name) { + return named(problem.getNodes(), name); + } + + public Node uniqueNode(Problem problem, String name) { + var uniqueNodes = statementsOfType(problem, UniqueDeclaration.class) + .flatMap(declaration -> declaration.getNodes().stream()); + return named(uniqueNodes, name); + } + + public NodeValueAssertion nodeValueAssertion(Problem problem, int i) { + return nthStatementOfType(problem, NodeValueAssertion.class, i); + } + + public ClassDeclaration findClass(Problem problem, String name) { + return namedStatementOfType(problem, ClassDeclaration.class, name); + } + + public ReferenceDeclaration reference(ClassDeclaration declaration, String name) { + return named(declaration.getReferenceDeclarations(), name); + } + + public EnumDeclaration findEnum(Problem problem, String name) { + return namedStatementOfType(problem, EnumDeclaration.class, name); + } + + public Node literal(EnumDeclaration declaration, String name) { + return named(declaration.getLiterals(), name); + } + + private T named(Stream stream, String name) { + return stream.filter(statement -> name.equals(statement.getName())).findAny().get(); + } + + private T named(List list, String name) { + return named(list.stream(), name); + } + + private Stream statementsOfType(Problem problem, Class type) { + return problem.getStatements().stream().filter(type::isInstance).map(type::cast); + } + + private T namedStatementOfType(Problem problem, Class type, + String name) { + return named(statementsOfType(problem, type), name); + } + + private T nthStatementOfType(Problem problem, Class type, int n) { + return statementsOfType(problem, type).skip(n).findFirst().get(); + } +} -- cgit v1.2.3-54-g00ecf