/* * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors * * SPDX-License-Identifier: EPL-2.0 */ package tools.refinery.language.resource.state; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; import com.google.inject.name.Named; 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.xtext.Constants; import org.eclipse.xtext.resource.DerivedStateAwareResource; import org.eclipse.xtext.resource.IDerivedStateComputer; import org.eclipse.xtext.resource.XtextResource; import tools.refinery.language.model.problem.*; import tools.refinery.language.utils.ProblemUtil; import java.util.*; import java.util.function.Function; @Singleton public class ProblemDerivedStateComputer implements IDerivedStateComputer { public static final String NEW_NODE = "new"; @Inject @Named(Constants.LANGUAGE_NAME) private String languageName; @Inject private Provider nodeNameCollectorProvider; @Inject private DerivedVariableComputer derivedVariableComputer; @Override public void installDerivedState(DerivedStateAwareResource resource, boolean preLinkingPhase) { var problem = getProblem(resource); if (problem != null) { var adapter = getOrInstallAdapter(resource); installDerivedProblemState(problem, adapter, preLinkingPhase); } } protected Problem getProblem(Resource resource) { List contents = resource.getContents(); if (contents.isEmpty()) { return null; } EObject object = contents.get(0); if (object instanceof Problem problem) { return problem; } return null; } protected void installDerivedProblemState(Problem problem, Adapter adapter, boolean preLinkingPhase) { installDerivedClassDeclarationState(problem, adapter); if (preLinkingPhase) { return; } Set nodeNames = installDerivedNodes(problem); derivedVariableComputer.installDerivedVariables(problem, nodeNames); } protected void installDerivedClassDeclarationState(Problem problem, Adapter adapter) { for (var statement : problem.getStatements()) { if (statement instanceof ClassDeclaration classDeclaration) { installOrRemoveNewNode(adapter, classDeclaration); for (var featureDeclaration : classDeclaration.getFeatureDeclarations()) { if (featureDeclaration instanceof ReferenceDeclaration referenceDeclaration) { installOrRemoveInvalidMultiplicityPredicate(adapter, classDeclaration, referenceDeclaration); } } } } } protected void installOrRemoveNewNode(Adapter adapter, ClassDeclaration declaration) { if (declaration.isAbstract()) { var newNode = declaration.getNewNode(); if (newNode != null) { declaration.setNewNode(null); adapter.removeNewNode(declaration); } } else { if (declaration.getNewNode() == null) { var newNode = adapter.createNewNodeIfAbsent(declaration, key -> createNode(NEW_NODE)); declaration.setNewNode(newNode); } } } protected void installOrRemoveInvalidMultiplicityPredicate( Adapter adapter, ClassDeclaration containingClassDeclaration, ReferenceDeclaration declaration) { if (ProblemUtil.hasMultiplicityConstraint(declaration)) { if (declaration.getInvalidMultiplicity() == null) { var invalidMultiplicity = adapter.createInvalidMultiplicityPredicateIfAbsent(declaration, key -> { var predicate = ProblemFactory.eINSTANCE.createPredicateDefinition(); predicate.setError(true); predicate.setName("invalidMultiplicity"); var parameter = ProblemFactory.eINSTANCE.createParameter(); parameter.setParameterType(containingClassDeclaration); parameter.setName("node"); predicate.getParameters().add(parameter); return predicate; }); declaration.setInvalidMultiplicity(invalidMultiplicity); } } else { var invalidMultiplicity = declaration.getInvalidMultiplicity(); if (invalidMultiplicity != null) { declaration.setInvalidMultiplicity(null); adapter.removeInvalidMultiplicityPredicate(declaration); } } } protected Set installDerivedNodes(Problem problem) { var collector = nodeNameCollectorProvider.get(); collector.collectNodeNames(problem); Set nodeNames = collector.getNodeNames(); List graphNodes = problem.getNodes(); for (String nodeName : nodeNames) { var graphNode = createNode(nodeName); graphNodes.add(graphNode); } return nodeNames; } protected Node createNode(String name) { var node = ProblemFactory.eINSTANCE.createNode(); node.setName(name); return node; } @Override public void discardDerivedState(DerivedStateAwareResource resource) { var problem = getProblem(resource); if (problem != null) { var adapter = getOrInstallAdapter(resource); discardDerivedProblemState(problem, adapter); } } protected void discardDerivedProblemState(Problem problem, Adapter adapter) { var abstractClassDeclarations = new HashSet(); var referenceDeclarationsWithMultiplicity = new HashSet(); problem.getNodes().clear(); for (var statement : problem.getStatements()) { if (statement instanceof ClassDeclaration classDeclaration) { classDeclaration.setNewNode(null); if (classDeclaration.isAbstract()) { abstractClassDeclarations.add(classDeclaration); } for (var featureDeclaration : classDeclaration.getFeatureDeclarations()) { if (featureDeclaration instanceof ReferenceDeclaration referenceDeclaration && ProblemUtil.hasMultiplicityConstraint(referenceDeclaration)) { referenceDeclarationsWithMultiplicity.add(referenceDeclaration); } } } } adapter.retainAll(abstractClassDeclarations, referenceDeclarationsWithMultiplicity); derivedVariableComputer.discardDerivedVariables(problem); } protected Adapter getOrInstallAdapter(Resource resource) { if (!(resource instanceof XtextResource)) { return new Adapter(); } String resourceLanguageName = ((XtextResource) resource).getLanguageName(); if (!languageName.equals(resourceLanguageName)) { return new Adapter(); } var adapter = (Adapter) EcoreUtil.getAdapter(resource.eAdapters(), Adapter.class); if (adapter == null) { adapter = new Adapter(); resource.eAdapters().add(adapter); } return adapter; } protected static class Adapter extends AdapterImpl { private final Map newNodes = new HashMap<>(); private final Map invalidMultiplicityPredicates = new HashMap<>(); public Node createNewNodeIfAbsent(ClassDeclaration classDeclaration, Function createNode) { return newNodes.computeIfAbsent(classDeclaration, createNode); } public void removeNewNode(ClassDeclaration classDeclaration) { newNodes.remove(classDeclaration); } public PredicateDefinition createInvalidMultiplicityPredicateIfAbsent( ReferenceDeclaration referenceDeclaration, Function createPredicate) { return invalidMultiplicityPredicates.computeIfAbsent(referenceDeclaration, createPredicate); } public void removeInvalidMultiplicityPredicate(ReferenceDeclaration referenceDeclaration) { invalidMultiplicityPredicates.remove(referenceDeclaration); } public void retainAll(Collection abstractClassDeclarations, Collection referenceDeclarationsWithMultiplicity) { newNodes.keySet().retainAll(abstractClassDeclarations); invalidMultiplicityPredicates.keySet().retainAll(referenceDeclarationsWithMultiplicity); } @Override public boolean isAdapterForType(Object type) { return Adapter.class == type; } } }