/*
* SPDX-FileCopyrightText: 2021-2024 The Refinery Authors
*
* SPDX-License-Identifier: EPL-2.0
*/
package tools.refinery.language.ide.syntaxcoloring;
import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
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.jetbrains.annotations.NotNull;
import tools.refinery.language.model.problem.*;
import tools.refinery.language.utils.ProblemDesugarer;
import tools.refinery.language.utils.ProblemUtil;
import java.util.List;
public class ProblemSemanticHighlightingCalculator extends DefaultSemanticHighlightingCalculator {
private static final String BUILTIN_CLASS = "builtin";
private static final String ABSTRACT_CLASS = "abstract";
private static final String CONTAINMENT_CLASS = "containment";
private static final String ERROR_CLASS = "error";
private static final String NODE_CLASS = "node";
private static final String INDIVIDUAL_NODE_CLASS = "individual";
private static final String NEW_NODE_CLASS = "new";
@Inject
private OperationCanceledManager operationCanceledManager;
@Inject
private ProblemDesugarer desugarer;
@Inject
private TypeHashProvider typeHashProvider;
@Override
protected boolean highlightElement(EObject object, IHighlightedPositionAcceptor acceptor,
CancelIndicator cancelIndicator) {
highlightName(object, acceptor);
highlightCrossReferences(object, acceptor, cancelIndicator);
return false;
}
protected void highlightName(EObject object, IHighlightedPositionAcceptor acceptor) {
if (!(object instanceof NamedElement)) {
return;
}
String[] highlightClass = getHighlightClass(object, null);
if (highlightClass.length > 0) {
highlightFeature(acceptor, object, ProblemPackage.Literals.NAMED_ELEMENT__NAME, highlightClass);
}
}
protected void highlightCrossReferences(EObject object, IHighlightedPositionAcceptor acceptor,
CancelIndicator cancelIndicator) {
for (EReference reference : object.eClass().getEAllReferences()) {
if (reference.isContainment()) {
continue;
}
operationCanceledManager.checkCanceled(cancelIndicator);
if (reference.isMany()) {
highlightManyValues(object, reference, acceptor);
} else {
highlightSingleValue(object, reference, acceptor);
}
}
}
protected void highlightSingleValue(EObject object, EReference reference, IHighlightedPositionAcceptor acceptor) {
EObject valueObj = (EObject) object.eGet(reference);
String[] highlightClass = getHighlightClass(valueObj, reference);
if (highlightClass.length > 0) {
highlightFeature(acceptor, object, reference, highlightClass);
}
}
protected void highlightManyValues(EObject object, EReference reference, IHighlightedPositionAcceptor acceptor) {
@SuppressWarnings("unchecked")
EList extends EObject> values = (EList extends EObject>) 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, reference);
if (highlightClass.length > 0) {
highlightNode(acceptor, node, highlightClass);
}
}
}
protected String[] getHighlightClass(EObject eObject, EReference reference) {
boolean isError = ProblemUtil.isError(eObject);
if (ProblemUtil.isBuiltIn(eObject)) {
var className = isError ? ERROR_CLASS : BUILTIN_CLASS;
return new String[] { className };
}
return getUserDefinedElementHighlightClass(eObject, reference, isError);
}
@NotNull
private String[] getUserDefinedElementHighlightClass(EObject eObject, EReference reference, boolean isError) {
ImmutableList.Builder classesBuilder = ImmutableList.builder();
if (eObject instanceof ClassDeclaration classDeclaration && classDeclaration.isAbstract()) {
classesBuilder.add(ABSTRACT_CLASS);
}
if (eObject instanceof ReferenceDeclaration referenceDeclaration
&& desugarer.isContainmentReference(referenceDeclaration)) {
classesBuilder.add(CONTAINMENT_CLASS);
}
if (isError) {
classesBuilder.add(ERROR_CLASS);
}
if (eObject instanceof Node node) {
if (reference == ProblemPackage.Literals.VARIABLE_OR_NODE_EXPR__VARIABLE_OR_NODE) {
classesBuilder.add(NODE_CLASS);
}
if (ProblemUtil.isIndividualNode(node)) {
classesBuilder.add(INDIVIDUAL_NODE_CLASS);
}
if (ProblemUtil.isNewNode(node)) {
classesBuilder.add(NEW_NODE_CLASS);
}
}
if (eObject instanceof Relation relation) {
var typeHash = typeHashProvider.getTypeHash(relation);
if (typeHash != null) {
classesBuilder.add("typeHash-" + typeHash);
}
}
List classes = classesBuilder.build();
return classes.toArray(new String[0]);
}
}