From 97ea34af2f8e5d0ca9da5dda331a9f54e580c4c6 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Thu, 29 Jul 2021 17:09:24 +0200 Subject: Refactoring based on Sonar reports --- build.gradle | 30 +-- gradle/java-common.gradle | 50 +++++ gradle/mwe2.gradle | 11 ++ gradle/source-layout.gradle | 28 --- gradle/xtext-common.gradle | 21 ++ language-ide/build.gradle | 2 + .../ProblemSemanticHighlightingCalculator.java | 85 ++++---- language-model/build.gradle | 18 +- language-mwe2/build.gradle | 2 + .../mwe2/ProblemWebIntegrationFragment.java | 36 ++-- language-web/build.gradle | 8 +- .../viatra/solver/language/web/ProblemServlet.java | 10 +- .../viatra/solver/language/web/ServerLauncher.java | 55 ++---- language/build.gradle | 16 +- .../viatra/solver/language/GenerateProblem.mwe2 | 4 +- .../solver/language/ProblemRuntimeModule.java | 3 + .../solver/language/ProblemStandaloneSetup.java | 13 +- .../viatra/solver/language/ProblemUtil.java | 26 ++- .../conversion/ProblemValueConverterService.java | 4 +- .../language/generator/ProblemGenerator.java | 30 --- .../viatra/solver/language/naming/NamingUtil.java | 35 ++++ .../language/resource/DerivedVariableComputer.java | 195 +++++++++++++++++++ .../language/resource/NodeNameCollector.java | 52 ++--- .../resource/ProblemDerivedStateComputer.java | 213 +++------------------ .../ProblemResourceDescriptionStrategy.java | 67 ++++--- .../language/scoping/ProblemScopeProvider.java | 17 +- .../language/validation/ProblemValidator.java | 18 +- 27 files changed, 551 insertions(+), 498 deletions(-) create mode 100644 gradle/java-common.gradle create mode 100644 gradle/mwe2.gradle delete mode 100644 gradle/source-layout.gradle create mode 100644 gradle/xtext-common.gradle delete mode 100644 language/src/main/java/org/eclipse/viatra/solver/language/generator/ProblemGenerator.java create mode 100644 language/src/main/java/org/eclipse/viatra/solver/language/naming/NamingUtil.java create mode 100644 language/src/main/java/org/eclipse/viatra/solver/language/resource/DerivedVariableComputer.java diff --git a/build.gradle b/build.gradle index e1d31ecc..1736d644 100644 --- a/build.gradle +++ b/build.gradle @@ -12,35 +12,7 @@ plugins { id 'com.moowork.node' version "1.3.1" apply false } -subprojects { - repositories { - mavenCentral() - } - - apply plugin: 'java' - dependencies { - compile platform("org.eclipse.xtext:xtext-dev-bom:${xtextVersion}") - } - - apply plugin: 'org.xtext.xtend' - apply from: "${rootDir}/gradle/source-layout.gradle" - apply plugin: 'eclipse' - +allprojects { group = 'org.eclipse.viatra.solver' version = '1.0.0-SNAPSHOT' - - sourceCompatibility = '11' - targetCompatibility = '11' - - configurations.all { - exclude group: 'asm' - } - - eclipse.classpath.file.whenMerged { - for (entry in entries) { - if (entry.path.endsWith('xtext-gen')) { - entry.entryAttributes['ignore_optional_problems'] = true - } - } - } } diff --git a/gradle/java-common.gradle b/gradle/java-common.gradle new file mode 100644 index 00000000..0cc09a4f --- /dev/null +++ b/gradle/java-common.gradle @@ -0,0 +1,50 @@ +repositories { + mavenCentral() +} + +apply plugin: 'java' +apply plugin: 'org.xtext.xtend' + +sourceCompatibility = '11' +targetCompatibility = '11' + +sourceSets { + main { + xtendOutputDir = 'src/main/xtend-gen' + } + test { + xtendOutputDir = 'src/test/xtend-gen' + } +} + +configurations.all { + exclude group: 'asm' +} + +jar { + manifest { + attributes 'Bundle-SymbolicName': project.name + } +} + +clean.doLast { + delete 'src/main/xtend-gen' + delete 'src/test/xtend-gen' +} + +apply plugin: 'eclipse' + +eclipse { + classpath.file.whenMerged { + for (entry in entries) { + if (entry.path.endsWith('-gen')) { + entry.entryAttributes['ignore_optional_problems'] = true + } + } + } + + jdt.file.whenMerged { properties -> + // Allow @SupperessWarnings to suppress SonalLint warnings + properties['org.eclipse.jdt.core.compiler.problem.unhandledWarningToken'] = 'ignore' + } +} diff --git a/gradle/mwe2.gradle b/gradle/mwe2.gradle new file mode 100644 index 00000000..5fd53126 --- /dev/null +++ b/gradle/mwe2.gradle @@ -0,0 +1,11 @@ +configurations { + mwe2 { + extendsFrom compile + } +} + +dependencies { + mwe2 "org.eclipse.emf:org.eclipse.emf.mwe2.launch:${mwe2Version}" +} + +eclipse.classpath.plusConfigurations += [configurations.mwe2] diff --git a/gradle/source-layout.gradle b/gradle/source-layout.gradle deleted file mode 100644 index 254798f1..00000000 --- a/gradle/source-layout.gradle +++ /dev/null @@ -1,28 +0,0 @@ -sourceSets { - main { - java.srcDirs = ['src/main/java', 'src/main/xtext-gen'] - resources.srcDirs = ['src/main/resources', 'src/main/xtext-gen'] - xtendOutputDir = 'src/main/xtend-gen' - } - test { - java.srcDirs = ['src/test/java', 'src/test/xtext-gen'] - resources.srcDirs = ['src/test/resources', 'src/test/xtext-gen'] - xtendOutputDir = 'src/test/xtend-gen' - } -} - -jar { - from(sourceSets.main.allSource) { - include '**/*.xtext' - } - manifest { - attributes 'Bundle-SymbolicName': project.name - } -} - -clean.doLast { - delete 'src/main/xtend-gen' - delete 'src/main/xtext-gen' - delete 'src/test/xtend-gen' - delete 'src/test/xtext-gen' -} diff --git a/gradle/xtext-common.gradle b/gradle/xtext-common.gradle new file mode 100644 index 00000000..cf6a5831 --- /dev/null +++ b/gradle/xtext-common.gradle @@ -0,0 +1,21 @@ +apply from: "${rootDir}/gradle/java-common.gradle" + +dependencies { + compile platform("org.eclipse.xtext:xtext-dev-bom:${xtextVersion}") +} + +sourceSets { + main { + java.srcDirs += ['src/main/xtext-gen'] + resources.srcDirs += ['src/main/xtext-gen'] + } + test { + java.srcDirs = ['src/test/xtext-gen'] + resources.srcDirs = ['src/test/xtext-gen'] + } +} + +clean.doLast { + delete 'src/main/xtext-gen' + delete 'src/test/xtext-gen' +} diff --git a/language-ide/build.gradle b/language-ide/build.gradle index 396f63bd..1731cc59 100644 --- a/language-ide/build.gradle +++ b/language-ide/build.gradle @@ -1,3 +1,5 @@ +apply from: "${rootDir}/gradle/xtext-common.gradle" + dependencies { compile project(':language') compile "org.eclipse.xtext:org.eclipse.xtext.ide:${xtextVersion}" diff --git a/language-ide/src/main/java/org/eclipse/viatra/solver/language/ide/syntaxcoloring/ProblemSemanticHighlightingCalculator.java b/language-ide/src/main/java/org/eclipse/viatra/solver/language/ide/syntaxcoloring/ProblemSemanticHighlightingCalculator.java index eee5070b..b1a69e9d 100644 --- a/language-ide/src/main/java/org/eclipse/viatra/solver/language/ide/syntaxcoloring/ProblemSemanticHighlightingCalculator.java +++ b/language-ide/src/main/java/org/eclipse/viatra/solver/language/ide/syntaxcoloring/ProblemSemanticHighlightingCalculator.java @@ -3,26 +3,24 @@ package org.eclipse.viatra.solver.language.ide.syntaxcoloring; import java.util.List; import org.eclipse.emf.common.util.EList; -import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; -import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.viatra.solver.language.ProblemUtil; import org.eclipse.viatra.solver.language.model.problem.ClassDeclaration; import org.eclipse.viatra.solver.language.model.problem.EnumDeclaration; +import org.eclipse.viatra.solver.language.model.problem.NamedElement; import org.eclipse.viatra.solver.language.model.problem.Node; import org.eclipse.viatra.solver.language.model.problem.Parameter; import org.eclipse.viatra.solver.language.model.problem.PredicateDefinition; +import org.eclipse.viatra.solver.language.model.problem.ProblemPackage; import org.eclipse.viatra.solver.language.model.problem.ReferenceDeclaration; import org.eclipse.viatra.solver.language.model.problem.Variable; -import org.eclipse.viatra.solver.language.scoping.ProblemGlobalScopeProvider; import org.eclipse.xtext.ide.editor.syntaxcoloring.DefaultSemanticHighlightingCalculator; import org.eclipse.xtext.ide.editor.syntaxcoloring.IHighlightedPositionAcceptor; import org.eclipse.xtext.nodemodel.INode; import org.eclipse.xtext.nodemodel.util.NodeModelUtils; import org.eclipse.xtext.service.OperationCanceledManager; import org.eclipse.xtext.util.CancelIndicator; -import org.eclipse.xtext.util.SimpleAttributeResolver; import com.google.common.collect.ImmutableList; import com.google.inject.Inject; @@ -49,19 +47,18 @@ public class ProblemSemanticHighlightingCalculator extends DefaultSemanticHighli @Override protected boolean highlightElement(EObject object, IHighlightedPositionAcceptor acceptor, CancelIndicator cancelIndicator) { - highlightName(object, acceptor, cancelIndicator); + highlightName(object, acceptor); highlightCrossReferences(object, acceptor, cancelIndicator); return false; } - - protected void highlightName(EObject object, IHighlightedPositionAcceptor acceptor, - CancelIndicator cancelIndicator) { + + protected void highlightName(EObject object, IHighlightedPositionAcceptor acceptor) { + if (!(object instanceof NamedElement)) { + return; + } String[] highlightClass = getHighlightClass(object); if (highlightClass.length > 0) { - EAttribute nameAttribute = SimpleAttributeResolver.NAME_RESOLVER.getAttribute(object); - if (nameAttribute != null) { - highlightFeature(acceptor, object, nameAttribute, highlightClass); - } + highlightFeature(acceptor, object, ProblemPackage.Literals.NAMED_ELEMENT__NAME, highlightClass); } } @@ -73,36 +70,44 @@ public class ProblemSemanticHighlightingCalculator extends DefaultSemanticHighli } operationCanceledManager.checkCanceled(cancelIndicator); if (reference.isMany()) { - @SuppressWarnings("unchecked") - EList values = (EList) object.eGet(reference); - List nodes = NodeModelUtils.findNodesForFeature(object, reference); - int size = Math.min(values.size(), nodes.size()); - for (int i = 0; i < size; i++) { - EObject valueInList = values.get(i); - INode node = nodes.get(i); - String[] highlightClass = getHighlightClass(valueInList); - if (highlightClass.length > 0) { - highlightNode(acceptor, node, highlightClass); - } - } + highlightManyValues(object, reference, acceptor); } else { - EObject valueObj = (EObject) object.eGet(reference); - String[] highlightClass = getHighlightClass(valueObj); - if (highlightClass.length > 0) { - highlightFeature(acceptor, object, reference, highlightClass); - } + highlightSingleValue(object, reference, acceptor); + } + } + } + + protected void highlightSingleValue(EObject object, EReference reference, IHighlightedPositionAcceptor acceptor) { + EObject valueObj = (EObject) object.eGet(reference); + String[] highlightClass = getHighlightClass(valueObj); + if (highlightClass.length > 0) { + highlightFeature(acceptor, object, reference, highlightClass); + } + } + + protected void highlightManyValues(EObject object, EReference reference, IHighlightedPositionAcceptor acceptor) { + @SuppressWarnings("unchecked") + EList values = (EList) object.eGet(reference); + List nodes = NodeModelUtils.findNodesForFeature(object, reference); + int size = Math.min(values.size(), nodes.size()); + for (var i = 0; i < size; i++) { + EObject valueInList = values.get(i); + INode node = nodes.get(i); + String[] highlightClass = getHighlightClass(valueInList); + if (highlightClass.length > 0) { + highlightNode(acceptor, node, highlightClass); } } } protected String[] getHighlightClass(EObject eObject) { - if (isBuiltIn(eObject)) { + if (ProblemUtil.isBuiltIn(eObject)) { return new String[] { BUILTIN_CLASS }; } ImmutableList.Builder classesBuilder = ImmutableList.builder(); if (eObject instanceof ClassDeclaration) { classesBuilder.add(CLASS_CLASS); - ClassDeclaration classDeclaration = (ClassDeclaration) eObject; + var classDeclaration = (ClassDeclaration) eObject; if (classDeclaration.isAbstract()) { classesBuilder.add(ABSTRACT_CLASS); } @@ -112,21 +117,21 @@ public class ProblemSemanticHighlightingCalculator extends DefaultSemanticHighli } if (eObject instanceof ReferenceDeclaration) { classesBuilder.add(REFERENCE_CLASS); - ReferenceDeclaration referenceDeclaration = (ReferenceDeclaration) eObject; + var referenceDeclaration = (ReferenceDeclaration) eObject; if (referenceDeclaration.isContainment()) { classesBuilder.add(CONTAINMENT_CLASS); } } if (eObject instanceof PredicateDefinition) { classesBuilder.add(PREDICATE_CLASS); - PredicateDefinition predicateDefinition = (PredicateDefinition) eObject; + var predicateDefinition = (PredicateDefinition) eObject; if (predicateDefinition.isError()) { classesBuilder.add(ERROR_CLASS); } } if (eObject instanceof Node) { classesBuilder.add(NODE_CLASS); - Node node = (Node) eObject; + var node = (Node) eObject; if (ProblemUtil.isEnumNode(node)) { classesBuilder.add(ENUM_NODE_CLASS); } @@ -139,7 +144,7 @@ public class ProblemSemanticHighlightingCalculator extends DefaultSemanticHighli } if (eObject instanceof Variable) { classesBuilder.add(VARIABLE_CLASS); - Variable variable = (Variable) eObject; + var variable = (Variable) eObject; if (ProblemUtil.isSingletonVariable(variable)) { classesBuilder.add(SINGLETON_VARIABLE_CLASS); } @@ -147,14 +152,4 @@ public class ProblemSemanticHighlightingCalculator extends DefaultSemanticHighli List classes = classesBuilder.build(); return classes.toArray(new String[0]); } - - protected boolean isBuiltIn(EObject eObject) { - if (eObject != null) { - Resource eResource = eObject.eResource(); - if (eResource != null) { - return ProblemGlobalScopeProvider.BULTIN_LIBRARY_URI.equals(eResource.getURI()); - } - } - return false; - } } diff --git a/language-model/build.gradle b/language-model/build.gradle index 56ab4a19..2d9b54b9 100644 --- a/language-model/build.gradle +++ b/language-model/build.gradle @@ -1,14 +1,10 @@ -configurations { - mwe2 { - extendsFrom compile - } -} +apply from: "${rootDir}/gradle/java-common.gradle" +apply from: "${rootDir}/gradle/mwe2.gradle" dependencies { compile "org.eclipse.emf:org.eclipse.emf.ecore:${ecoreVersion}" 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.launch:${mwe2Version}" mwe2 "org.eclipse.emf:org.eclipse.emf.mwe2.lib:${mwe2Version}" mwe2 "org.eclipse.xtext:org.eclipse.xtext:${xtextVersion}" mwe2 "org.eclipse.xtext:org.eclipse.xtext.xbase:${xtextVersion}" @@ -35,12 +31,4 @@ task generateEPackage(type: JavaExec) { compileJava.dependsOn(generateEPackage) clean.dependsOn(cleanGenerateEPackage) -eclipse { - project { - natures += ['org.eclipse.sirius.nature.modelingproject', 'org.eclipse.pde.PluginNature'] - } - - classpath { - plusConfigurations += [configurations.mwe2] - } -} \ No newline at end of file +eclipse.project.natures += ['org.eclipse.sirius.nature.modelingproject', 'org.eclipse.pde.PluginNature'] diff --git a/language-mwe2/build.gradle b/language-mwe2/build.gradle index 8596b4c1..ae456c39 100644 --- a/language-mwe2/build.gradle +++ b/language-mwe2/build.gradle @@ -1,3 +1,5 @@ +apply from: "${rootDir}/gradle/java-common.gradle" + dependencies { compile "org.eclipse.xtext:org.eclipse.xtext.xtext.generator:${xtextVersion}" } diff --git a/language-mwe2/src/main/java/org/eclipse/viatra/solver/language/mwe2/ProblemWebIntegrationFragment.java b/language-mwe2/src/main/java/org/eclipse/viatra/solver/language/mwe2/ProblemWebIntegrationFragment.java index 83b56bc6..78311e0d 100644 --- a/language-mwe2/src/main/java/org/eclipse/viatra/solver/language/mwe2/ProblemWebIntegrationFragment.java +++ b/language-mwe2/src/main/java/org/eclipse/viatra/solver/language/mwe2/ProblemWebIntegrationFragment.java @@ -7,6 +7,10 @@ import org.eclipse.xtext.xtext.generator.web.WebIntegrationFragment; import com.google.common.collect.Multimap; public class ProblemWebIntegrationFragment extends WebIntegrationFragment { + private static final String START_STATE = "start"; + private static final String PREDICATE_BODY_STATE = "predicateBody"; + private static final String CM_MODE_META = "meta"; + public ProblemWebIntegrationFragment() { setFramework(Framework.CODEMIRROR.name()); // We use our custom token style for single-quoted names @@ -36,25 +40,27 @@ public class ProblemWebIntegrationFragment extends WebIntegrationFragment { protected Multimap createCodeMirrorPatterns(String langId, Set keywords) { Multimap patterns = super.createCodeMirrorPatterns(langId, keywords); // We use our custom token style for single-quoted names - patterns.put("start", "{token: \"quoted-name\", regex: \"['](?:(?:\\\\\\\\.)|(?:[^'\\\\\\\\]))*?[']\"}"); + patterns.put(START_STATE, "{token: \"quoted-name\", regex: \"['](?:(?:\\\\\\\\.)|(?:[^'\\\\\\\\]))*?[']\"}"); // Use the CodeMirror default .cm-number instead of .cm-constant.cm-numeric - patterns.put("start", + patterns.put(START_STATE, "{token: \"number\", regex: \"[+-]?\\\\d+(?:(?:\\\\.\\\\d*)?(?:[eE][+-]?\\\\d+)?)?\\\\b\"}"); - patterns.put("start", "{token: \"number\", regex: \"[*]\"}"); + patterns.put(START_STATE, "{token: \"number\", regex: \"[*]\"}"); // We use our own custom single-line comments - patterns.put("start", "{token: \"comment\", regex: \"%.*$\"}"); - patterns.put("start", "{token: \"comment\", regex: \"\\\\/\\\\/.*$\"}"); - patterns.put("meta", "lineComment: \"%\""); + patterns.put(START_STATE, "{token: \"comment\", regex: \"%.*$\"}"); + patterns.put(START_STATE, "{token: \"comment\", regex: \"\\\\/\\\\/.*$\"}"); + patterns.put(CM_MODE_META, "lineComment: \"%\""); // Override indentation behavior - patterns.put("start", "{token: \"lparen\", indent: true, regex: \"[[({]\"}"); - patterns.put("start", "{token: \"rparen\", dedent: true, regex: \"[\\\\])}]\"}"); - patterns.putAll("predicateBody", patterns.get("start")); - patterns.put("start", "{indent: true, push: \"predicateBody\", regex: \"<=>\"}"); - patterns.put("predicateBody", "{dedent: true, dedentIfLineStart: false, pop: true, regex: \"\\\\.\\\\s*$\"}"); - patterns.put("predicateBody", "{indent: true, dedent: true, regex: \"[;]\"}"); - // We must repeat the keyword rule here, because Xtext only adds it to "main" later. - patterns.put("predicateBody", "{token: \"keyword\", regex: \"\\\\b(?:\" + keywords + \")\\\\b\"}"); - patterns.put("meta", "electricChars: \"])];\""); + patterns.put(START_STATE, "{token: \"lparen\", indent: true, regex: \"[[({]\"}"); + patterns.put(START_STATE, "{token: \"rparen\", dedent: true, regex: \"[\\\\])}]\"}"); + patterns.putAll(PREDICATE_BODY_STATE, patterns.get(START_STATE)); + patterns.put(START_STATE, "{indent: true, push: \"" + PREDICATE_BODY_STATE + "\", regex: \"<=>\"}"); + patterns.put(PREDICATE_BODY_STATE, + "{dedent: true, dedentIfLineStart: false, pop: true, regex: \"\\\\.\\\\s*$\"}"); + patterns.put(PREDICATE_BODY_STATE, "{indent: true, dedent: true, regex: \"[;]\"}"); + // We must repeat the keyword rule here, because Xtext only adds it to "main" + // later + patterns.put(PREDICATE_BODY_STATE, "{token: \"keyword\", regex: \"\\\\b(?:\" + keywords + \")\\\\b\"}"); + patterns.put(CM_MODE_META, "electricChars: \"])];\""); return patterns; } } diff --git a/language-web/build.gradle b/language-web/build.gradle index e04af7cb..e00b88d8 100644 --- a/language-web/build.gradle +++ b/language-web/build.gradle @@ -1,3 +1,5 @@ +apply from: "${rootDir}/gradle/xtext-common.gradle" + dependencies { compile project(':language') compile project(':language-ide') @@ -94,9 +96,7 @@ task webpackServe(type: NpmTask) { } eclipse { - project { - file.whenMerged { - natures.remove('org.eclipse.wst.common.modulecore.ModuleCoreNature') - } + project.file.whenMerged { + natures.remove('org.eclipse.wst.common.modulecore.ModuleCoreNature') } } diff --git a/language-web/src/main/java/org/eclipse/viatra/solver/language/web/ProblemServlet.java b/language-web/src/main/java/org/eclipse/viatra/solver/language/web/ProblemServlet.java index 5de708f0..337df54b 100644 --- a/language-web/src/main/java/org/eclipse/viatra/solver/language/web/ProblemServlet.java +++ b/language-web/src/main/java/org/eclipse/viatra/solver/language/web/ProblemServlet.java @@ -3,9 +3,9 @@ */ package org.eclipse.viatra.solver.language.web; -import com.google.inject.Injector; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; + import org.eclipse.xtext.util.DisposableRegistry; import org.eclipse.xtext.web.servlet.XtextServlet; @@ -17,14 +17,18 @@ public class ProblemServlet extends XtextServlet { private static final long serialVersionUID = 1L; - DisposableRegistry disposableRegistry; + // Xtext requires a mutable servlet instance field. + @SuppressWarnings("squid:S2226") + private DisposableRegistry disposableRegistry; + @Override public void init() throws ServletException { super.init(); - Injector injector = new ProblemWebSetup().createInjectorAndDoEMFRegistration(); + var injector = new ProblemWebSetup().createInjectorAndDoEMFRegistration(); this.disposableRegistry = injector.getInstance(DisposableRegistry.class); } + @Override public void destroy() { if (disposableRegistry != null) { disposableRegistry.dispose(); diff --git a/language-web/src/main/java/org/eclipse/viatra/solver/language/web/ServerLauncher.java b/language-web/src/main/java/org/eclipse/viatra/solver/language/web/ServerLauncher.java index efa6b034..2c0413df 100644 --- a/language-web/src/main/java/org/eclipse/viatra/solver/language/web/ServerLauncher.java +++ b/language-web/src/main/java/org/eclipse/viatra/solver/language/web/ServerLauncher.java @@ -7,7 +7,6 @@ import java.io.IOException; import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; -import java.net.URL; import org.eclipse.jetty.annotations.AnnotationConfiguration; import org.eclipse.jetty.server.Server; @@ -24,9 +23,9 @@ public class ServerLauncher { private final Server server; - public ServerLauncher(InetSocketAddress bindAddress, Resource baseResource) throws IOException, URISyntaxException { + public ServerLauncher(InetSocketAddress bindAddress, Resource baseResource) { server = new Server(bindAddress); - WebAppContext ctx = new WebAppContext(); + var ctx = new WebAppContext(); ctx.setBaseResource(baseResource); ctx.setWelcomeFiles(new String[] { "index.html" }); ctx.setContextPath("/"); @@ -40,27 +39,13 @@ public class ServerLauncher { public void start() throws Exception { server.start(); LOG.info("Server started " + server.getURI() + "..."); - new Thread() { - public void run() { - try { - LOG.info("Press enter to stop the server..."); - int key = System.in.read(); - if (key != -1) { - server.stop(); - } else { - LOG.warn( - "Console input is not available. In order to stop the server, you need to cancel process manually."); - } - } catch (Exception e) { - LOG.warn(e); - System.exit(-1); - } - } - }.start(); - try { - server.join(); - } catch (InterruptedException e) { - LOG.info(e); + LOG.info("Press enter to stop the server..."); + int key = System.in.read(); + if (key != -1) { + server.stop(); + } else { + LOG.warn( + "Console input is not available. In order to stop the server, you need to cancel process manually."); } } @@ -75,34 +60,34 @@ public class ServerLauncher { if (baseResourceOverride != null) { return Resource.newResource(baseResourceOverride); } - URL indexUrlInJar = ServerLauncher.class.getResource("/webapp/index.html"); + var indexUrlInJar = ServerLauncher.class.getResource("/webapp/index.html"); if (indexUrlInJar == null) { - throw new RuntimeException("Cannot find pacakged web assets"); + throw new IOException("Cannot find pacakged web assets"); } - URI webRootUri = URI.create(indexUrlInJar.toURI().toASCIIString().replaceFirst("/index.html$", "/")); + var webRootUri = URI.create(indexUrlInJar.toURI().toASCIIString().replaceFirst("/index.html$", "/")); return Resource.newResource(webRootUri); } public static void main(String[] args) { - String listenAddress = System.getenv("LISTEN_ADDRESS"); + var listenAddress = System.getenv("LISTEN_ADDRESS"); if (listenAddress == null) { listenAddress = "localhost"; } - int port = 1312; - String portStr = System.getenv("LISTEN_PORT"); + var port = 1312; + var portStr = System.getenv("LISTEN_PORT"); if (portStr != null) { try { port = Integer.parseInt(portStr); } catch (NumberFormatException e) { LOG.warn(e); - System.exit(-1); + System.exit(1); } } - String baseResourceOverride = System.getenv("BASE_RESOURCE"); + var baseResourceOverride = System.getenv("BASE_RESOURCE"); try { - InetSocketAddress bindAddress = getBindAddress(listenAddress, port); - Resource baseResource = getBaseResource(baseResourceOverride); - ServerLauncher serverLauncher = new ServerLauncher(bindAddress, baseResource); + var bindAddress = getBindAddress(listenAddress, port); + var baseResource = getBaseResource(baseResourceOverride); + var serverLauncher = new ServerLauncher(bindAddress, baseResource); serverLauncher.start(); } catch (Exception exception) { LOG.warn(exception); diff --git a/language/build.gradle b/language/build.gradle index 7e079f27..5ad5ab1f 100644 --- a/language/build.gradle +++ b/language/build.gradle @@ -1,8 +1,5 @@ -configurations { - mwe2 { - extendsFrom compile - } -} +apply from: "${rootDir}/gradle/xtext-common.gradle" +apply from: "${rootDir}/gradle/mwe2.gradle" dependencies { testCompile "org.junit.jupiter:junit-jupiter-api:${junitVersion}" @@ -15,13 +12,18 @@ dependencies { compile "org.eclipse.xtext:org.eclipse.xtext.xbase:${xtextVersion}" compile "org.eclipse.emf:org.eclipse.emf.ecore:${ecoreVersion}" compile project(':language-model') - mwe2 "org.eclipse.emf:org.eclipse.emf.mwe2.launch:${mwe2Version}" mwe2 "org.eclipse.xtext:org.eclipse.xtext.common.types:${xtextVersion}" mwe2 "org.eclipse.xtext:org.eclipse.xtext.xtext.generator:${xtextVersion}" mwe2 "org.eclipse.xtext:xtext-antlr-generator:${xtextAntlrGeneratorVersion}" mwe2 project(':language-mwe2') } +jar { + from(sourceSets.main.allSource) { + include '**/*.xtext' + } +} + task generateXtextLanguage(type: JavaExec) { main = 'org.eclipse.emf.mwe2.launch.runtime.Mwe2Launcher' classpath = configurations.mwe2 @@ -43,5 +45,3 @@ test { generateXtext.dependsOn(generateXtextLanguage) clean.dependsOn(cleanGenerateXtextLanguage) - -eclipse.classpath.plusConfigurations += [configurations.mwe2] diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/GenerateProblem.mwe2 b/language/src/main/java/org/eclipse/viatra/solver/language/GenerateProblem.mwe2 index 18f70080..ca3e2665 100644 --- a/language/src/main/java/org/eclipse/viatra/solver/language/GenerateProblem.mwe2 +++ b/language/src/main/java/org/eclipse/viatra/solver/language/GenerateProblem.mwe2 @@ -44,7 +44,9 @@ Workflow { validator = { generateDeprecationValidation = true } - generator = null + generator = { + generateStub = false + } junitSupport = { junitVersion = "5" } diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/ProblemRuntimeModule.java b/language/src/main/java/org/eclipse/viatra/solver/language/ProblemRuntimeModule.java index eb9ddd77..9fec7d75 100644 --- a/language/src/main/java/org/eclipse/viatra/solver/language/ProblemRuntimeModule.java +++ b/language/src/main/java/org/eclipse/viatra/solver/language/ProblemRuntimeModule.java @@ -62,6 +62,8 @@ public class ProblemRuntimeModule extends AbstractProblemRuntimeModule { return DerivedStateAwareResource.class; } + // Method name follows Xtext convention. + @SuppressWarnings("squid:S100") public Class bindIResourceDescription$Manager() { return DerivedStateAwareResourceDescriptionManager.class; } @@ -74,6 +76,7 @@ public class ProblemRuntimeModule extends AbstractProblemRuntimeModule { return ProblemDerivedStateComputer.class; } + @Override public Class bindILocationInFileProvider() { return ProblemLocationInFileProvider.class; } diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/ProblemStandaloneSetup.java b/language/src/main/java/org/eclipse/viatra/solver/language/ProblemStandaloneSetup.java index 5652f859..11e5ad8a 100644 --- a/language/src/main/java/org/eclipse/viatra/solver/language/ProblemStandaloneSetup.java +++ b/language/src/main/java/org/eclipse/viatra/solver/language/ProblemStandaloneSetup.java @@ -9,18 +9,25 @@ import org.eclipse.viatra.solver.language.model.problem.ProblemPackage; import com.google.inject.Injector; /** - * Initialization support for running Xtext languages without Equinox extension registry. + * Initialization support for running Xtext languages without Equinox extension + * registry. */ public class ProblemStandaloneSetup extends ProblemStandaloneSetupGenerated { public static void doSetup() { new ProblemStandaloneSetup().createInjectorAndDoEMFRegistration(); } - + @Override + // Here we can't rely on java.util.HashMap#computeIfAbsent, because + // org.eclipse.emf.ecore.impl.EPackageRegistryImpl#containsKey is overridden + // without also overriding computeIfAbsent. We must make sure to call the + // overridden containsKey implementation. + @SuppressWarnings("squid:S3824") public Injector createInjectorAndDoEMFRegistration() { - if (!EPackage.Registry.INSTANCE.containsKey(ProblemPackage.eNS_URI)) + if (!EPackage.Registry.INSTANCE.containsKey(ProblemPackage.eNS_URI)) { EPackage.Registry.INSTANCE.put(ProblemPackage.eNS_URI, ProblemPackage.eINSTANCE); + } return super.createInjectorAndDoEMFRegistration(); } } diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/ProblemUtil.java b/language/src/main/java/org/eclipse/viatra/solver/language/ProblemUtil.java index e0a72687..2d7fede6 100644 --- a/language/src/main/java/org/eclipse/viatra/solver/language/ProblemUtil.java +++ b/language/src/main/java/org/eclipse/viatra/solver/language/ProblemUtil.java @@ -16,27 +16,18 @@ import org.eclipse.viatra.solver.language.model.problem.ProblemPackage; import org.eclipse.viatra.solver.language.model.problem.ReferenceDeclaration; import org.eclipse.viatra.solver.language.model.problem.Relation; import org.eclipse.viatra.solver.language.model.problem.Variable; +import org.eclipse.viatra.solver.language.naming.NamingUtil; import org.eclipse.viatra.solver.language.scoping.ProblemGlobalScopeProvider; import com.google.common.collect.ImmutableList; -import com.google.inject.Singleton; -@Singleton public final class ProblemUtil { private ProblemUtil() { throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); } - public static final String SINGLETON_VARIABLE_PREFIX = "_"; - - public static final String ENUM_NODE_NAME_QUOTE = "'"; - public static final String NODE_CLASS_NAME = "node"; - public static boolean isSingletonVariableName(String name) { - return name != null && name.startsWith(SINGLETON_VARIABLE_PREFIX); - } - public static boolean isSingletonVariable(Variable variable) { return variable.eContainingFeature() == ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__SINGLETON_VARIABLE; } @@ -46,10 +37,7 @@ public final class ProblemUtil { } public static boolean isEnumNode(Node node) { - String name = node.getName(); - boolean isNameQuoted = name != null && name.startsWith(ENUM_NODE_NAME_QUOTE) - && name.endsWith(ENUM_NODE_NAME_QUOTE); - return isNameQuoted || isEnumLiteral(node); + return NamingUtil.isQuotedName(node.getName()) || isEnumLiteral(node); } public static boolean isNewNode(Node node) { @@ -63,6 +51,16 @@ public final class ProblemUtil { .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 ProblemGlobalScopeProvider.BULTIN_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) diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/conversion/ProblemValueConverterService.java b/language/src/main/java/org/eclipse/viatra/solver/language/conversion/ProblemValueConverterService.java index 4f5fd069..1c665e6f 100644 --- a/language/src/main/java/org/eclipse/viatra/solver/language/conversion/ProblemValueConverterService.java +++ b/language/src/main/java/org/eclipse/viatra/solver/language/conversion/ProblemValueConverterService.java @@ -9,8 +9,10 @@ import com.google.inject.Inject; public class ProblemValueConverterService extends DefaultTerminalConverters { @Inject private UpperBoundValueConverter upperBoundValueConverter; - + @ValueConverter(rule = "UpperBound") + // Method name follows Xtext convention. + @SuppressWarnings("squid:S100") public IValueConverter UpperBound() { return upperBoundValueConverter; } diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/generator/ProblemGenerator.java b/language/src/main/java/org/eclipse/viatra/solver/language/generator/ProblemGenerator.java deleted file mode 100644 index b6ea3553..00000000 --- a/language/src/main/java/org/eclipse/viatra/solver/language/generator/ProblemGenerator.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * generated by Xtext 2.26.0.M1 - */ -package org.eclipse.viatra.solver.language.generator; - -import org.eclipse.emf.ecore.resource.Resource; -import org.eclipse.xtext.generator.AbstractGenerator; -import org.eclipse.xtext.generator.IFileSystemAccess2; -import org.eclipse.xtext.generator.IGeneratorContext; - -/** - * Generates code from your model files on save. - * - * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#code-generation - */ -public class ProblemGenerator extends AbstractGenerator { - - @Override - public void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) { -// Iterator filtered = Iterators.filter(resource.getAllContents(), Greeting.class); -// Iterator names = Iterators.transform(filtered, new Function() { -// -// @Override -// public String apply(Greeting greeting) { -// return greeting.getName(); -// } -// }); -// fsa.generateFile("greetings.txt", "People to greet: " + IteratorExtensions.join(names, ", ")); - } -} diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/naming/NamingUtil.java b/language/src/main/java/org/eclipse/viatra/solver/language/naming/NamingUtil.java new file mode 100644 index 00000000..decc014a --- /dev/null +++ b/language/src/main/java/org/eclipse/viatra/solver/language/naming/NamingUtil.java @@ -0,0 +1,35 @@ +package org.eclipse.viatra.solver.language.naming; + +import java.util.regex.Pattern; + +public final class NamingUtil { + private static final String SINGLETON_VARIABLE_PREFIX = "_"; + + private static final String ENUM_NODE_NAME_QUOTE = "'"; + + private static final Pattern ID_REGEX = Pattern.compile("[_a-zA-Z][_0-9a-zA-Z]*"); + + private NamingUtil() { + throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); + } + + public static boolean isNullOrEmpty(String name) { + return name == null || name.isEmpty(); + } + + public static boolean isSingletonVariableName(String name) { + return name != null && name.startsWith(SINGLETON_VARIABLE_PREFIX); + } + + public static boolean isQuotedName(String name) { + return name != null && name.startsWith(ENUM_NODE_NAME_QUOTE) && name.endsWith(ENUM_NODE_NAME_QUOTE); + } + + public static boolean isValidId(String name) { + return name != null && ID_REGEX.matcher(name).matches(); + } + + public static boolean isValidNodeName(String name) { + return isValidId(name) || isQuotedName(name); + } +} diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/resource/DerivedVariableComputer.java b/language/src/main/java/org/eclipse/viatra/solver/language/resource/DerivedVariableComputer.java new file mode 100644 index 00000000..1b0146b6 --- /dev/null +++ b/language/src/main/java/org/eclipse/viatra/solver/language/resource/DerivedVariableComputer.java @@ -0,0 +1,195 @@ +package org.eclipse.viatra.solver.language.resource; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.viatra.solver.language.model.problem.Argument; +import org.eclipse.viatra.solver.language.model.problem.Atom; +import org.eclipse.viatra.solver.language.model.problem.Conjunction; +import org.eclipse.viatra.solver.language.model.problem.ExistentialQuantifier; +import org.eclipse.viatra.solver.language.model.problem.ImplicitVariable; +import org.eclipse.viatra.solver.language.model.problem.Literal; +import org.eclipse.viatra.solver.language.model.problem.NegativeLiteral; +import org.eclipse.viatra.solver.language.model.problem.Parameter; +import org.eclipse.viatra.solver.language.model.problem.PredicateDefinition; +import org.eclipse.viatra.solver.language.model.problem.Problem; +import org.eclipse.viatra.solver.language.model.problem.ProblemFactory; +import org.eclipse.viatra.solver.language.model.problem.ProblemPackage; +import org.eclipse.viatra.solver.language.model.problem.Statement; +import org.eclipse.viatra.solver.language.model.problem.VariableOrNodeArgument; +import org.eclipse.viatra.solver.language.naming.NamingUtil; +import org.eclipse.xtext.linking.impl.LinkingHelper; +import org.eclipse.xtext.naming.IQualifiedNameConverter; +import org.eclipse.xtext.nodemodel.INode; +import org.eclipse.xtext.nodemodel.util.NodeModelUtils; +import org.eclipse.xtext.scoping.IScope; +import org.eclipse.xtext.scoping.IScopeProvider; +import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.google.inject.name.Named; + +@Singleton +public class DerivedVariableComputer { + @Inject + private LinkingHelper linkingHelper; + + @Inject + private IQualifiedNameConverter qualifiedNameConverter; + + @Inject + @Named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE) + private IScopeProvider scopeProvider; + + public void installDerivedVariables(Problem problem, Set nodeNames) { + for (Statement statement : problem.getStatements()) { + if (statement instanceof PredicateDefinition) { + PredicateDefinition definition = (PredicateDefinition) statement; + installDerivedPredicateDefinitionState(definition, nodeNames); + } + } + } + + protected void installDerivedPredicateDefinitionState(PredicateDefinition definition, Set nodeNames) { + Set knownVariables = new HashSet<>(); + knownVariables.addAll(nodeNames); + for (Parameter parameter : definition.getParameters()) { + String name = parameter.getName(); + if (name != null) { + knownVariables.add(name); + } + } + for (Conjunction body : definition.getBodies()) { + installDeriveConjunctionState(body, knownVariables); + } + } + + protected void installDeriveConjunctionState(Conjunction conjunction, Set knownVariables) { + Set newVariables = new HashSet<>(); + for (Literal literal : conjunction.getLiterals()) { + if (literal instanceof Atom) { + var atom = (Atom) literal; + createSigletonVariablesAndCollectVariables(atom, knownVariables, newVariables); + } + } + createVariables(conjunction, newVariables); + newVariables.addAll(knownVariables); + for (Literal literal : conjunction.getLiterals()) { + if (literal instanceof NegativeLiteral) { + var negativeLiteral = (NegativeLiteral) literal; + installDeriveNegativeLiteralState(negativeLiteral, newVariables); + } + } + } + + protected void installDeriveNegativeLiteralState(NegativeLiteral negativeLiteral, Set knownVariables) { + Set newVariables = new HashSet<>(); + createSigletonVariablesAndCollectVariables(negativeLiteral.getAtom(), knownVariables, newVariables); + createVariables(negativeLiteral, newVariables); + } + + protected void createSigletonVariablesAndCollectVariables(Atom atom, Set knownVariables, + Set newVariables) { + for (Argument argument : atom.getArguments()) { + if (argument instanceof VariableOrNodeArgument) { + var variableOrNodeArgument = (VariableOrNodeArgument) argument; + IScope scope = scopeProvider.getScope(variableOrNodeArgument, + ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE); + List nodes = NodeModelUtils.findNodesForFeature(variableOrNodeArgument, + ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE); + for (INode node : nodes) { + var variableName = linkingHelper.getCrossRefNodeAsString(node, true); + var created = tryCreateVariableForArgument(variableOrNodeArgument, variableName, scope, + knownVariables, newVariables); + if (created) { + break; + } + } + } + } + } + + protected boolean tryCreateVariableForArgument(VariableOrNodeArgument variableOrNodeArgument, String variableName, + IScope scope, Set knownVariables, Set newVariables) { + if (!NamingUtil.isValidId(variableName)) { + return false; + } + var qualifiedName = qualifiedNameConverter.toQualifiedName(variableName); + if (scope.getSingleElement(qualifiedName) != null) { + return false; + } + if (NamingUtil.isSingletonVariableName(variableName)) { + createSingletonVariable(variableOrNodeArgument, variableName); + return true; + } + if (!knownVariables.contains(variableName)) { + newVariables.add(variableName); + return true; + } + return false; + } + + protected void createVariables(ExistentialQuantifier quantifier, Set newVariables) { + for (String variableName : newVariables) { + createVariable(quantifier, variableName); + } + } + + protected void createVariable(ExistentialQuantifier quantifier, String variableName) { + if (NamingUtil.isValidId(variableName)) { + ImplicitVariable variable = createNamedVariable(variableName); + quantifier.getImplicitVariables().add(variable); + } + } + + protected void createSingletonVariable(VariableOrNodeArgument argument, String variableName) { + if (NamingUtil.isValidId(variableName)) { + ImplicitVariable variable = createNamedVariable(variableName); + argument.setSingletonVariable(variable); + } + } + + protected ImplicitVariable createNamedVariable(String variableName) { + var variable = ProblemFactory.eINSTANCE.createImplicitVariable(); + variable.setName(variableName); + return variable; + } + + public void discardDerivedVariables(Problem problem) { + for (Statement statement : problem.getStatements()) { + if (statement instanceof PredicateDefinition) { + discardPredicateDefinitionState((PredicateDefinition) statement); + } + } + } + + protected void discardPredicateDefinitionState(PredicateDefinition definition) { + for (Conjunction body : definition.getBodies()) { + body.getImplicitVariables().clear(); + for (Literal literal : body.getLiterals()) { + if (literal instanceof Atom) { + discardDerivedAtomState((Atom) literal); + } + if (literal instanceof NegativeLiteral) { + var negativeLiteral = (NegativeLiteral) literal; + negativeLiteral.getImplicitVariables().clear(); + discardDerivedAtomState(negativeLiteral.getAtom()); + } + } + } + } + + protected void discardDerivedAtomState(Atom atom) { + if (atom == null) { + return; + } + for (Argument argument : atom.getArguments()) { + if (argument instanceof VariableOrNodeArgument) { + var variableOrNodeArgument = (VariableOrNodeArgument) argument; + variableOrNodeArgument.setSingletonVariable(null); + } + } + } +} diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/resource/NodeNameCollector.java b/language/src/main/java/org/eclipse/viatra/solver/language/resource/NodeNameCollector.java index 79d7ffbb..597dd92d 100644 --- a/language/src/main/java/org/eclipse/viatra/solver/language/resource/NodeNameCollector.java +++ b/language/src/main/java/org/eclipse/viatra/solver/language/resource/NodeNameCollector.java @@ -1,6 +1,5 @@ package org.eclipse.viatra.solver.language.resource; -import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.Predicate; @@ -21,15 +20,16 @@ import org.eclipse.viatra.solver.language.model.problem.Problem; import org.eclipse.viatra.solver.language.model.problem.ProblemPackage; import org.eclipse.viatra.solver.language.model.problem.Statement; import org.eclipse.viatra.solver.language.model.problem.VariableOrNodeArgument; +import org.eclipse.viatra.solver.language.naming.NamingUtil; import org.eclipse.xtext.linking.impl.LinkingHelper; import org.eclipse.xtext.naming.IQualifiedNameConverter; -import org.eclipse.xtext.naming.QualifiedName; import org.eclipse.xtext.nodemodel.INode; import org.eclipse.xtext.nodemodel.util.NodeModelUtils; import org.eclipse.xtext.scoping.IScope; import org.eclipse.xtext.scoping.IScopeProvider; import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider; +import com.google.common.collect.ImmutableSet; import com.google.inject.Inject; import com.google.inject.name.Named; @@ -44,12 +44,12 @@ public class NodeNameCollector { @Named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE) private IScopeProvider scopeProvider; - private final Set nodeNames = new HashSet<>(); + private final ImmutableSet.Builder nodeNames = ImmutableSet.builder(); private IScope nodeScope; public Set getNodeNames() { - return nodeNames; + return nodeNames.build(); } public void collectNodeNames(Problem problem) { @@ -73,35 +73,39 @@ public class NodeNameCollector { for (AssertionArgument argument : assertion.getArguments()) { if (argument instanceof NodeAssertionArgument) { collectNodeNames(argument, ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE, - ProblemDerivedStateComputer::validNodeName); + NamingUtil::isValidNodeName); } } } protected void collectNodeValueAssertionNodeNames(NodeValueAssertion nodeValueAssertion) { collectNodeNames(nodeValueAssertion, ProblemPackage.Literals.NODE_VALUE_ASSERTION__NODE, - ProblemDerivedStateComputer::validNodeName); + NamingUtil::isValidNodeName); } protected void collectPredicateDefinitionNodeNames(PredicateDefinition predicateDefinition) { for (Conjunction body : predicateDefinition.getBodies()) { for (Literal literal : body.getLiterals()) { - Atom atom = null; - if (literal instanceof Atom) { - atom = (Atom) literal; - } else if (literal instanceof NegativeLiteral) { - NegativeLiteral negativeLiteral = (NegativeLiteral) literal; - atom = negativeLiteral.getAtom(); - } - if (atom == null) { - continue; - } - for (Argument argument : atom.getArguments()) { - if (argument instanceof VariableOrNodeArgument) { - collectNodeNames(argument, ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE, - ProblemDerivedStateComputer::validQuotedId); - } - } + collectLiteralNodeNames(literal); + } + } + } + + protected void collectLiteralNodeNames(Literal literal) { + Atom atom = null; + if (literal instanceof Atom) { + atom = (Atom) literal; + } else if (literal instanceof NegativeLiteral) { + var negativeLiteral = (NegativeLiteral) literal; + atom = negativeLiteral.getAtom(); + } + if (atom == null) { + return; + } + for (Argument argument : atom.getArguments()) { + if (argument instanceof VariableOrNodeArgument) { + collectNodeNames(argument, ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE, + NamingUtil::isQuotedName); } } } @@ -109,11 +113,11 @@ public class NodeNameCollector { private void collectNodeNames(EObject eObject, EStructuralFeature feature, Predicate condition) { List nodes = NodeModelUtils.findNodesForFeature(eObject, feature); for (INode node : nodes) { - String nodeName = linkingHelper.getCrossRefNodeAsString(node, true); + var nodeName = linkingHelper.getCrossRefNodeAsString(node, true); if (!condition.test(nodeName)) { continue; } - QualifiedName qualifiedName = qualifiedNameConverter.toQualifiedName(nodeName); + var qualifiedName = qualifiedNameConverter.toQualifiedName(nodeName); if (nodeScope.getSingleElement(qualifiedName) == null) { nodeNames.add(nodeName); } diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/resource/ProblemDerivedStateComputer.java b/language/src/main/java/org/eclipse/viatra/solver/language/resource/ProblemDerivedStateComputer.java index 81236afd..a2d0eef6 100644 --- a/language/src/main/java/org/eclipse/viatra/solver/language/resource/ProblemDerivedStateComputer.java +++ b/language/src/main/java/org/eclipse/viatra/solver/language/resource/ProblemDerivedStateComputer.java @@ -1,43 +1,26 @@ package org.eclipse.viatra.solver.language.resource; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.regex.Pattern; +import java.util.function.Function; import org.eclipse.emf.common.notify.impl.AdapterImpl; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; -import org.eclipse.viatra.solver.language.ProblemUtil; -import org.eclipse.viatra.solver.language.model.problem.Argument; -import org.eclipse.viatra.solver.language.model.problem.Atom; import org.eclipse.viatra.solver.language.model.problem.ClassDeclaration; -import org.eclipse.viatra.solver.language.model.problem.Conjunction; -import org.eclipse.viatra.solver.language.model.problem.ExistentialQuantifier; -import org.eclipse.viatra.solver.language.model.problem.ImplicitVariable; -import org.eclipse.viatra.solver.language.model.problem.Literal; -import org.eclipse.viatra.solver.language.model.problem.NegativeLiteral; import org.eclipse.viatra.solver.language.model.problem.Node; -import org.eclipse.viatra.solver.language.model.problem.Parameter; -import org.eclipse.viatra.solver.language.model.problem.PredicateDefinition; import org.eclipse.viatra.solver.language.model.problem.Problem; import org.eclipse.viatra.solver.language.model.problem.ProblemFactory; -import org.eclipse.viatra.solver.language.model.problem.ProblemPackage; import org.eclipse.viatra.solver.language.model.problem.Statement; -import org.eclipse.viatra.solver.language.model.problem.VariableOrNodeArgument; import org.eclipse.xtext.Constants; -import org.eclipse.xtext.linking.impl.LinkingHelper; -import org.eclipse.xtext.naming.IQualifiedNameConverter; -import org.eclipse.xtext.naming.QualifiedName; -import org.eclipse.xtext.nodemodel.INode; -import org.eclipse.xtext.nodemodel.util.NodeModelUtils; import org.eclipse.xtext.resource.DerivedStateAwareResource; import org.eclipse.xtext.resource.IDerivedStateComputer; import org.eclipse.xtext.resource.XtextResource; -import org.eclipse.xtext.scoping.IScope; import org.eclipse.xtext.scoping.IScopeProvider; import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider; @@ -50,32 +33,25 @@ import com.google.inject.name.Named; public class ProblemDerivedStateComputer implements IDerivedStateComputer { public static final String NEW_NODE = "new"; - private static final Pattern ID_REGEX = Pattern.compile("[_a-zA-Z][_0-9a-zA-Z]*"); - - private static final Pattern QUOTED_ID_REGEX = Pattern.compile("'(\\\\.|[^\\'])*'"); - @Inject @Named(Constants.LANGUAGE_NAME) private String languageName; - @Inject - private LinkingHelper linkingHelper; - - @Inject - private IQualifiedNameConverter qualifiedNameConverter; - @Inject @Named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE) private IScopeProvider scopeProvider; @Inject private Provider nodeNameCollectorProvider; - + + @Inject + private DerivedVariableComputer derivedVariableComputer; + @Override public void installDerivedState(DerivedStateAwareResource resource, boolean preLinkingPhase) { - Problem problem = getProblem(resource); + var problem = getProblem(resource); if (problem != null) { - Adapter adapter = getOrInstallAdapter(resource); + var adapter = getOrInstallAdapter(resource); installDerivedProblemState(problem, adapter, preLinkingPhase); } } @@ -98,20 +74,15 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer { return; } Set nodeNames = installDerivedNodes(problem); - for (Statement statement : problem.getStatements()) { - if (statement instanceof PredicateDefinition) { - PredicateDefinition definition = (PredicateDefinition) statement; - installDerivedPredicateDefinitionState(definition, nodeNames); - } - } + derivedVariableComputer.installDerivedVariables(problem, nodeNames); } protected void installNewNodes(Problem problem, Adapter adapter) { for (Statement statement : problem.getStatements()) { if (statement instanceof ClassDeclaration) { - ClassDeclaration declaration = (ClassDeclaration) statement; + var declaration = (ClassDeclaration) statement; if (!declaration.isAbstract() && declaration.getNewNode() == null) { - Node newNode = adapter.newNodes.computeIfAbsent(declaration, key -> createNode(NEW_NODE)); + var newNode = adapter.createNodeIfAbsent(declaration, key -> createNode(NEW_NODE)); declaration.setNewNode(newNode); } } @@ -119,123 +90,28 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer { } protected Set installDerivedNodes(Problem problem) { - NodeNameCollector collector = nodeNameCollectorProvider.get(); + var collector = nodeNameCollectorProvider.get(); collector.collectNodeNames(problem); Set nodeNames = collector.getNodeNames(); List grapNodes = problem.getNodes(); for (String nodeName : nodeNames) { - Node graphNode = createNode(nodeName); + var graphNode = createNode(nodeName); grapNodes.add(graphNode); } return nodeNames; } protected Node createNode(String name) { - Node node = ProblemFactory.eINSTANCE.createNode(); + var node = ProblemFactory.eINSTANCE.createNode(); node.setName(name); return node; } - protected void installDerivedPredicateDefinitionState(PredicateDefinition definition, Set nodeNames) { - Set knownVariables = new HashSet<>(); - knownVariables.addAll(nodeNames); - for (Parameter parameter : definition.getParameters()) { - String name = parameter.getName(); - if (name != null) { - knownVariables.add(name); - } - } - for (Conjunction body : definition.getBodies()) { - installDeriveConjunctionState(body, knownVariables); - } - } - - protected void installDeriveConjunctionState(Conjunction conjunction, Set knownVariables) { - Set newVariables = new HashSet<>(); - for (Literal literal : conjunction.getLiterals()) { - if (literal instanceof Atom) { - Atom atom = (Atom) literal; - createSigletonVariablesAndCollectVariables(atom, knownVariables, newVariables); - } - } - createVariables(conjunction, newVariables); - newVariables.addAll(knownVariables); - for (Literal literal : conjunction.getLiterals()) { - if (literal instanceof NegativeLiteral) { - NegativeLiteral negativeLiteral = (NegativeLiteral) literal; - installDeriveNegativeLiteralState(negativeLiteral, newVariables); - } - } - } - - protected void installDeriveNegativeLiteralState(NegativeLiteral negativeLiteral, Set knownVariables) { - Set newVariables = new HashSet<>(); - createSigletonVariablesAndCollectVariables(negativeLiteral.getAtom(), knownVariables, newVariables); - createVariables(negativeLiteral, newVariables); - } - - protected void createSigletonVariablesAndCollectVariables(Atom atom, Set knownVariables, - Set newVariables) { - for (Argument argument : atom.getArguments()) { - if (argument instanceof VariableOrNodeArgument) { - VariableOrNodeArgument variableOrNodeArgument = (VariableOrNodeArgument) argument; - IScope scope = scopeProvider.getScope(variableOrNodeArgument, - ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE); - List nodes = NodeModelUtils.findNodesForFeature(variableOrNodeArgument, - ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE); - for (INode node : nodes) { - String variableName = linkingHelper.getCrossRefNodeAsString(node, true); - if (!validId(variableName)) { - continue; - } - QualifiedName qualifiedName = qualifiedNameConverter.toQualifiedName(variableName); - if (scope.getSingleElement(qualifiedName) != null) { - continue; - } - if (ProblemUtil.isSingletonVariableName(variableName)) { - createSingletonVariable(variableOrNodeArgument, variableName); - break; - } - if (!knownVariables.contains(variableName)) { - newVariables.add(variableName); - break; - } - } - } - } - } - - protected void createVariables(ExistentialQuantifier quantifier, Set newVariables) { - for (String variableName : newVariables) { - createVariable(quantifier, variableName); - } - } - - protected void createVariable(ExistentialQuantifier quantifier, String variableName) { - if (validId(variableName)) { - ImplicitVariable variable = createNamedVariable(variableName); - quantifier.getImplicitVariables().add(variable); - } - } - - protected void createSingletonVariable(VariableOrNodeArgument argument, String variableName) { - if (validId(variableName)) { - ImplicitVariable variable = createNamedVariable(variableName); - argument.setSingletonVariable(variable); - } - } - - protected ImplicitVariable createNamedVariable(String variableName) { - ImplicitVariable variable = ProblemFactory.eINSTANCE.createImplicitVariable(); - variable.setName(variableName); - return variable; - } - @Override public void discardDerivedState(DerivedStateAwareResource resource) { - Problem problem = getProblem(resource); + var problem = getProblem(resource); if (problem != null) { - Adapter adapter = getOrInstallAdapter(resource); + var adapter = getOrInstallAdapter(resource); discardDerivedProblemState(problem, adapter); } } @@ -245,52 +121,13 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer { problem.getNodes().clear(); for (Statement statement : problem.getStatements()) { if (statement instanceof ClassDeclaration) { - ClassDeclaration classDeclaration = (ClassDeclaration) statement; + var classDeclaration = (ClassDeclaration) statement; classDeclaration.setNewNode(null); classDeclarations.add(classDeclaration); } - if (statement instanceof PredicateDefinition) { - PredicateDefinition definition = (PredicateDefinition) statement; - for (Conjunction body : definition.getBodies()) { - body.getImplicitVariables().clear(); - for (Literal literal : body.getLiterals()) { - if (literal instanceof Atom) { - discardDerivedAtomState((Atom) literal); - } - if (literal instanceof NegativeLiteral) { - NegativeLiteral negativeLiteral = (NegativeLiteral) literal; - negativeLiteral.getImplicitVariables().clear(); - discardDerivedAtomState(negativeLiteral.getAtom()); - } - } - } - } } - adapter.newNodes.keySet().retainAll(classDeclarations); - } - - protected void discardDerivedAtomState(Atom atom) { - if (atom == null) { - return; - } - for (Argument argument : atom.getArguments()) { - if (argument instanceof VariableOrNodeArgument) { - VariableOrNodeArgument variableOrNodeArgument = (VariableOrNodeArgument) argument; - variableOrNodeArgument.setSingletonVariable(null); - } - } - } - - protected static boolean validId(String name) { - return name != null && ID_REGEX.matcher(name).matches(); - } - - protected static boolean validQuotedId(String name) { - return name != null && QUOTED_ID_REGEX.matcher(name).matches(); - } - - protected static boolean validNodeName(String name) { - return validId(name) || validQuotedId(name); + adapter.retainAll(classDeclarations); + derivedVariableComputer.discardDerivedVariables(problem); } protected Adapter getOrInstallAdapter(Resource resource) { @@ -301,7 +138,7 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer { if (!languageName.equals(resourceLanguageName)) { return new Adapter(); } - Adapter adapter = (Adapter) EcoreUtil.getAdapter(resource.eAdapters(), Adapter.class); + var adapter = (Adapter) EcoreUtil.getAdapter(resource.eAdapters(), Adapter.class); if (adapter == null) { adapter = new Adapter(); resource.eAdapters().add(adapter); @@ -310,7 +147,15 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer { } protected static class Adapter extends AdapterImpl { - public Map newNodes = new HashMap<>(); + private Map newNodes = new HashMap<>(); + + public Node createNodeIfAbsent(ClassDeclaration classDeclaration, Function createNode) { + return newNodes.computeIfAbsent(classDeclaration, createNode); + } + + public void retainAll(Collection classDeclarations) { + newNodes.keySet().retainAll(classDeclarations); + } @Override public boolean isAdapterForType(Object type) { diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/resource/ProblemResourceDescriptionStrategy.java b/language/src/main/java/org/eclipse/viatra/solver/language/resource/ProblemResourceDescriptionStrategy.java index 686e54df..da737e3d 100644 --- a/language/src/main/java/org/eclipse/viatra/solver/language/resource/ProblemResourceDescriptionStrategy.java +++ b/language/src/main/java/org/eclipse/viatra/solver/language/resource/ProblemResourceDescriptionStrategy.java @@ -6,6 +6,7 @@ import org.eclipse.viatra.solver.language.model.problem.NamedElement; import org.eclipse.viatra.solver.language.model.problem.Node; import org.eclipse.viatra.solver.language.model.problem.Problem; import org.eclipse.viatra.solver.language.model.problem.Variable; +import org.eclipse.viatra.solver.language.naming.NamingUtil; import org.eclipse.xtext.EcoreUtil2; import org.eclipse.xtext.naming.IQualifiedNameConverter; import org.eclipse.xtext.naming.QualifiedName; @@ -27,61 +28,59 @@ public class ProblemResourceDescriptionStrategy extends DefaultResourceDescripti if (!shouldExport(eObject)) { return false; } - if (!(eObject instanceof NamedElement)) { - return true; - } - NamedElement namedElement = (NamedElement) eObject; - String name = namedElement.getName(); - if (name == null || name.isEmpty()) { + var qualifiedName = getNameAsQualifiedName(eObject); + if (qualifiedName == null) { return true; } - Problem problem = EcoreUtil2.getContainerOfType(namedElement, Problem.class); - QualifiedName problemQualifiedName = null; - if (problem != null) { - String problemName = problem.getName(); - if (problemName != null && !problemName.isEmpty()) { - problemQualifiedName = qualifiedNameConverter.toQualifiedName(problemName); - } - } - QualifiedName qualifiedName = qualifiedNameConverter.toQualifiedName(namedElement.getName()); + var problem = EcoreUtil2.getContainerOfType(eObject, Problem.class); + var problemQualifiedName = getNameAsQualifiedName(problem); boolean nameExported; - if (shouldExportSimpleName(namedElement)) { - acceptEObjectDescription(namedElement, problemQualifiedName, qualifiedName, acceptor); + if (shouldExportSimpleName(eObject)) { + acceptEObjectDescription(eObject, problemQualifiedName, qualifiedName, acceptor); nameExported = true; } else { nameExported = false; } - EObject parent = namedElement.eContainer(); + var parent = eObject.eContainer(); while (parent != null && parent != problem) { - if (parent instanceof NamedElement) { - NamedElement namedParent = (NamedElement) parent; - String parentName = namedParent.getName(); - if (parentName != null || !name.isEmpty()) { - QualifiedName parentQualifiedName = qualifiedNameConverter.toQualifiedName(parentName); - qualifiedName = parentQualifiedName.append(qualifiedName); - if (shouldExportSimpleName(namedParent)) { - acceptEObjectDescription(namedElement, problemQualifiedName, qualifiedName, acceptor); - nameExported = true; - } else { - nameExported = false; - } - } + var parentQualifiedName = getNameAsQualifiedName(parent); + if (parentQualifiedName == null) { + continue; + } + qualifiedName = parentQualifiedName.append(qualifiedName); + if (shouldExportSimpleName(parent)) { + acceptEObjectDescription(eObject, problemQualifiedName, qualifiedName, acceptor); + nameExported = true; + } else { + nameExported = false; } parent = parent.eContainer(); } if (!nameExported) { - acceptEObjectDescription(namedElement, problemQualifiedName, qualifiedName, acceptor); + acceptEObjectDescription(eObject, problemQualifiedName, qualifiedName, acceptor); } return true; } + protected QualifiedName getNameAsQualifiedName(EObject eObject) { + if (!(eObject instanceof NamedElement)) { + return null; + } + var namedElement = (NamedElement) eObject; + var name = namedElement.getName(); + if (NamingUtil.isNullOrEmpty(name)) { + return null; + } + return qualifiedNameConverter.toQualifiedName(name); + } + protected boolean shouldExport(EObject eObject) { if (eObject instanceof Variable) { // Variables are always private to the containing predicate definition. return false; } if (eObject instanceof Node) { - Node node = (Node) eObject; + var node = (Node) eObject; // Only enum literals and new nodes are visible across problem files. return ProblemUtil.isEnumLiteral(node) || ProblemUtil.isNewNode(node); } @@ -97,7 +96,7 @@ public class ProblemResourceDescriptionStrategy extends DefaultResourceDescripti private void acceptEObjectDescription(EObject eObject, QualifiedName prefix, QualifiedName qualifiedName, IAcceptor acceptor) { - QualifiedName qualifiedNameWithPrefix = prefix == null ? qualifiedName : prefix.append(qualifiedName); + var qualifiedNameWithPrefix = prefix == null ? qualifiedName : prefix.append(qualifiedName); acceptor.accept(EObjectDescription.create(qualifiedNameWithPrefix, eObject)); } } diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/scoping/ProblemScopeProvider.java b/language/src/main/java/org/eclipse/viatra/solver/language/scoping/ProblemScopeProvider.java index 597085a8..33f8c50f 100644 --- a/language/src/main/java/org/eclipse/viatra/solver/language/scoping/ProblemScopeProvider.java +++ b/language/src/main/java/org/eclipse/viatra/solver/language/scoping/ProblemScopeProvider.java @@ -4,7 +4,6 @@ package org.eclipse.viatra.solver.language.scoping; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import org.eclipse.emf.ecore.EObject; @@ -16,7 +15,6 @@ import org.eclipse.viatra.solver.language.model.problem.PredicateDefinition; import org.eclipse.viatra.solver.language.model.problem.Problem; import org.eclipse.viatra.solver.language.model.problem.ProblemPackage; import org.eclipse.viatra.solver.language.model.problem.ReferenceDeclaration; -import org.eclipse.viatra.solver.language.model.problem.Relation; import org.eclipse.viatra.solver.language.model.problem.Variable; import org.eclipse.viatra.solver.language.model.problem.VariableOrNodeArgument; import org.eclipse.xtext.EcoreUtil2; @@ -34,7 +32,7 @@ public class ProblemScopeProvider extends AbstractProblemScopeProvider { @Override public IScope getScope(EObject context, EReference reference) { - IScope scope = super.getScope(context, reference); + var scope = super.getScope(context, reference); if (reference == ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE || reference == ProblemPackage.Literals.NODE_VALUE_ASSERTION__NODE) { return getNodesScope(context, scope); @@ -49,7 +47,7 @@ public class ProblemScopeProvider extends AbstractProblemScopeProvider { } protected IScope getNodesScope(EObject context, IScope delegateScope) { - Problem problem = EcoreUtil2.getContainerOfType(context, Problem.class); + var problem = EcoreUtil2.getContainerOfType(context, Problem.class); if (problem == null) { return delegateScope; } @@ -73,7 +71,7 @@ public class ProblemScopeProvider extends AbstractProblemScopeProvider { } currentContext = currentContext.eContainer(); } - if (currentContext instanceof PredicateDefinition) { + if (currentContext != null) { PredicateDefinition definition = (PredicateDefinition) currentContext; variables.addAll(definition.getParameters()); } @@ -81,17 +79,16 @@ public class ProblemScopeProvider extends AbstractProblemScopeProvider { } protected IScope getOppositeScope(EObject context, IScope delegateScope) { - ReferenceDeclaration referenceDeclaration = EcoreUtil2.getContainerOfType(context, ReferenceDeclaration.class); + var referenceDeclaration = EcoreUtil2.getContainerOfType(context, ReferenceDeclaration.class); if (referenceDeclaration == null) { return delegateScope; } - Relation relation = referenceDeclaration.getReferenceType(); + var relation = referenceDeclaration.getReferenceType(); if (!(relation instanceof ClassDeclaration)) { return delegateScope; } - ClassDeclaration classDeclaration = (ClassDeclaration) relation; - Collection referenceDeclarations = ProblemUtil - .getAllReferenceDeclarations(classDeclaration); + var classDeclaration = (ClassDeclaration) relation; + var referenceDeclarations = ProblemUtil.getAllReferenceDeclarations(classDeclaration); return Scopes.scopeFor(referenceDeclarations, delegateScope); } } diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/validation/ProblemValidator.java b/language/src/main/java/org/eclipse/viatra/solver/language/validation/ProblemValidator.java index 2b17e222..96b656a3 100644 --- a/language/src/main/java/org/eclipse/viatra/solver/language/validation/ProblemValidator.java +++ b/language/src/main/java/org/eclipse/viatra/solver/language/validation/ProblemValidator.java @@ -3,23 +3,11 @@ */ package org.eclipse.viatra.solver.language.validation; - /** - * This class contains custom validation rules. + * This class contains custom validation rules. * - * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#validation + * See + * https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#validation */ public class ProblemValidator extends AbstractProblemValidator { - -// public static final String INVALID_NAME = "invalidName"; -// -// @Check -// public void checkGreetingStartsWithCapital(Greeting greeting) { -// if (!Character.isUpperCase(greeting.getName().charAt(0))) { -// warning("Name should start with a capital", -// ProblemPackage.Literals.GREETING__NAME, -// INVALID_NAME); -// } -// } - } -- cgit v1.2.3-54-g00ecf