From dc1f72e207a21ef217404466ff5c4a123809addc Mon Sep 17 00:00:00 2001 From: Axel RICHARD Date: Wed, 29 Oct 2025 17:22:47 +0100 Subject: [PATCH] [enh] Migrate label services to new services organization Bug: https://github.com/eclipse-syson/syson/issues/1628 Signed-off-by: Axel RICHARD --- CHANGELOG.adoc | 25 +- .../services/syson-diagram-services/pom.xml | 5 + .../services/DiagramMutationLabelService.java | 146 ++++ .../services/DiagramQueryLabelService.java | 759 ++++++++++++++++++ .../services/api/IDiagramLabelService.java | 94 +++ .../aql/DiagramMutationAQLService.java | 47 +- .../services/aql/DiagramQueryAQLService.java | 108 ++- .../services/utils}/MultiLineLabelSwitch.java | 7 +- .../DiagramQueryLabelServiceTest.java} | 155 +++- .../utils}/MultilineLabelSwitchTest.java | 9 +- .../services/aql/ModelQueryAQLService.java | 8 +- .../eclipse/syson/services/LabelService.java | 632 --------------- .../syson/services/LabelServiceTest.java | 148 ---- ...java => MetamodelElementQueryService.java} | 2 +- .../views/syson-diagram-common-view/pom.xml | 5 + ...nnectorAsUsageEdgeDescriptionProvider.java | 7 +- ...tionOwnedUsageEdgeDescriptionProvider.java | 10 +- ...ractDependencyEdgeDescriptionProvider.java | 7 +- ...tractFlowUsageEdgeDescriptionProvider.java | 9 +- ...InterfaceUsageEdgeDescriptionProvider.java | 9 +- ...ractSuccessionEdgeDescriptionProvider.java | 7 +- ...ageNestedUsageEdgeDescriptionProvider.java | 8 +- ...trolNodeActionNodeDescriptionProvider.java | 7 +- ...ractDefinitionNodeDescriptionProvider.java | 9 +- ...temUsageBorderNodeDescriptionProvider.java | 7 +- ...bstractPackageNodeDescriptionProvider.java | 9 +- .../AbstractUsageNodeDescriptionProvider.java | 9 +- .../AnnotatingNodeDescriptionProvider.java | 9 +- ...ompartmentItemNodeDescriptionProvider.java | 11 +- ...mportedPackageNodeDescriptionProvider.java | 4 +- .../view/services/ViewLabelService.java | 309 +------ .../common/view/services/ViewNodeService.java | 6 +- .../nodes/ActorNodeDescriptionProvider.java | 3 +- ...ortUsageBorderNodeDescriptionProvider.java | 7 +- ...nceUsageBorderNodeDescriptionProvider.java | 7 +- .../ViewUsageNodeDescriptionProvider.java | 9 +- scripts/check-coverage.jsh | 8 +- 37 files changed, 1450 insertions(+), 1171 deletions(-) create mode 100644 backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationLabelService.java create mode 100644 backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramQueryLabelService.java create mode 100644 backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/api/IDiagramLabelService.java rename backend/services/{syson-services/src/main/java/org/eclipse/syson/services => syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/utils}/MultiLineLabelSwitch.java (99%) rename backend/{views/syson-diagram-common-view/src/test/java/org/eclipse/syson/diagram/common/view/services/ViewLabelServiceTest.java => services/syson-diagram-services/src/test/java/org/eclipse/syson/diagram/services/DiagramQueryLabelServiceTest.java} (72%) rename backend/services/{syson-services/src/test/java/org/eclipse/syson/services => syson-diagram-services/src/test/java/org/eclipse/syson/diagram/services/utils}/MultilineLabelSwitchTest.java (98%) delete mode 100644 backend/services/syson-services/src/test/java/org/eclipse/syson/services/LabelServiceTest.java rename backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/{ElementQueryService.java => MetamodelElementQueryService.java} (97%) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index ffd49aef0..7ed118305 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -15,7 +15,30 @@ - https://github.com/eclipse-syson/syson/issues/1628[#1628] [services] Introduce new services organization. https://doc.mbse-syson.org/syson/main/developer-guide/index.html#services_organization[See the developer guide in the documentation for more details]. Also introduces new `ServiceMethod` helper class to build AQL service call expressions from type-safe Java method references instead of hardcoded strings. -- [services] _isActor(Element)_ has been moved from `ViewNodeService` to `ElementQueryService`. +- [services] _isActor(Element)_ has been moved from `ViewNodeService` to `MetamodelElementQueryService`. +- [services] The following services have been moved from `LabelService` and `ViewLabelService` to `DiagramQueryLabelService`: +* String getBorderNodeUsageLabel(Usage usage) +* String getCompartmentItemLabel(Documentation documentation) +* String getCompartmentItemLabel(Usage usage) +* String getContainerLabel(Element element) +* String getDefaultInitialDirectEditLabel(Comment comment) +* String getDefaultInitialDirectEditLabel(Element element) +* String getDefaultInitialDirectEditLabel(TextualRepresentation textualRepresentation) +* String getDependencyLabel(Dependency dependency) +* String getEdgeLabel(Element element) +* String getInitialDirectEditListItemLabel(Documentation documentation) +* String getInitialDirectEditListItemLabel(Comment comment) +* String getInitialDirectEditListItemLabel(Usage usage) +* String getMultiplicityLabel(Element element) +* String getTransitionLabel(TransitionUsage transition) +- [services] The following services have been moved from `LabelService` and `ViewLabelService` to `DiagramMutationLabelService`: +* Element directEdit(Element element, String newLabel) +* Element directEdit(Element element, String newLabel, String... options) +* Element directEdit(Element element, String newLabel, boolean isCompartmentItem, String... options) +* Element directEditNode(Element element, String newLabel) +* Element directEditListItem(Element element, String newLabel) +* Element editMultiplicityRangeCenterLabel(Element element, String newLabel) +* Element editEdgeCenterLabel(Element element, String newLabel) === Dependency update diff --git a/backend/services/syson-diagram-services/pom.xml b/backend/services/syson-diagram-services/pom.xml index 7aeeeeb49..ae5af7e59 100644 --- a/backend/services/syson-diagram-services/pom.xml +++ b/backend/services/syson-diagram-services/pom.xml @@ -62,6 +62,11 @@ syson-sysml-metamodel 2025.10.1 + + org.eclipse.syson + syson-services + 2025.10.1 + org.testcontainers diff --git a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationLabelService.java b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationLabelService.java new file mode 100644 index 000000000..2b21cce2e --- /dev/null +++ b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationLabelService.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright (c) 2025 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.diagram.services; + +import java.text.MessageFormat; +import java.util.Objects; + +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.ParseTreeWalker; +import org.eclipse.sirius.components.core.api.IFeedbackMessageService; +import org.eclipse.sirius.components.representations.Message; +import org.eclipse.sirius.components.representations.MessageLevel; +import org.eclipse.syson.direct.edit.grammars.DirectEditLexer; +import org.eclipse.syson.direct.edit.grammars.DirectEditParser; +import org.eclipse.syson.services.DiagramDirectEditListener; +import org.eclipse.syson.services.LabelService; +import org.eclipse.syson.sysml.ConstraintUsage; +import org.eclipse.syson.sysml.Element; +import org.eclipse.syson.sysml.RequirementConstraintMembership; +import org.springframework.stereotype.Service; + +/** + * Label-related services doing mutations in diagrams. + * + * @author arichard + */ +@Service +public class DiagramMutationLabelService { + + private final IFeedbackMessageService feedbackMessageService; + + public DiagramMutationLabelService(IFeedbackMessageService feedbackMessageService) { + this.feedbackMessageService = Objects.requireNonNull(feedbackMessageService); + } + + /** + * Apply the direct edit result (i.e. the newLabel) to the given {@link Element}. + * + * @param element + * the given {@link Element}. + * @param newLabel + * the new value to apply. + * @return the given {@link Element}. + */ + public Element directEdit(Element element, String newLabel) { + return this.directEdit(element, newLabel, (String[]) null); + } + + /** + * Apply the direct edit result (i.e. the newLabel) to the given {@link Element}, with the provided {@code options}. + *

+ * This method is typically used to enable direct edit with some restrictions (e.g. de-activate the ability to edit + * the name of an element via direct edit). See {@link #directEdit(Element, String)} to perform a direct edit with + * default options. + *

+ * + * @param element + * the given {@link Element} + * @param newLabel + * the new value to apply + * @param options + * the options of the direct edit + * @return the given {@link Element} + */ + public Element directEdit(Element element, String newLabel, String... options) { + return this.directEdit(element, newLabel, false, options); + } + + /** + * Apply the direct edit result (i.e. the newLabel) to the given {@link Element}. + * + * @param element + * the given {@link Element}. + * @param newLabel + * the new value to apply. + * @return the given {@link Element}. + */ + public Element directEdit(Element element, String newLabel, boolean isCompartmentItem, String... options) { + DirectEditLexer lexer = new DirectEditLexer(CharStreams.fromString(newLabel)); + CommonTokenStream tokens = new CommonTokenStream(lexer); + DirectEditParser parser = new DirectEditParser(tokens); + ParseTree tree; + if (element instanceof ConstraintUsage && element.getOwningMembership() instanceof RequirementConstraintMembership && isCompartmentItem) { + // Use the constraint expression parser only if the element is a constraint owned by a requirement, other + // constraints (including requirements) are parsed as regular elements. + tree = parser.constraintExpression(); + } else if (isCompartmentItem) { + tree = parser.listItemExpression(); + } else { + tree = parser.nodeExpression(); + } + ParseTreeWalker walker = new ParseTreeWalker(); + DiagramDirectEditListener listener = new DiagramDirectEditListener(element, this.feedbackMessageService, options); + walker.walk(listener, tree); + listener.resolveProxies().forEach(proxy -> { + this.feedbackMessageService.addFeedbackMessage(new Message(MessageFormat.format("Unable to resolve \u2035{0}\u2035", proxy.nameToResolve()), MessageLevel.WARNING)); + }); + return element; + } + + /** + * Apply the direct edit result (i.e. the newLabel) to the given graphical node {@link Element}. + * + * @param element + * the given {@link Element}. + * @param newLabel + * the new value to apply. + * @return the given {@link Element}. + */ + public Element directEditNode(Element element, String newLabel) { + return this.directEdit(element, newLabel, false, (String[]) null); + } + + /** + * Apply the direct edit result (i.e. the newLabel) to the given graphical list item {@link Element}. + * + * @param element + * the given {@link Element}. + * @param newLabel + * the new value to apply. + * @return the given {@link Element}. + */ + public Element directEditListItem(Element element, String newLabel) { + return this.directEdit(element, newLabel, true, (String[]) null); + } + + public Element editMultiplicityRangeCenterLabel(Element element, String newLabel) { + return this.directEdit(element, newLabel, LabelService.NAME_OFF, LabelService.REDEFINITION_OFF, LabelService.SUBSETTING_OFF, LabelService.TYPING_OFF, LabelService.VALUE_OFF); + } + + public Element editEdgeCenterLabel(Element element, String newLabel) { + return this.directEdit(element, newLabel, LabelService.REDEFINITION_OFF, LabelService.SUBSETTING_OFF, LabelService.VALUE_OFF); + } +} diff --git a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramQueryLabelService.java b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramQueryLabelService.java new file mode 100644 index 000000000..37ab0ef47 --- /dev/null +++ b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramQueryLabelService.java @@ -0,0 +1,759 @@ +/******************************************************************************* + * Copyright (c) 2025 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.diagram.services; + +import static java.util.stream.Collectors.joining; + +import java.util.Objects; +import java.util.function.BinaryOperator; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.syson.diagram.services.api.IDiagramLabelService; +import org.eclipse.syson.diagram.services.utils.MultiLineLabelSwitch; +import org.eclipse.syson.services.SimpleNameDeresolver; +import org.eclipse.syson.sysml.AcceptActionUsage; +import org.eclipse.syson.sysml.ActionUsage; +import org.eclipse.syson.sysml.AttributeUsage; +import org.eclipse.syson.sysml.Comment; +import org.eclipse.syson.sysml.ConstraintUsage; +import org.eclipse.syson.sysml.Definition; +import org.eclipse.syson.sysml.Dependency; +import org.eclipse.syson.sysml.Documentation; +import org.eclipse.syson.sysml.Element; +import org.eclipse.syson.sysml.Expression; +import org.eclipse.syson.sysml.Feature; +import org.eclipse.syson.sysml.FeatureDirectionKind; +import org.eclipse.syson.sysml.FeatureTyping; +import org.eclipse.syson.sysml.FeatureValue; +import org.eclipse.syson.sysml.LiteralExpression; +import org.eclipse.syson.sysml.MultiplicityRange; +import org.eclipse.syson.sysml.OwningMembership; +import org.eclipse.syson.sysml.Redefinition; +import org.eclipse.syson.sysml.RequirementConstraintMembership; +import org.eclipse.syson.sysml.StateSubactionMembership; +import org.eclipse.syson.sysml.Subclassification; +import org.eclipse.syson.sysml.Subsetting; +import org.eclipse.syson.sysml.TextualRepresentation; +import org.eclipse.syson.sysml.TransitionUsage; +import org.eclipse.syson.sysml.Type; +import org.eclipse.syson.sysml.Usage; +import org.eclipse.syson.sysml.VariantMembership; +import org.eclipse.syson.sysml.helper.LabelConstants; +import org.eclipse.syson.sysml.textual.SysMLElementSerializer; +import org.eclipse.syson.sysml.textual.utils.Appender; +import org.eclipse.syson.sysml.textual.utils.FileNameDeresolver; +import org.eclipse.syson.sysml.textual.utils.INameDeresolver; +import org.eclipse.syson.sysml.util.ElementUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +/** + * Label-related services doing queries in diagrams. + * + * @author arichard + */ +@Service +public class DiagramQueryLabelService implements IDiagramLabelService { + + /** + * The default separator used when printing a set of Trigger Actions for a TransitionUsage label + */ + private static final String TRIGGER_ACTION_SEPARATOR = " | "; + + /** + * The default separator used when printing a set of Guard Expressions for a TransitionUsage label + */ + private static final String GUARD_EXPRESSION_SEPARATOR = "& "; + + /** + * The default separator used when printing a set of Effect Action for a TransitionUsage label + */ + private static final String EFFECT_ACTION_SEPARATOR = ", "; + + private final Logger logger = LoggerFactory.getLogger(DiagramQueryLabelService.class); + + @Override + public String getIdentificationLabel(Element element) { + StringBuilder label = new StringBuilder(); + if (element instanceof ActionUsage && element.eContainer() instanceof StateSubactionMembership ssm) { + label.append(ssm.getKind()); + } + label.append(this.getShortNameLabel(element)); + String declaredName = element.getDeclaredName(); + if (declaredName != null) { + label.append(declaredName); + } + return label.toString(); + } + + @Override + public String getRedefinitionLabel(Element element) { + StringBuilder label = new StringBuilder(); + var optRedefinition = element.getOwnedRelationship().stream() + .filter(Redefinition.class::isInstance) + .map(Redefinition.class::cast) + .findFirst(); + if (optRedefinition.isPresent()) { + Redefinition redefinition = optRedefinition.get(); + if (!redefinition.isIsImplied()) { + var redefinedFeature = redefinition.getRedefinedFeature(); + if (redefinedFeature != null) { + label.append(LabelConstants.SPACE); + label.append(LabelConstants.REDEFINITION); + label.append(LabelConstants.SPACE); + label.append(this.getDeclaredNameLabel(redefinedFeature)); + } + } + } + return label.toString(); + } + + @Override + public String getSubclassificationLabel(Element element) { + StringBuilder label = new StringBuilder(); + var optSubclassification = element.getOwnedRelationship().stream() + .filter(Subclassification.class::isInstance) + .map(Subclassification.class::cast) + .findFirst(); + if (optSubclassification.isPresent()) { + Subclassification subclassification = optSubclassification.get(); + if (!subclassification.isIsImplied()) { + var superclassifier = subclassification.getSuperclassifier(); + if (superclassifier != null) { + label.append(LabelConstants.SPACE); + label.append(LabelConstants.SUBCLASSIFICATION); + label.append(LabelConstants.SPACE); + label.append(this.getDeclaredNameLabel(superclassifier)); + } + } + } + return label.toString(); + } + + @Override + public String getSubsettingLabel(Element element) { + StringBuilder label = new StringBuilder(); + var optSubsetting = element.getOwnedRelationship().stream() + .filter(r -> r instanceof Subsetting && !(r instanceof Redefinition)) + .map(Subsetting.class::cast) + .findFirst(); + if (optSubsetting.isPresent()) { + Subsetting subsetting = optSubsetting.get(); + if (!subsetting.isIsImplied()) { + var subsettedFeature = subsetting.getSubsettedFeature(); + if (subsettedFeature != null) { + label.append(LabelConstants.SPACE); + label.append(LabelConstants.SUBSETTING); + label.append(LabelConstants.SPACE); + label.append(this.getDeclaredNameLabel(subsettedFeature)); + } + } + } + return label.toString(); + } + + @Override + public String getTypingLabel(Element element) { + StringBuilder label = new StringBuilder(); + var optFeatureTyping = element.getOwnedRelationship().stream() + .filter(FeatureTyping.class::isInstance) + .map(FeatureTyping.class::cast) + .findFirst(); + if (optFeatureTyping.isPresent()) { + FeatureTyping featureTyping = optFeatureTyping.get(); + if (!featureTyping.isIsImplied()) { + var type = featureTyping.getType(); + if (type != null) { + label.append(LabelConstants.SPACE); + label.append(LabelConstants.COLON); + label.append(LabelConstants.SPACE); + label.append(this.getDeclaredNameLabel(type)); + } + } + } + return label.toString(); + } + + @Override + public String getValueLabel(Usage usage) { + return this.getValueStringRepresentation(usage, false); + } + + @Override + public String getSysmlTextualRepresentation(Element element, boolean resolvableName) { + return this.buildSerializer(resolvableName).doSwitch(element); + } + + /** + * Return the container label for the given {@link Element}. + * + * @param element + * the given {@link Element}. + * @return the container label for the given {@link Element}. + */ + public String getContainerLabel(Element element) { + return new MultiLineLabelSwitch(this).doSwitch(element); + } + + /** + * Return the label of the multiplicity part of the given {@link Element}. + * + * @param element + * the given {@link Element}. + * @return the label of the multiplicity part of the given {@link Element} if there is one, an empty string + * otherwise. + */ + public String getMultiplicityLabel(Element element) { + return this.getMultiplicityStringRepresentation(element, false); + } + + /** + * Return the label of the multiplicity part of the given {@link Element}. + * + * @param element + * element the given {@link Element}. + * @param directEditInput + * holds true if the label is used as a direct edit input + * @return the label of the multiplicity part of the given {@link Element} if there is one, an empty string + * otherwise. + */ + private String getMultiplicityStringRepresentation(Element element, boolean directEditInput) { + StringBuilder label = new StringBuilder(); + var optMultiplicityRange = element.getOwnedRelationship().stream() + .filter(OwningMembership.class::isInstance) + .map(OwningMembership.class::cast) + .flatMap(m -> m.getOwnedRelatedElement().stream()) + .filter(MultiplicityRange.class::isInstance) + .map(MultiplicityRange.class::cast) + .findFirst(); + label.append(LabelConstants.SPACE); + if (optMultiplicityRange.isPresent()) { + var range = optMultiplicityRange.get(); + String firstBound = null; + String secondBound = null; + var bounds = range.getOwnedRelationship().stream() + .filter(OwningMembership.class::isInstance) + .map(OwningMembership.class::cast) + .flatMap(m -> m.getOwnedRelatedElement().stream()) + .filter(LiteralExpression.class::isInstance) + .map(LiteralExpression.class::cast) + .toList(); + if (bounds.size() == 1) { + firstBound = this.getSysmlTextualRepresentation(bounds.get(0), directEditInput); + } else if (bounds.size() == 2) { + firstBound = this.getSysmlTextualRepresentation(bounds.get(0), directEditInput); + secondBound = this.getSysmlTextualRepresentation(bounds.get(1), directEditInput); + } + label.append(LabelConstants.OPEN_BRACKET); + if (firstBound != null) { + label.append(firstBound); + } + if (secondBound != null) { + label.append(".."); + label.append(secondBound); + } + label.append(LabelConstants.CLOSE_BRACKET + LabelConstants.SPACE); + } + if (element instanceof Feature feature) { + if (feature.isIsOrdered()) { + label.append(LabelConstants.ORDERED + LabelConstants.SPACE); + } + if (!feature.isIsUnique()) { + label.append(LabelConstants.NON_UNIQUE + LabelConstants.SPACE); + } + } + String labelAsString = label.toString().trim(); + if (!labelAsString.isEmpty()) { + return LabelConstants.SPACE + labelAsString; + } else { + return labelAsString; + } + } + + /** + * Get the value to display when a direct edit has been called on the given {@link Element}. + * + * @param element + * the given {@link Element}. + * @return the value to display. + */ + public String getDefaultInitialDirectEditLabel(Element element) { + StringBuilder builder = new StringBuilder(); + if (element instanceof Usage usage) { + builder.append(this.getBasicNamePrefix(usage)); + } + builder.append(this.getIdentificationLabel(element)); + builder.append(this.getMultiplicityStringRepresentation(element, true)); + builder.append(this.getTypingLabel(element)); + builder.append(this.getRedefinitionLabel(element)); + builder.append(this.getSubsettingLabel(element)); + builder.append(this.getSubclassificationLabel(element)); + if (element instanceof Usage usage) { + builder.append(this.getValueStringRepresentation(usage, true)); + } + return builder.toString(); + } + + /** + * Return the label of the value part of the given {@link Usage}. + * + * @param usage + * the given {@link Usage}. + * @param directEditInput + * if the label is being used as direct edit input + * @return the label of the value part of the given {@link Usage} if there is one, an empty string otherwise. + */ + private String getValueStringRepresentation(Usage usage, boolean directEditInput) { + StringBuilder label = new StringBuilder(); + var featureValue = usage.getOwnedRelationship().stream() + .filter(FeatureValue.class::isInstance) + .map(FeatureValue.class::cast) + .findFirst(); + if (featureValue.isPresent()) { + var expression = featureValue.get().getValue(); + String valueAsString = null; + if (expression != null) { + valueAsString = this.getSysmlTextualRepresentation(expression, directEditInput); + } + + if (featureValue.get().isIsDefault()) { + label + .append(LabelConstants.SPACE) + .append(LabelConstants.DEFAULT); + } + label + .append(LabelConstants.SPACE) + .append(this.getFeatureValueRelationshipSymbol(featureValue.get())) + .append(LabelConstants.SPACE) + .append(valueAsString); + } + return label.toString(); + } + + /** + * Return the label of the prefix part of the given {@link Usage}. + * + * @param usage + * the given {@link Usage}. + * @return the label of the prefix part of the given {@link Usage} if there is one, an empty string otherwise. + */ + private String getBasicNamePrefix(Element element) { + StringBuilder label = new StringBuilder(); + if (element instanceof Usage usage) { + if (usage.isIsVariation()) { + label.append(LabelConstants.VARIATION + LabelConstants.SPACE); + } + } else if (element instanceof Definition definition) { + if (definition.isIsVariation()) { + label.append(LabelConstants.VARIATION + LabelConstants.SPACE); + } + } + EObject membership = element.getOwningMembership(); + if (membership != null) { + EObject parent = membership.eContainer(); + boolean hasVariationParent = (parent instanceof Definition && ((Definition) parent).isIsVariation()) | (parent instanceof Usage && ((Usage) parent).isIsVariation()); + if (membership instanceof VariantMembership | hasVariationParent) { + label.append(LabelConstants.VARIANT); + } + } + if (element instanceof Type type && type.isIsAbstract()) { + label.append(LabelConstants.ABSTRACT + LabelConstants.SPACE); + } + if (element instanceof Usage usage) { + this.getReferenceUsagePrefix(usage, label); + } + return label.toString(); + } + + /** + * Get the value to display when a direct edit has been called on the given {@link Comment}. + * + * @param comment + * the given {@link Comment}. + * @return the value to display. + */ + public String getDefaultInitialDirectEditLabel(Comment comment) { + return comment.getBody(); + } + + /** + * Get the value to display when a direct edit has been called on the given {@link TextualRepresentation}. + * + * @param textualRepresentation + * the given {@link Comment}. + * @return the value to display. + */ + public String getDefaultInitialDirectEditLabel(TextualRepresentation textualRepresentation) { + return textualRepresentation.getBody(); + } + + + /** + * Return the label of the prefix part of the given {@link Usage}. + * + * @param usage + * the given {@link Usage}. + * @return the label of the prefix part of the given {@link Usage} if there is one, an empty string otherwise. + */ + public String getUsageListItemPrefix(Usage usage) { + StringBuilder label = new StringBuilder(); + if (usage.getDirection() == FeatureDirectionKind.IN) { + label.append(LabelConstants.IN + LabelConstants.SPACE); + } else if (usage.getDirection() == FeatureDirectionKind.OUT) { + label.append(LabelConstants.OUT + LabelConstants.SPACE); + } else if (usage.getDirection() == FeatureDirectionKind.INOUT) { + label.append(LabelConstants.INOUT + LabelConstants.SPACE); + } + if (usage.isIsAbstract() && !usage.isIsVariation()) { + label.append(LabelConstants.ABSTRACT + LabelConstants.SPACE); + } + if (usage.isIsVariation()) { + label.append(LabelConstants.VARIATION + LabelConstants.SPACE); + } + if (usage.isIsConstant()) { + label.append(LabelConstants.CONSTANT + LabelConstants.SPACE); + } + if (usage.isIsDerived()) { + label.append(LabelConstants.DERIVED + LabelConstants.SPACE); + } + if (usage.isIsEnd()) { + label.append(LabelConstants.END + LabelConstants.SPACE); + } + this.getReferenceUsagePrefix(usage, label); + return label.toString(); + } + + /** + * Return the label for the given {@link Usage} when displayed as a compartment item. + * + * @param usage + * the given {@link Usage}. + * @return the label for the given {@link Usage}. + */ + public String getCompartmentItemLabel(Usage usage) { + return this.getCompartmentItemStringRepresentation(usage, false); + } + + private String getCompartmentItemStringRepresentation(Usage usage, boolean directEditInput) { + StringBuilder label = new StringBuilder(); + if (usage instanceof ConstraintUsage constraintUsage + && usage.getOwningMembership() instanceof RequirementConstraintMembership) { + // Use the constraint-specific rendering only if the element is a constraint owned by a requirement. Other + // constraints (including requirements) are rendered as regular elements. + label.append(this.getCompartmentItemLabel(constraintUsage, directEditInput)); + } else { + label.append(this.getUsageListItemPrefix(usage)); + label.append(this.getIdentificationLabel(usage)); + label.append(this.getMultiplicityStringRepresentation(usage, directEditInput)); + label.append(this.getTypingLabel(usage)); + label.append(this.getRedefinitionLabel(usage)); + label.append(this.getSubsettingLabel(usage)); + label.append(this.getValueStringRepresentation(usage, directEditInput)); + } + return label.toString(); + } + + /** + * The default label for edges. + * + * @param element + * the element to get the edge label from + * @return the edge label + */ + public String getEdgeLabel(Element element) { + StringBuilder label = new StringBuilder(); + label.append(this.getIdentificationLabel(element)); + label.append(this.getTypingLabel(element)); + return label.toString(); + } + + /** + * Returns the label for the given {@code dependency}. + * + * @param dependency + * the dependency to get the edge label from + * @return the edge label + */ + public String getDependencyLabel(Dependency dependency) { + return this.getIdentificationLabel(dependency); + } + + /** + * Returns the label for the given {@link ConstraintUsage} when displayed as compartment item. + * + * @param constraintUsage + * the given {@link ConstraintUsage} + * @return the label for the given {@link ConstraintUsage} + */ + private String getCompartmentItemLabel(ConstraintUsage constraintUsage, boolean directEditInput) { + StringBuilder label = new StringBuilder(); + if (constraintUsage == null) { + label.append(""); + } else if (!constraintUsage.getOwnedMember().isEmpty() && constraintUsage.getOwnedMember().get(0) instanceof Expression expression) { + label.append(this.getSysmlTextualRepresentation(expression, directEditInput)); + } else { + // The constraint doesn't have an expression, we use its name as default label. + label.append(this.getIdentificationLabel(constraintUsage)); + } + return label.toString(); + } + + /** + * Return the label for the given {@link Documentation} when displayed as a compartment item. + * + * @param documentation + * the given {@link Documentation}. + * @return the label for the given {@link Documentation}. + */ + public String getCompartmentItemLabel(Documentation documentation) { + StringBuilder label = new StringBuilder(); + String declaredName = documentation.getDeclaredName(); + if (declaredName != null && !declaredName.isBlank()) { + label.append(declaredName); + label.append(LabelConstants.CR); + } + String body = documentation.getBody(); + if (body != null) { + label.append(body); + } + return label.toString(); + } + + /** + * Get the value to display when a direct edit has been called on the given item list {@link Usage}. + * + * @param usage + * the given {@link Usage}. + * @return the value to display. + */ + public String getInitialDirectEditListItemLabel(Usage usage) { + String result; + if (usage instanceof ConstraintUsage constraintUsage && + usage.getOwningMembership() instanceof RequirementConstraintMembership) { + result = this.getInitialDirectEditListItemLabel(constraintUsage, true); + } else { + result = this.getCompartmentItemStringRepresentation(usage, true); + } + return result; + } + + private String getInitialDirectEditListItemLabel(ConstraintUsage constraintUsage, boolean directEditInput) { + String result; + if (!constraintUsage.getOwnedMember().isEmpty() && constraintUsage.getOwnedMember().get(0) instanceof Expression expression) { + result = this.getSysmlTextualRepresentation(expression, directEditInput); + } else { + // The constraint doesn't have an expression, we set an initial empty string for the direct edit. + result = ""; + } + return result; + } + + /** + * Get the value to display when a direct edit has been called on the given {@link Documentation}. + * + * @param documentation + * the given {@link Documentation}. + * @return the value to display. + */ + public String getInitialDirectEditListItemLabel(Documentation documentation) { + return documentation.getBody(); + } + + /** + * Get the value to display when a direct edit has been called on the given {@link Comment}. + * + * @param comment + * the given {@link comment}. + * @return the value to display. + */ + public String getInitialDirectEditListItemLabel(Comment comment) { + return comment.getBody(); + } + + /** + * Computes the label for a {@link TransitionUsage}. + * + * @param transition + * The {@link TransitionUsage} to compute the label for + */ + public String getTransitionLabel(TransitionUsage transition) { + return this.getTransitionLabel(transition, true); + } + + /** + * Computes the label for a {@link TransitionUsage}. + * + * @param transition + * The {@link TransitionUsage} to compute the label for + * @param displayGuard + * holds true to display the guard + */ + private String getTransitionLabel(TransitionUsage transition, boolean displayGuard) { + // trigger-expression '/' ActionUsage + Appender appender = new Appender(LabelConstants.SPACE, LabelConstants.SPACE); + + this.handleTransitionTriggerExpression(transition, displayGuard, appender, false); + + EList effectActions = transition.getEffectAction(); + if (!effectActions.isEmpty()) { + String effectLabel = this.getEffectActionsDefaultDirectEditLabel(effectActions); + if (!effectLabel.isBlank()) { + appender.appendWithSpaceIfNeeded("/ " + effectLabel); + } + } + return appender.toString(); + } + + private void handleTransitionTriggerExpression(TransitionUsage transition, boolean displayGuard, Appender appender, boolean directEditInput) { + this.handleAcceptParameterPart(transition, appender, directEditInput); + if (displayGuard) { + this.handleGuardExpression(transition, appender, directEditInput); + } + } + + private void handleGuardExpression(TransitionUsage transition, Appender appender, boolean directEditInput) { + EList guardExpressions = transition.getGuardExpression(); + if (!guardExpressions.isEmpty()) { + String textGuardExpression = guardExpressions.stream().map(exp -> this.getSysmlTextualRepresentation(exp, directEditInput)) + .filter(Objects::nonNull) + .collect(joining(GUARD_EXPRESSION_SEPARATOR)); + appender.appendWithSpaceIfNeeded("[").append(textGuardExpression).append("]"); + } + } + + private void handleAcceptParameterPart(TransitionUsage transition, Appender appender, boolean directEditInput) { + EList triggerActions = transition.getTriggerAction(); + if (!triggerActions.isEmpty()) { + SysMLElementSerializer sysmlSerializer = this.buildSerializer(directEditInput); + String textGuardExpression = triggerActions.stream().map(sysmlSerializer::getAcceptParameterPart) + .filter(Objects::nonNull) + .collect(joining(TRIGGER_ACTION_SEPARATOR)); + appender.append(textGuardExpression); + } + } + + private String getEffectActionsDefaultDirectEditLabel(EList effectActions) { + String effectLabel; + effectLabel = this.getElementsDefaultInitialDirectEditLabel(effectActions, (a, b) -> { + return a + EFFECT_ACTION_SEPARATOR + b; + }); + return effectLabel; + } + + private String getElementsDefaultInitialDirectEditLabel(EList elements, BinaryOperator reduceOperator) { + return elements.stream() + .map(action -> { + if (action instanceof AcceptActionUsage aau && aau.getPayloadParameter() != null) { + return this.getDefaultInitialDirectEditLabel(aau.getPayloadParameter()); + } + return this.getDefaultInitialDirectEditLabel(action); + }) + .reduce(reduceOperator) + .orElse(""); + } + + /** + * Return the label for the given {@link Usage} represented as a border node. + * + * @param usage + * the given {@link Usage}. + * @return the label for the given {@link Usage}. + */ + public String getBorderNodeUsageLabel(Usage usage) { + StringBuilder label = new StringBuilder(); + String declaredName = usage.getDeclaredName(); + if (declaredName != null && !declaredName.isBlank()) { + label.append(declaredName); + } + label + .append(this.getTypingLabel(usage)) + .append(this.getRedefinitionLabel(usage)) + .append(this.getSubsettingLabel(usage)); + return label.toString(); + } + + private String getDeclaredNameLabel(Element element) { + var label = element.getName(); + if (ElementUtil.isFromStandardLibrary(element)) { + label = element.getQualifiedName(); + } + return label; + } + + /** + * Returns the label for the short name of the given {@code element}. + * + * @param element + * the element to compute the short name label from + * @return the label for the short name of the given {@code element} + */ + private String getShortNameLabel(Element element) { + StringBuilder label = new StringBuilder(); + String declaredShortName = element.getDeclaredShortName(); + if (declaredShortName != null && !declaredShortName.isBlank()) { + label.append(LabelConstants.LESSER_THAN) + .append(declaredShortName) + .append(LabelConstants.GREATER_THAN) + .append(LabelConstants.SPACE); + } + return label.toString(); + } + + /** + * Return the label of the reference prefix part of the given {@link Usage}. + * + * @param usage + * the given {@link Usage}. + * @return the label of the reference prefix part of the given {@link Usage} if there is one, an empty string + * otherwise. + */ + private void getReferenceUsagePrefix(Usage usage, StringBuilder label) { + if (usage.isIsReference() && !(usage instanceof AttributeUsage)) { + // AttributeUsage are always referential, so no need to add the ref keyword + label.append(LabelConstants.REF + LabelConstants.SPACE); + } + } + + /** + * Builds a SysMLSerializer. + * + * @param resolvableName + * holds true to compute resolvable names for references, otherwise simple name are used to + * reference an element. + * @return a new {@link SysMLElementSerializer}. + */ + private SysMLElementSerializer buildSerializer(boolean resolvableName) { + final INameDeresolver nameDeresolver; + if (resolvableName) { + nameDeresolver = new FileNameDeresolver(); + } else { + nameDeresolver = new SimpleNameDeresolver(); + } + return new SysMLElementSerializer("\n", " ", nameDeresolver, s -> { + this.logger.info(s.message()); + }); + } + + private String getFeatureValueRelationshipSymbol(FeatureValue featureValueMembership) { + final String affectationSymbole; + + if (featureValueMembership.isIsInitial()) { + affectationSymbole = LabelConstants.COLON_EQUAL; + } else { + affectationSymbole = LabelConstants.EQUAL; + } + return affectationSymbole; + } +} diff --git a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/api/IDiagramLabelService.java b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/api/IDiagramLabelService.java new file mode 100644 index 000000000..3abc37330 --- /dev/null +++ b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/api/IDiagramLabelService.java @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (c) 2025 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.diagram.services.api; + +import org.eclipse.syson.sysml.Element; +import org.eclipse.syson.sysml.Usage; + +/** + * Interface providing diagram label-related services. + * + * @author arichard + */ +public interface IDiagramLabelService { + + /** + * Returns the identification of the provided {@code element}. + *

+ * The identification of an element is the concatenation of its short name and its declared name. + *

+ * + * @param element + * the element to get the identification from + * @return the identification + */ + String getIdentificationLabel(Element element); + + /** + * Return the label of the redefinition of the given {@link Element}. + * + * @param element + * the given {@link Element}. + * @return the label of the redefinition of the given {@link Element} if there is one, an empty string otherwise. + */ + String getRedefinitionLabel(Element element); + + /** + * Return the label of the subclassification of the given {@link Element}. + * + * @param usage + * the given {@link Element}. + * @return the label of the subclassification of the given {@link Element} if there is one, an empty string + * otherwise. + */ + String getSubclassificationLabel(Element element); + + /** + * Return the label of the subsetting of the given {@link Element}. + * + * @param usage + * the given {@link Element}. + * @return the label of the subsetting of the given {@link Element} if there is one, an empty string otherwise. + */ + String getSubsettingLabel(Element element); + + /** + * Get the SysML textual representation of the given element. + * + * @param element + * the element to convert to textual format + * @param resolvableName + * holds true to compute resolvable names for references, otherwise simple name are used to + * reference an element. + * @return a textual representation or null if none + */ + String getSysmlTextualRepresentation(Element element, boolean resolvableName); + + /** + * Return the label of the typing part of the given {@link Element}. + * + * @param usage + * the given {@link Element}. + * @return the label of the typing part of the given {@link Element} if there is one, an empty string otherwise. + */ + String getTypingLabel(Element element); + + /** + * Return the label of the value part of the given {@link Usage}. + * + * @param usage + * the given {@link Usage}. + * @return the label of the value part of the given {@link Usage} if there is one, an empty string otherwise. + */ + String getValueLabel(Usage usage); +} diff --git a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/aql/DiagramMutationAQLService.java b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/aql/DiagramMutationAQLService.java index a6f9de8d1..2b002ba2b 100644 --- a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/aql/DiagramMutationAQLService.java +++ b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/aql/DiagramMutationAQLService.java @@ -12,12 +12,57 @@ *******************************************************************************/ package org.eclipse.syson.diagram.services.aql; +import java.util.Objects; + +import org.eclipse.syson.diagram.services.DiagramMutationLabelService; +import org.eclipse.syson.sysml.Element; + /** * Entry point for all diagram-related services doing mutations in diagrams and called by AQL expressions in diagram - * descriptions. + * descriptions. This class is not a @Service class but act as it was, because it is called by IJavaServiceProvider. * * @author arichard */ public class DiagramMutationAQLService { + private final DiagramMutationLabelService diagramMutationLabelService; + + public DiagramMutationAQLService(DiagramMutationLabelService diagramMutationLabelService) { + this.diagramMutationLabelService = Objects.requireNonNull(diagramMutationLabelService); + } + + /** + * {@link DiagramMutationLabelService#directEdit(Element, String)}. + */ + public Element directEdit(Element element, String newLabel) { + return this.diagramMutationLabelService.directEdit(element, newLabel); + } + + /** + * {@link DiagramMutationLabelService#directEditNode(Element, String)}. + */ + public Element directEditNode(Element element, String newLabel) { + return this.diagramMutationLabelService.directEditNode(element, newLabel); + } + + /** + * {@link DiagramMutationLabelService#directEditListItem(Element, String)}. + */ + public Element directEditListItem(Element element, String newLabel) { + return this.diagramMutationLabelService.directEditListItem(element, newLabel); + } + + /** + * {@link DiagramMutationLabelService#editMultiplicityRangeCenterLabel(Element, String)}. + */ + public Element editMultiplicityRangeCenterLabel(Element element, String newLabel) { + return this.diagramMutationLabelService.editMultiplicityRangeCenterLabel(element, newLabel); + } + + /** + * {@link DiagramMutationLabelService#editEdgeCenterLabel(Element, String)}. + */ + public Element editEdgeCenterLabel(Element element, String newLabel) { + return this.diagramMutationLabelService.editEdgeCenterLabel(element, newLabel); + } } diff --git a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/aql/DiagramQueryAQLService.java b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/aql/DiagramQueryAQLService.java index 35c43b3b9..3c5a4ef9b 100644 --- a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/aql/DiagramQueryAQLService.java +++ b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/aql/DiagramQueryAQLService.java @@ -12,12 +12,118 @@ *******************************************************************************/ package org.eclipse.syson.diagram.services.aql; +import java.util.Objects; + +import org.eclipse.syson.diagram.services.DiagramQueryLabelService; +import org.eclipse.syson.sysml.Comment; +import org.eclipse.syson.sysml.Dependency; +import org.eclipse.syson.sysml.Documentation; +import org.eclipse.syson.sysml.Element; +import org.eclipse.syson.sysml.TextualRepresentation; +import org.eclipse.syson.sysml.TransitionUsage; +import org.eclipse.syson.sysml.Usage; + /** * Entry point for all diagram-related services doing queries in diagrams and called by AQL expressions in diagram - * descriptions. + * descriptions. This class is not a @Service class but act as it was, because it is called by IJavaServiceProvider. * * @author arichard */ public class DiagramQueryAQLService { + private final DiagramQueryLabelService diagramQueryLabelService; + + public DiagramQueryAQLService(DiagramQueryLabelService diagramQueryLabelService) { + this.diagramQueryLabelService = Objects.requireNonNull(diagramQueryLabelService); + } + + /** + * {@link DiagramQueryLabelService#getBorderNodeUsageLabel(Usage)}. + */ + public String getBorderNodeUsageLabel(Usage usage) { + return this.diagramQueryLabelService.getBorderNodeUsageLabel(usage); + } + + /** + * {@link DiagramQueryLabelService#getCompartmentItemLabel(Usage)}. + * {@link DiagramQueryLabelService#getCompartmentItemLabel(Documentation)}. + */ + public String getCompartmentItemLabel(Element element) { + String compartmentItemLabel = ""; + if (element instanceof Documentation documentation) { + compartmentItemLabel = this.diagramQueryLabelService.getCompartmentItemLabel(documentation); + } else if (element instanceof Usage usage) { + compartmentItemLabel = this.diagramQueryLabelService.getCompartmentItemLabel(usage); + } + return compartmentItemLabel; + } + + /** + * {@link DiagramQueryLabelService#getContainerLabel(Element)}. + */ + public String getContainerLabel(Element element) { + return this.diagramQueryLabelService.getContainerLabel(element); + } + + /** + * {@link DiagramQueryLabelService#getDefaultInitialDirectEditLabel(Comment)}. + * {@link DiagramQueryLabelService#getDefaultInitialDirectEditLabel(Element)}. + * {@link DiagramQueryLabelService#getDefaultInitialDirectEditLabel(TextualRepresentation)}. + */ + public String getDefaultInitialDirectEditLabel(Element element) { + String defaultInitialDirectEditLabel = ""; + if (element instanceof Comment comment) { + defaultInitialDirectEditLabel = this.diagramQueryLabelService.getDefaultInitialDirectEditLabel(comment); + } else if (element instanceof TextualRepresentation textualRepresentation) { + defaultInitialDirectEditLabel = this.diagramQueryLabelService.getDefaultInitialDirectEditLabel(textualRepresentation); + } else { + defaultInitialDirectEditLabel = this.diagramQueryLabelService.getDefaultInitialDirectEditLabel(element); + } + return defaultInitialDirectEditLabel; + } + + /** + * {@link DiagramQueryLabelService#getDependencyLabel(Dependency)}. + */ + public String getDependencyLabel(Dependency dependency) { + return this.diagramQueryLabelService.getDependencyLabel(dependency); + } + + /** + * {@link DiagramQueryLabelService#getEdgeLabel(Element)}. + */ + public String getEdgeLabel(Element element) { + return this.diagramQueryLabelService.getEdgeLabel(element); + } + + /** + * {@link DiagramQueryLabelService#getInitialDirectEditListItemLabel(Documentation)}. + * {@link DiagramQueryLabelService#getInitialDirectEditListItemLabel(Comment)}. + * {@link DiagramQueryLabelService#getInitialDirectEditListItemLabel(Usage)}. + */ + public String getInitialDirectEditListItemLabel(Element element) { + String initialDirectEditListItemLabel = ""; + if (element instanceof Documentation documentation) { + initialDirectEditListItemLabel = this.diagramQueryLabelService.getInitialDirectEditListItemLabel(documentation); + } else if (element instanceof Comment comment) { + initialDirectEditListItemLabel = this.diagramQueryLabelService.getInitialDirectEditListItemLabel(comment); + } else if (element instanceof Usage usage) { + initialDirectEditListItemLabel = this.diagramQueryLabelService.getInitialDirectEditListItemLabel(usage); + } + return initialDirectEditListItemLabel; + } + + /** + * {@link DiagramQueryLabelService#getMultiplicityLabel(Element)}. + */ + public String getMultiplicityLabel(Element element) { + return this.diagramQueryLabelService.getMultiplicityLabel(element); + } + + /** + * {@link DiagramQueryLabelService#getTransitionLabel(TransitionUsage)}. + */ + public String getTransitionLabel(TransitionUsage transition) { + return this.diagramQueryLabelService.getTransitionLabel(transition); + } } diff --git a/backend/services/syson-services/src/main/java/org/eclipse/syson/services/MultiLineLabelSwitch.java b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/utils/MultiLineLabelSwitch.java similarity index 99% rename from backend/services/syson-services/src/main/java/org/eclipse/syson/services/MultiLineLabelSwitch.java rename to backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/utils/MultiLineLabelSwitch.java index 8919aaf81..8a7ed5c65 100644 --- a/backend/services/syson-services/src/main/java/org/eclipse/syson/services/MultiLineLabelSwitch.java +++ b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/utils/MultiLineLabelSwitch.java @@ -10,11 +10,12 @@ * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -package org.eclipse.syson.services; +package org.eclipse.syson.diagram.services.utils; import java.util.Objects; import org.eclipse.emf.ecore.EObject; +import org.eclipse.syson.diagram.services.api.IDiagramLabelService; import org.eclipse.syson.sysml.AcceptActionUsage; import org.eclipse.syson.sysml.ActionDefinition; import org.eclipse.syson.sysml.ActionUsage; @@ -83,9 +84,9 @@ */ public class MultiLineLabelSwitch extends SysmlSwitch { - private final LabelService labelService; + private final IDiagramLabelService labelService; - public MultiLineLabelSwitch(LabelService labelService) { + public MultiLineLabelSwitch(IDiagramLabelService labelService) { this.labelService = Objects.requireNonNull(labelService); } diff --git a/backend/views/syson-diagram-common-view/src/test/java/org/eclipse/syson/diagram/common/view/services/ViewLabelServiceTest.java b/backend/services/syson-diagram-services/src/test/java/org/eclipse/syson/diagram/services/DiagramQueryLabelServiceTest.java similarity index 72% rename from backend/views/syson-diagram-common-view/src/test/java/org/eclipse/syson/diagram/common/view/services/ViewLabelServiceTest.java rename to backend/services/syson-diagram-services/src/test/java/org/eclipse/syson/diagram/services/DiagramQueryLabelServiceTest.java index 365a1851e..19cf66027 100644 --- a/backend/views/syson-diagram-common-view/src/test/java/org/eclipse/syson/diagram/common/view/services/ViewLabelServiceTest.java +++ b/backend/services/syson-diagram-services/src/test/java/org/eclipse/syson/diagram/services/DiagramQueryLabelServiceTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024, 2025 Obeo. + * Copyright (c) 2025 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -10,14 +10,13 @@ * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -package org.eclipse.syson.diagram.common.view.services; +package org.eclipse.syson.diagram.services; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.List; -import org.eclipse.sirius.components.core.api.IFeedbackMessageService; import org.eclipse.syson.sysml.AttributeUsage; import org.eclipse.syson.sysml.ConstraintUsage; import org.eclipse.syson.sysml.Dependency; @@ -44,11 +43,11 @@ import org.junit.jupiter.api.Test; /** - * View Label services tests. + * Label services tests. * * @author jmallet */ -public class ViewLabelServiceTest { +public class DiagramQueryLabelServiceTest { private static final String ATTRIBUTE_USAGE_NAME = "myAttributeUsage"; @@ -60,11 +59,115 @@ public class ViewLabelServiceTest { private static final String SHORT_NAME_LABEL = LabelConstants.LESSER_THAN + SHORT_NAME + LabelConstants.GREATER_THAN; - private ViewLabelService viewLabelService; + private DiagramQueryLabelService labelService; @BeforeEach void beforeEach() { - this.viewLabelService = new ViewLabelService(new IFeedbackMessageService.NoOp(), new ShowDiagramsIconsService()); + this.labelService = new DiagramQueryLabelService(); + } + + @DisplayName("Check Attribute label with name and short name") + @Test + void testAttributeLabelWithNameAndShortName() { + AttributeUsage attributeUsage = SysmlFactory.eINSTANCE.createAttributeUsage(); + attributeUsage.setDeclaredName(ATTRIBUTE_USAGE_NAME); + attributeUsage.setDeclaredShortName(ATTRIBUTE_USAGE_SHORT_NAME); + StringBuilder expectedLabel = new StringBuilder(); + expectedLabel.append(LabelConstants.OPEN_QUOTE) + .append("attribute") + .append(LabelConstants.CLOSE_QUOTE) + .append(LabelConstants.CR) + .append(LabelConstants.LESSER_THAN) + .append(ATTRIBUTE_USAGE_SHORT_NAME) + .append(LabelConstants.GREATER_THAN) + .append(LabelConstants.SPACE) + .append(ATTRIBUTE_USAGE_NAME); + assertEquals(expectedLabel.toString(), this.labelService.getContainerLabel(attributeUsage)); + } + + @DisplayName("Check Usage prefix label with default properties") + @Test + void testUsagePrefixLabelWithDefaultProperties() { + Usage usageWithNoDirection = SysmlFactory.eINSTANCE.createUsage(); + assertEquals(LabelConstants.REF + LabelConstants.SPACE, this.labelService.getUsageListItemPrefix(usageWithNoDirection)); + } + + @DisplayName("Check Usage prefix label with abstract property") + @Test + void testUsagePrefixLabelWithAbstractProperty() { + Usage usageWithNoDirection = SysmlFactory.eINSTANCE.createUsage(); + usageWithNoDirection.setIsAbstract(true); + assertEquals(LabelConstants.ABSTRACT + LabelConstants.SPACE + LabelConstants.REF + LabelConstants.SPACE, this.labelService.getUsageListItemPrefix(usageWithNoDirection)); + } + + @DisplayName("Check Usage prefix label with derived property") + @Test + void testUsagePrefixLabelWithDerivedProperty() { + Usage usageWithNoDirection = SysmlFactory.eINSTANCE.createUsage(); + usageWithNoDirection.setIsDerived(true); + assertEquals(LabelConstants.DERIVED + LabelConstants.SPACE + LabelConstants.REF + LabelConstants.SPACE, this.labelService.getUsageListItemPrefix(usageWithNoDirection)); + } + + @DisplayName("Check Usage prefix label with end property") + @Test + void testUsagePrefixLabelWithEndProperty() { + Usage usageWithNoDirection = SysmlFactory.eINSTANCE.createUsage(); + usageWithNoDirection.setIsEnd(true); + assertEquals(LabelConstants.END + LabelConstants.SPACE + LabelConstants.REF + LabelConstants.SPACE, this.labelService.getUsageListItemPrefix(usageWithNoDirection)); + } + + @DisplayName("Check Usage prefix label with constant property") + @Test + void testUsagePrefixLabelWithConstantProperty() { + Usage usageWithNoDirection = SysmlFactory.eINSTANCE.createUsage(); + usageWithNoDirection.setIsConstant(true); + assertEquals(LabelConstants.CONSTANT + LabelConstants.SPACE + LabelConstants.REF + LabelConstants.SPACE, this.labelService.getUsageListItemPrefix(usageWithNoDirection)); + } + + @DisplayName("Check Usage prefix label with readonly property") + @Test + void testUsagePrefixLabelWithVariationProperty() { + Usage usageWithNoDirection = SysmlFactory.eINSTANCE.createUsage(); + usageWithNoDirection.setIsVariation(true); + assertEquals(LabelConstants.VARIATION + LabelConstants.SPACE + LabelConstants.REF + LabelConstants.SPACE, this.labelService.getUsageListItemPrefix(usageWithNoDirection)); + } + + @DisplayName("Check Usage prefix label with many custom properties") + @Test + void testUsagePrefixLabelWithManyCustomProperties() { + Usage usageWithNoDirection = SysmlFactory.eINSTANCE.createUsage(); + usageWithNoDirection.setIsAbstract(true); + usageWithNoDirection.setIsDerived(true); + usageWithNoDirection.setIsEnd(true); + usageWithNoDirection.setIsConstant(true); + usageWithNoDirection.setIsVariation(true); + + assertEquals(LabelConstants.VARIATION + LabelConstants.SPACE + LabelConstants.CONSTANT + LabelConstants.SPACE + + LabelConstants.DERIVED + LabelConstants.SPACE + + LabelConstants.END + + LabelConstants.SPACE + LabelConstants.REF + LabelConstants.SPACE, this.labelService.getUsageListItemPrefix(usageWithNoDirection)); + } + + @DisplayName("Check Usage prefix label with direction properties") + @Test + void testUsagePrefixLabelWithDirectionProperties() { + Usage usageWithDirection = SysmlFactory.eINSTANCE.createUsage(); + + usageWithDirection.setDirection(FeatureDirectionKind.IN); + assertEquals(LabelConstants.IN + LabelConstants.SPACE + LabelConstants.REF + LabelConstants.SPACE, this.labelService.getUsageListItemPrefix(usageWithDirection)); + + usageWithDirection.setDirection(FeatureDirectionKind.OUT); + assertEquals(LabelConstants.OUT + LabelConstants.SPACE + LabelConstants.REF + LabelConstants.SPACE, this.labelService.getUsageListItemPrefix(usageWithDirection)); + + usageWithDirection.setDirection(FeatureDirectionKind.INOUT); + assertEquals(LabelConstants.INOUT + LabelConstants.SPACE + LabelConstants.REF + LabelConstants.SPACE, this.labelService.getUsageListItemPrefix(usageWithDirection)); + } + + @DisplayName("Check AttributeUsage prefix label with default properties") + @Test + void testAttributeUsagePrefixLabelWithDefaultProperties() { + AttributeUsage attributeUsageWithNoDirection = SysmlFactory.eINSTANCE.createAttributeUsage(); + assertEquals("", this.labelService.getUsageListItemPrefix(attributeUsageWithNoDirection)); } @DisplayName("Check Attribute Usage item label with name") @@ -72,7 +175,7 @@ void beforeEach() { void testItemCompartmentLabelWithName() { AttributeUsage attributeUsage = SysmlFactory.eINSTANCE.createAttributeUsage(); attributeUsage.setDeclaredName(ATTRIBUTE_USAGE_NAME); - assertEquals(ATTRIBUTE_USAGE_NAME, this.viewLabelService.getCompartmentItemLabel(attributeUsage)); + assertEquals(ATTRIBUTE_USAGE_NAME, this.labelService.getCompartmentItemLabel(attributeUsage)); } @DisplayName("Check Attribute Usage item label with name and short name") @@ -81,7 +184,7 @@ void testItemCompartmentLabelWithNameAndShortName() { attributeUsage.setDeclaredName(ATTRIBUTE_USAGE_NAME); attributeUsage.setDeclaredShortName(ATTRIBUTE_USAGE_SHORT_NAME); assertEquals(LabelConstants.LESSER_THAN + ATTRIBUTE_USAGE_SHORT_NAME + LabelConstants.GREATER_THAN + LabelConstants.SPACE + ATTRIBUTE_USAGE_NAME, - this.viewLabelService.getCompartmentItemLabel(attributeUsage)); + this.labelService.getCompartmentItemLabel(attributeUsage)); } @DisplayName("GIVEN a FeatureValue to an attribute, WHEN it is an initial FeatureValue relationship, THEN the label should use the symbole ':=' instead of '='") @@ -96,11 +199,11 @@ public void testInitalFeatureValueSymboleLabel() { featureValue.getOwnedRelatedElement().add(literal); attributeUsage.getOwnedRelationship().add(featureValue); - assertEquals("myAttributeUsage = 1", this.viewLabelService.getCompartmentItemLabel(attributeUsage)); + assertEquals("myAttributeUsage = 1", this.labelService.getCompartmentItemLabel(attributeUsage)); featureValue.setIsInitial(true); - assertEquals("myAttributeUsage := 1", this.viewLabelService.getCompartmentItemLabel(attributeUsage)); + assertEquals("myAttributeUsage := 1", this.labelService.getCompartmentItemLabel(attributeUsage)); } @@ -116,15 +219,15 @@ public void testDefaultFeatureValueSymboleLabel() { featureValue.getOwnedRelatedElement().add(literal); attributeUsage.getOwnedRelationship().add(featureValue); - assertEquals("myAttributeUsage = 1", this.viewLabelService.getCompartmentItemLabel(attributeUsage)); + assertEquals("myAttributeUsage = 1", this.labelService.getCompartmentItemLabel(attributeUsage)); featureValue.setIsDefault(true); - assertEquals("myAttributeUsage default = 1", this.viewLabelService.getCompartmentItemLabel(attributeUsage)); + assertEquals("myAttributeUsage default = 1", this.labelService.getCompartmentItemLabel(attributeUsage)); featureValue.setIsInitial(true); - assertEquals("myAttributeUsage default := 1", this.viewLabelService.getCompartmentItemLabel(attributeUsage)); + assertEquals("myAttributeUsage default := 1", this.labelService.getCompartmentItemLabel(attributeUsage)); } @@ -132,7 +235,7 @@ public void testDefaultFeatureValueSymboleLabel() { @Test void testItemCompartmentLabelWithoutName() { AttributeUsage attributeUsage = SysmlFactory.eINSTANCE.createAttributeUsage(); - assertEquals("", this.viewLabelService.getCompartmentItemLabel(attributeUsage)); + assertEquals("", this.labelService.getCompartmentItemLabel(attributeUsage)); } @DisplayName("Check Attribute Usage item label with prefix") @@ -142,7 +245,7 @@ void testItemCompartmentLabelWithPrefix() { attributeUsage.setDeclaredName(ATTRIBUTE_USAGE_NAME); attributeUsage.setIsAbstract(true); - assertEquals(LabelConstants.ABSTRACT + LabelConstants.SPACE + ATTRIBUTE_USAGE_NAME, this.viewLabelService.getCompartmentItemLabel(attributeUsage)); + assertEquals(LabelConstants.ABSTRACT + LabelConstants.SPACE + ATTRIBUTE_USAGE_NAME, this.labelService.getCompartmentItemLabel(attributeUsage)); } @DisplayName("Check Attribute Usage item label with multiplicity") @@ -152,7 +255,7 @@ void testItemCompartmentLabelWithMultiplicity() { attributeUsage.setDeclaredName(ATTRIBUTE_USAGE_NAME); attributeUsage.setIsOrdered(true); - assertEquals(ATTRIBUTE_USAGE_NAME + LabelConstants.SPACE + LabelConstants.ORDERED, this.viewLabelService.getCompartmentItemLabel(attributeUsage)); + assertEquals(ATTRIBUTE_USAGE_NAME + LabelConstants.SPACE + LabelConstants.ORDERED, this.labelService.getCompartmentItemLabel(attributeUsage)); } @DisplayName("Check Attribute Usage item label with prefix and multiplicity") @@ -164,7 +267,7 @@ void testItemCompartmentLabelWithPrefixAndMultiplicity() { attributeUsage.setIsOrdered(true); attributeUsage.setIsAbstract(true); assertEquals(LabelConstants.ABSTRACT + LabelConstants.SPACE + ATTRIBUTE_USAGE_NAME + LabelConstants.SPACE + LabelConstants.ORDERED, - this.viewLabelService.getCompartmentItemLabel(attributeUsage)); + this.labelService.getCompartmentItemLabel(attributeUsage)); } @DisplayName("GIVEN a ConstraintUsage with no expression, WHEN its label is computed, THEN the label contains the name of the constraint") @@ -175,7 +278,7 @@ public void testGetCompartmentItemLabelOfConstraintWithNoExpression() { // Constraints have a special label when they are inside a RequirementConstraintMembership RequirementConstraintMembership requirementConstraintMembership = SysmlFactory.eINSTANCE.createRequirementConstraintMembership(); requirementConstraintMembership.getOwnedRelatedElement().add(constraintUsage); - assertThat(this.viewLabelService.getCompartmentItemLabel(constraintUsage)).isEqualTo(CONSTRAINT_USAGE_NAME); + assertThat(this.labelService.getCompartmentItemLabel(constraintUsage)).isEqualTo(CONSTRAINT_USAGE_NAME); } @DisplayName("GIVEN a ConstraintUsage with a boolean expression, WHEN its label is computed, THEN the label represents the expression") @@ -194,7 +297,7 @@ public void testGetCompartmentItemLabelOfConstraintWithBooleanExpression() { literalInteger.setValue(2); yValue.getOwnedRelatedElement().add(literalInteger); - assertThat(this.viewLabelService.getCompartmentItemLabel(constraintUsage)).isEqualTo("1 >= 2"); + assertThat(this.labelService.getCompartmentItemLabel(constraintUsage)).isEqualTo("1 >= 2"); } @DisplayName("GIVEN a ConstraintUsage with an expression containing a subject reference, WHEN its label is computed, THEN the label represents the expression") @@ -217,7 +320,7 @@ public void testGetCompartmentItemLabelOfConstraintWithSubjectReferenceExpressio yFeatureReference.getOwnedRelationship().add(yFeatureReferenceMembership); yFeatureReferenceMembership.setMemberElement(subjectReference); - assertThat(this.viewLabelService.getCompartmentItemLabel(constraintUsage)).isEqualTo("1 >= mySubject"); + assertThat(this.labelService.getCompartmentItemLabel(constraintUsage)).isEqualTo("1 >= mySubject"); } @@ -242,7 +345,7 @@ public void testGetCompartmentItemLabelOfConstraintWithAttributeReferenceExpress yFeatureReference.getOwnedRelationship().add(yFeatureReferenceMembership); yFeatureReferenceMembership.setMemberElement(attributeUsage); - assertThat(this.viewLabelService.getCompartmentItemLabel(constraintUsage)).isEqualTo("1 >= myAttribute"); + assertThat(this.labelService.getCompartmentItemLabel(constraintUsage)).isEqualTo("1 >= myAttribute"); } @DisplayName("GIVEN a ConstraintUsage with an expression containing a single feature chaining, WHEN its label is computed, THEN the label represents the expression") @@ -267,7 +370,7 @@ public void testGetCompartmentItemLabelOfConstraintWithSingleFeatureChainingExpr featureChainExpression.getOwnedRelationship().add(featureChainMembership); featureChainMembership.setMemberElement(subAttributeUsage); - assertThat(this.viewLabelService.getCompartmentItemLabel(constraintUsage)).isEqualTo("1 >= myAttribute.mySubAttribute"); + assertThat(this.labelService.getCompartmentItemLabel(constraintUsage)).isEqualTo("1 >= myAttribute.mySubAttribute"); } @DisplayName("GIVEN a ConstraintUsage with an expression containing multiple feature chainings, WHEN its label is computed, THEN the label represents the expression") @@ -300,7 +403,7 @@ public void testGetCompartmentItemLabelOfConstraintWithMultipleFeatureChainingEx featureChaining2.setChainingFeature(zAttributeUsage); feature.getOwnedRelationship().addAll(List.of(featureChaining1, featureChaining2)); - assertThat(this.viewLabelService.getCompartmentItemLabel(constraintUsage)).isEqualTo("1 >= x.y.z"); + assertThat(this.labelService.getCompartmentItemLabel(constraintUsage)).isEqualTo("1 >= x.y.z"); } @DisplayName("GIVEN a Dependency with a name and short name, WHEN its edge label is computed, THEN the label contains the name and short name") @@ -310,7 +413,7 @@ public void testGetDependencyLabelOfDependencyWithNameAndShortName() { dependency.setDeclaredShortName(SHORT_NAME); dependency.setDeclaredName("dependency"); - assertThat(this.viewLabelService.getDependencyLabel(dependency)).isEqualTo(SHORT_NAME_LABEL + " dependency"); + assertThat(this.labelService.getDependencyLabel(dependency)).isEqualTo(SHORT_NAME_LABEL + " dependency"); } @DisplayName("GIVEN an Interface with a name and short name, WHEN its edge label is computed, THEN the label contains the name and short name") @@ -320,7 +423,7 @@ public void testGetEdgeLabelOfInterfaceWithNameAndShortName() { interfaceUsage.setDeclaredShortName(SHORT_NAME); interfaceUsage.setDeclaredName("interface"); - assertThat(this.viewLabelService.getEdgeLabel(interfaceUsage)).isEqualTo(SHORT_NAME_LABEL + " interface"); + assertThat(this.labelService.getEdgeLabel(interfaceUsage)).isEqualTo(SHORT_NAME_LABEL + " interface"); } /** diff --git a/backend/services/syson-services/src/test/java/org/eclipse/syson/services/MultilineLabelSwitchTest.java b/backend/services/syson-diagram-services/src/test/java/org/eclipse/syson/diagram/services/utils/MultilineLabelSwitchTest.java similarity index 98% rename from backend/services/syson-services/src/test/java/org/eclipse/syson/services/MultilineLabelSwitchTest.java rename to backend/services/syson-diagram-services/src/test/java/org/eclipse/syson/diagram/services/utils/MultilineLabelSwitchTest.java index dc07880fa..24ed8ec5d 100644 --- a/backend/services/syson-services/src/test/java/org/eclipse/syson/services/MultilineLabelSwitchTest.java +++ b/backend/services/syson-diagram-services/src/test/java/org/eclipse/syson/diagram/services/utils/MultilineLabelSwitchTest.java @@ -10,7 +10,7 @@ * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -package org.eclipse.syson.services; +package org.eclipse.syson.diagram.services.utils; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -18,7 +18,8 @@ import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; -import org.eclipse.sirius.components.core.api.IFeedbackMessageService; +import org.eclipse.syson.diagram.services.DiagramQueryLabelService; +import org.eclipse.syson.diagram.services.api.IDiagramLabelService; import org.eclipse.syson.sysml.AcceptActionUsage; import org.eclipse.syson.sysml.AnnotatingElement; import org.eclipse.syson.sysml.AssignmentActionUsage; @@ -40,7 +41,7 @@ * * @author jmallet */ -public class MultilineLabelSwitchTest extends AbstractServiceTest { +public class MultilineLabelSwitchTest { private static final SysmlPackage SYSML = SysmlPackage.eINSTANCE; @@ -120,7 +121,7 @@ public class MultilineLabelSwitchTest extends AbstractServiceTest { @BeforeEach void beforeEach() { - LabelService labelService = new LabelService(new IFeedbackMessageService.NoOp()); + IDiagramLabelService labelService = new DiagramQueryLabelService(); this.multiLineLabelSwitch = new MultiLineLabelSwitch(labelService); } diff --git a/backend/services/syson-model-services/src/main/java/org/eclipse/syson/model/services/aql/ModelQueryAQLService.java b/backend/services/syson-model-services/src/main/java/org/eclipse/syson/model/services/aql/ModelQueryAQLService.java index d268b2b6d..278ad1038 100644 --- a/backend/services/syson-model-services/src/main/java/org/eclipse/syson/model/services/aql/ModelQueryAQLService.java +++ b/backend/services/syson-model-services/src/main/java/org/eclipse/syson/model/services/aql/ModelQueryAQLService.java @@ -13,7 +13,7 @@ package org.eclipse.syson.model.services.aql; import org.eclipse.syson.sysml.Element; -import org.eclipse.syson.sysml.metamodel.services.ElementQueryService; +import org.eclipse.syson.sysml.metamodel.services.MetamodelElementQueryService; /** * Entry point for all model-related services doing queries in models and called by AQL expressions in representation @@ -23,14 +23,14 @@ */ public class ModelQueryAQLService { - private final ElementQueryService elementQueryService; + private final MetamodelElementQueryService elementQueryService; public ModelQueryAQLService() { - this.elementQueryService = new ElementQueryService(); + this.elementQueryService = new MetamodelElementQueryService(); } /** - * {@link ElementQueryService#isActor(Element)}. + * {@link MetamodelElementQueryService#isActor(Element)}. */ public boolean isActor(Element element) { return this.elementQueryService.isActor(element); diff --git a/backend/services/syson-services/src/main/java/org/eclipse/syson/services/LabelService.java b/backend/services/syson-services/src/main/java/org/eclipse/syson/services/LabelService.java index 5aa03eb32..21f4c12a0 100644 --- a/backend/services/syson-services/src/main/java/org/eclipse/syson/services/LabelService.java +++ b/backend/services/syson-services/src/main/java/org/eclipse/syson/services/LabelService.java @@ -12,49 +12,6 @@ *******************************************************************************/ package org.eclipse.syson.services; -import java.text.MessageFormat; -import java.util.Objects; - -import org.antlr.v4.runtime.CharStreams; -import org.antlr.v4.runtime.CommonTokenStream; -import org.antlr.v4.runtime.tree.ParseTree; -import org.antlr.v4.runtime.tree.ParseTreeWalker; -import org.eclipse.emf.ecore.EObject; -import org.eclipse.sirius.components.core.api.IFeedbackMessageService; -import org.eclipse.sirius.components.representations.Message; -import org.eclipse.sirius.components.representations.MessageLevel; -import org.eclipse.syson.direct.edit.grammars.DirectEditLexer; -import org.eclipse.syson.direct.edit.grammars.DirectEditParser; -import org.eclipse.syson.sysml.ActionUsage; -import org.eclipse.syson.sysml.AttributeUsage; -import org.eclipse.syson.sysml.Comment; -import org.eclipse.syson.sysml.ConstraintUsage; -import org.eclipse.syson.sysml.Definition; -import org.eclipse.syson.sysml.Element; -import org.eclipse.syson.sysml.Feature; -import org.eclipse.syson.sysml.FeatureDirectionKind; -import org.eclipse.syson.sysml.FeatureTyping; -import org.eclipse.syson.sysml.FeatureValue; -import org.eclipse.syson.sysml.LiteralExpression; -import org.eclipse.syson.sysml.MultiplicityRange; -import org.eclipse.syson.sysml.OwningMembership; -import org.eclipse.syson.sysml.Redefinition; -import org.eclipse.syson.sysml.RequirementConstraintMembership; -import org.eclipse.syson.sysml.StateSubactionMembership; -import org.eclipse.syson.sysml.Subclassification; -import org.eclipse.syson.sysml.Subsetting; -import org.eclipse.syson.sysml.TextualRepresentation; -import org.eclipse.syson.sysml.Type; -import org.eclipse.syson.sysml.Usage; -import org.eclipse.syson.sysml.VariantMembership; -import org.eclipse.syson.sysml.helper.LabelConstants; -import org.eclipse.syson.sysml.textual.SysMLElementSerializer; -import org.eclipse.syson.sysml.textual.utils.FileNameDeresolver; -import org.eclipse.syson.sysml.textual.utils.INameDeresolver; -import org.eclipse.syson.sysml.util.ElementUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * Label-related Java services used by SysON representations. * @@ -76,593 +33,4 @@ public class LabelService { public static final String TRANSITION_EXPRESSION_OFF = "TRANSITION_EXPRESSION_OFF"; - private final Logger logger = LoggerFactory.getLogger(LabelService.class); - - private final IFeedbackMessageService feedbackMessageService; - - public LabelService(IFeedbackMessageService feedbackMessageService) { - this.feedbackMessageService = Objects.requireNonNull(feedbackMessageService); - } - - public IFeedbackMessageService getFeedbackMessageService() { - return this.feedbackMessageService; - } - - /** - * Return the container label for the given {@link Element}. - * - * @param element - * the given {@link Element}. - * @return the container label for the given {@link Element}. - */ - public String getContainerLabel(Element element) { - return new MultiLineLabelSwitch(this).doSwitch(element); - } - - /** - * Apply the direct edit result (i.e. the newLabel) to the given {@link Element}. - * - * @param element - * the given {@link Element}. - * @param newLabel - * the new value to apply. - * @return the given {@link Element}. - */ - public Element directEdit(Element element, String newLabel) { - return this.directEdit(element, newLabel, (String[]) null); - } - - /** - * Apply the direct edit result (i.e. the newLabel) to the given {@link Element}, with the provided {@code options}. - *

- * This method is typically used to enable direct edit with some restrictions (e.g. de-activate the ability to edit - * the name of an element via direct edit). See {@link #directEdit(Element, String)} to perform a direct edit with - * default options. - *

- * - * @param element - * the given {@link Element} - * @param newLabel - * the new value to apply - * @param options - * the options of the direct edit - * @return the given {@link Element} - */ - public Element directEdit(Element element, String newLabel, String... options) { - return this.directEdit(element, newLabel, false, options); - } - - /** - * Apply the direct edit result (i.e. the newLabel) to the given graphical node {@link Element}. - * - * @param element - * the given {@link Element}. - * @param newLabel - * the new value to apply. - * @return the given {@link Element}. - */ - public Element directEditNode(Element element, String newLabel) { - return this.directEdit(element, newLabel, false, (String[]) null); - } - - /** - * Apply the direct edit result (i.e. the newLabel) to the given graphical list item {@link Element}. - * - * @param element - * the given {@link Element}. - * @param newLabel - * the new value to apply. - * @return the given {@link Element}. - */ - public Element directEditListItem(Element element, String newLabel) { - return this.directEdit(element, newLabel, true, (String[]) null); - } - - /** - * Apply the direct edit result (i.e. the newLabel) to the given {@link Element} without changing the name of the - * element itself. - * - * @param element - * the given {@link Element}. - * @param newLabel - * the new value to apply. - * @return the given {@link Element}. - */ - public Element directEditNameOff(Element element, String newLabel) { - return this.directEdit(element, newLabel, LabelService.NAME_OFF); - } - - /** - * Apply the direct edit result (i.e. the newLabel) to the given {@link Element}. - * - * @param element - * the given {@link Element}. - * @param newLabel - * the new value to apply. - * @return the given {@link Element}. - */ - public Element directEdit(Element element, String newLabel, boolean isCompartmentItem, String... options) { - DirectEditLexer lexer = new DirectEditLexer(CharStreams.fromString(newLabel)); - CommonTokenStream tokens = new CommonTokenStream(lexer); - DirectEditParser parser = new DirectEditParser(tokens); - ParseTree tree; - if (element instanceof ConstraintUsage && element.getOwningMembership() instanceof RequirementConstraintMembership && isCompartmentItem) { - // Use the constraint expression parser only if the element is a constraint owned by a requirement, other - // constraints (including requirements) are parsed as regular elements. - tree = parser.constraintExpression(); - } else if (isCompartmentItem) { - tree = parser.listItemExpression(); - } else { - tree = parser.nodeExpression(); - } - ParseTreeWalker walker = new ParseTreeWalker(); - DiagramDirectEditListener listener = new DiagramDirectEditListener(element, this.getFeedbackMessageService(), options); - walker.walk(listener, tree); - listener.resolveProxies().forEach(proxy -> { - this.feedbackMessageService.addFeedbackMessage(new Message(MessageFormat.format("Unable to resolve \u2035{0}\u2035", proxy.nameToResolve()), MessageLevel.WARNING)); - }); - return element; - } - - /** - * Get the value to display when a direct edit has been called on the given {@link Element}. - * - * @param element - * the given {@link Element}. - * @return the value to display. - */ - public String getDefaultInitialDirectEditLabel(Element element) { - StringBuilder builder = new StringBuilder(); - if (element instanceof Usage usage) { - builder.append(this.getBasicNamePrefix(usage)); - } - builder.append(this.getIdentificationLabel(element)); - builder.append(this.getMultiplicityStringRepresentation(element, true)); - builder.append(this.getTypingLabel(element)); - builder.append(this.getRedefinitionLabel(element)); - builder.append(this.getSubsettingLabel(element)); - builder.append(this.getSubclassificationLabel(element)); - if (element instanceof Usage usage) { - builder.append(this.getValueStringRepresentation(usage, true)); - } - return builder.toString(); - } - - /** - * Get the value to display when a direct edit has been called on the given {@link Comment}. - * - * @param comment - * the given {@link Comment}. - * @return the value to display. - */ - public String getDefaultInitialDirectEditLabel(Comment comment) { - return comment.getBody(); - } - - /** - * Get the value to display when a direct edit has been called on the given {@link TextualRepresentation}. - * - * @param textualRepresentation - * the given {@link Comment}. - * @return the value to display. - */ - public String getDefaultInitialDirectEditLabel(TextualRepresentation textualRepresentation) { - return textualRepresentation.getBody(); - } - - /** - * Return the label of the multiplicity part of the given {@link Element}. - * - * @param element - * the given {@link Element}. - * @return the label of the multiplicity part of the given {@link Element} if there is one, an empty string - * otherwise. - */ - public String getMultiplicityLabel(Element element) { - return this.getMultiplicityStringRepresentation(element, false); - } - - /** - * Return the label of the multiplicity part of the given {@link Element}. - * - * @param element - * element the given {@link Element}. - * @param directEditInput - * holds true if the label is used as a direct edit input - * @return the label of the multiplicity part of the given {@link Element} if there is one, an empty string - * otherwise. - */ - public String getMultiplicityStringRepresentation(Element element, boolean directEditInput) { - StringBuilder label = new StringBuilder(); - var optMultiplicityRange = element.getOwnedRelationship().stream() - .filter(OwningMembership.class::isInstance) - .map(OwningMembership.class::cast) - .flatMap(m -> m.getOwnedRelatedElement().stream()) - .filter(MultiplicityRange.class::isInstance) - .map(MultiplicityRange.class::cast) - .findFirst(); - label.append(LabelConstants.SPACE); - if (optMultiplicityRange.isPresent()) { - var range = optMultiplicityRange.get(); - String firstBound = null; - String secondBound = null; - var bounds = range.getOwnedRelationship().stream() - .filter(OwningMembership.class::isInstance) - .map(OwningMembership.class::cast) - .flatMap(m -> m.getOwnedRelatedElement().stream()) - .filter(LiteralExpression.class::isInstance) - .map(LiteralExpression.class::cast) - .toList(); - if (bounds.size() == 1) { - firstBound = this.getSysmlTextualRepresentation(bounds.get(0), directEditInput); - } else if (bounds.size() == 2) { - firstBound = this.getSysmlTextualRepresentation(bounds.get(0), directEditInput); - secondBound = this.getSysmlTextualRepresentation(bounds.get(1), directEditInput); - } - label.append(LabelConstants.OPEN_BRACKET); - if (firstBound != null) { - label.append(firstBound); - } - if (secondBound != null) { - label.append(".."); - label.append(secondBound); - } - label.append(LabelConstants.CLOSE_BRACKET + LabelConstants.SPACE); - } - if (element instanceof Feature feature) { - if (feature.isIsOrdered()) { - label.append(LabelConstants.ORDERED + LabelConstants.SPACE); - } - if (!feature.isIsUnique()) { - label.append(LabelConstants.NON_UNIQUE + LabelConstants.SPACE); - } - } - String labelAsString = label.toString().trim(); - if (!labelAsString.isEmpty()) { - return LabelConstants.SPACE + labelAsString; - } else { - return labelAsString; - } - } - - /** - * Return the label of the prefix part of the given {@link Usage}. - * - * @param usage - * the given {@link Usage}. - * @return the label of the prefix part of the given {@link Usage} if there is one, an empty string otherwise. - */ - public String getBasicNamePrefix(Element element) { - StringBuilder label = new StringBuilder(); - if (element instanceof Usage usage) { - if (usage.isIsVariation()) { - label.append(LabelConstants.VARIATION + LabelConstants.SPACE); - } - } else if (element instanceof Definition definition) { - if (definition.isIsVariation()) { - label.append(LabelConstants.VARIATION + LabelConstants.SPACE); - } - } - EObject membership = element.getOwningMembership(); - if (membership != null) { - EObject parent = membership.eContainer(); - boolean hasVariationParent = (parent instanceof Definition && ((Definition) parent).isIsVariation()) | (parent instanceof Usage && ((Usage) parent).isIsVariation()); - if (membership instanceof VariantMembership | hasVariationParent) { - label.append(LabelConstants.VARIANT); - } - } - if (element instanceof Type type && type.isIsAbstract()) { - label.append(LabelConstants.ABSTRACT + LabelConstants.SPACE); - } - if (element instanceof Usage usage) { - this.getReferenceUsagePrefix(usage, label); - } - return label.toString(); - } - - /** - * Return the label of the prefix part of the given {@link Usage}. - * - * @param usage - * the given {@link Usage}. - * @return the label of the prefix part of the given {@link Usage} if there is one, an empty string otherwise. - */ - public String getUsageListItemPrefix(Usage usage) { - StringBuilder label = new StringBuilder(); - if (usage.getDirection() == FeatureDirectionKind.IN) { - label.append(LabelConstants.IN + LabelConstants.SPACE); - } else if (usage.getDirection() == FeatureDirectionKind.OUT) { - label.append(LabelConstants.OUT + LabelConstants.SPACE); - } else if (usage.getDirection() == FeatureDirectionKind.INOUT) { - label.append(LabelConstants.INOUT + LabelConstants.SPACE); - } - if (usage.isIsAbstract() && !usage.isIsVariation()) { - label.append(LabelConstants.ABSTRACT + LabelConstants.SPACE); - } - if (usage.isIsVariation()) { - label.append(LabelConstants.VARIATION + LabelConstants.SPACE); - } - if (usage.isIsConstant()) { - label.append(LabelConstants.CONSTANT + LabelConstants.SPACE); - } - if (usage.isIsDerived()) { - label.append(LabelConstants.DERIVED + LabelConstants.SPACE); - } - if (usage.isIsEnd()) { - label.append(LabelConstants.END + LabelConstants.SPACE); - } - this.getReferenceUsagePrefix(usage, label); - return label.toString(); - } - - /** - * Get the SysML textual representation of the given element. - * - * @param element - * the element to convert to textual format - * @param resolvableName - * holds true to compute resolvable names for references, otherwise simple name are used to - * reference an element. - * @return a textual representation or null if none - */ - public String getSysmlTextualRepresentation(Element element, boolean resolvableName) { - return this.buildSerializer(resolvableName).doSwitch(element); - } - - /** - * Return the label of the value part of the given {@link Usage}. - * - * @param usage - * the given {@link Usage}. - * @return the label of the value part of the given {@link Usage} if there is one, an empty string otherwise. - */ - public String getValueLabel(Usage usage) { - return this.getValueStringRepresentation(usage, false); - } - - /** - * Return the label of the value part of the given {@link Usage}. - * - * @param usage - * the given {@link Usage}. - * @param directEditInput - * if the label is being used as direct edit input - * @return the label of the value part of the given {@link Usage} if there is one, an empty string otherwise. - */ - public String getValueStringRepresentation(Usage usage, boolean directEditInput) { - StringBuilder label = new StringBuilder(); - var featureValue = usage.getOwnedRelationship().stream() - .filter(FeatureValue.class::isInstance) - .map(FeatureValue.class::cast) - .findFirst(); - if (featureValue.isPresent()) { - var expression = featureValue.get().getValue(); - String valueAsString = null; - if (expression != null) { - valueAsString = this.getSysmlTextualRepresentation(expression, directEditInput); - } - - if (featureValue.get().isIsDefault()) { - label - .append(LabelConstants.SPACE) - .append(LabelConstants.DEFAULT); - } - label - .append(LabelConstants.SPACE) - .append(this.getFeatureValueRelationshipSymbol(featureValue.get())) - .append(LabelConstants.SPACE) - .append(valueAsString); - } - return label.toString(); - } - - /** - * Return the label of the reference prefix part of the given {@link Usage}. - * - * @param usage - * the given {@link Usage}. - * @return the label of the reference prefix part of the given {@link Usage} if there is one, an empty string - * otherwise. - */ - private void getReferenceUsagePrefix(Usage usage, StringBuilder label) { - if (usage.isIsReference() && !(usage instanceof AttributeUsage)) { - // AttributeUsage are always referential, so no need to add the ref keyword - label.append(LabelConstants.REF + LabelConstants.SPACE); - } - } - - /** - * Returns the identification of the provided {@code element}. - *

- * The identification of an element is the concatenation of its short name and its declared name. - *

- * - * @param element - * the element to get the identification from - * @return the identification - */ - protected String getIdentificationLabel(Element element) { - StringBuilder label = new StringBuilder(); - if (element instanceof ActionUsage && element.eContainer() instanceof StateSubactionMembership ssm) { - label.append(ssm.getKind()); - } - label.append(this.getShortNameLabel(element)); - String declaredName = element.getDeclaredName(); - if (declaredName != null) { - label.append(declaredName); - } - return label.toString(); - } - - /** - * Returns the label for the short name of the given {@code element}. - * - * @param element - * the element to compute the short name label from - * @return the label for the short name of the given {@code element} - */ - protected String getShortNameLabel(Element element) { - StringBuilder label = new StringBuilder(); - String declaredShortName = element.getDeclaredShortName(); - if (declaredShortName != null && !declaredShortName.isBlank()) { - label.append(LabelConstants.LESSER_THAN) - .append(declaredShortName) - .append(LabelConstants.GREATER_THAN) - .append(LabelConstants.SPACE); - } - return label.toString(); - } - - /** - * Return the label of the typing part of the given {@link Element}. - * - * @param usage - * the given {@link Element}. - * @return the label of the typing part of the given {@link Element} if there is one, an empty string otherwise. - */ - protected String getTypingLabel(Element element) { - StringBuilder label = new StringBuilder(); - var optFeatureTyping = element.getOwnedRelationship().stream() - .filter(FeatureTyping.class::isInstance) - .map(FeatureTyping.class::cast) - .findFirst(); - if (optFeatureTyping.isPresent()) { - FeatureTyping featureTyping = optFeatureTyping.get(); - if (!featureTyping.isIsImplied()) { - var type = featureTyping.getType(); - if (type != null) { - label.append(LabelConstants.SPACE); - label.append(LabelConstants.COLON); - label.append(LabelConstants.SPACE); - label.append(this.getDeclaredNameLabel(type)); - } - } - } - return label.toString(); - } - - /** - * Return the label of the subclassification of the given {@link Element}. - * - * @param usage - * the given {@link Element}. - * @return the label of the subclassification of the given {@link Element} if there is one, an empty string - * otherwise. - */ - protected String getSubclassificationLabel(Element element) { - StringBuilder label = new StringBuilder(); - var optSubclassification = element.getOwnedRelationship().stream() - .filter(Subclassification.class::isInstance) - .map(Subclassification.class::cast) - .findFirst(); - if (optSubclassification.isPresent()) { - Subclassification subclassification = optSubclassification.get(); - if (!subclassification.isIsImplied()) { - var superclassifier = subclassification.getSuperclassifier(); - if (superclassifier != null) { - label.append(LabelConstants.SPACE); - label.append(LabelConstants.SUBCLASSIFICATION); - label.append(LabelConstants.SPACE); - label.append(this.getDeclaredNameLabel(superclassifier)); - } - } - } - return label.toString(); - } - - /** - * Return the label of the subsetting of the given {@link Element}. - * - * @param usage - * the given {@link Element}. - * @return the label of the subsetting of the given {@link Element} if there is one, an empty string otherwise. - */ - protected String getSubsettingLabel(Element element) { - StringBuilder label = new StringBuilder(); - var optSubsetting = element.getOwnedRelationship().stream() - .filter(r -> r instanceof Subsetting && !(r instanceof Redefinition)) - .map(Subsetting.class::cast) - .findFirst(); - if (optSubsetting.isPresent()) { - Subsetting subsetting = optSubsetting.get(); - if (!subsetting.isIsImplied()) { - var subsettedFeature = subsetting.getSubsettedFeature(); - if (subsettedFeature != null) { - label.append(LabelConstants.SPACE); - label.append(LabelConstants.SUBSETTING); - label.append(LabelConstants.SPACE); - label.append(this.getDeclaredNameLabel(subsettedFeature)); - } - } - } - return label.toString(); - } - - /** - * Return the label of the redefinition of the given {@link Element}. - * - * @param element - * the given {@link Element}. - * @return the label of the redefinition of the given {@link Element} if there is one, an empty string otherwise. - */ - protected String getRedefinitionLabel(Element element) { - StringBuilder label = new StringBuilder(); - var optRedefinition = element.getOwnedRelationship().stream() - .filter(Redefinition.class::isInstance) - .map(Redefinition.class::cast) - .findFirst(); - if (optRedefinition.isPresent()) { - Redefinition redefinition = optRedefinition.get(); - if (!redefinition.isIsImplied()) { - var redefinedFeature = redefinition.getRedefinedFeature(); - if (redefinedFeature != null) { - label.append(LabelConstants.SPACE); - label.append(LabelConstants.REDEFINITION); - label.append(LabelConstants.SPACE); - label.append(this.getDeclaredNameLabel(redefinedFeature)); - } - } - } - return label.toString(); - } - - /** - * Builds a SysMLSerializer. - * - * @param resolvableName - * holds true to compute resolvable names for references, otherwise simple name are used to - * reference an element. - * @return a new {@link SysMLElementSerializer}. - */ - protected SysMLElementSerializer buildSerializer(boolean resolvableName) { - final INameDeresolver nameDeresolver; - if (resolvableName) { - nameDeresolver = new FileNameDeresolver(); - } else { - nameDeresolver = new SimpleNameDeresolver(); - } - return new SysMLElementSerializer("\n", " ", nameDeresolver, s -> { - this.logger.info(s.message()); - }); - } - - private String getFeatureValueRelationshipSymbol(FeatureValue featureValueMembership) { - final String affectationSymbole; - - if (featureValueMembership.isIsInitial()) { - affectationSymbole = LabelConstants.COLON_EQUAL; - } else { - affectationSymbole = LabelConstants.EQUAL; - } - return affectationSymbole; - } - - protected String getDeclaredNameLabel(Element element) { - var label = element.getName(); - if (ElementUtil.isFromStandardLibrary(element)) { - label = element.getQualifiedName(); - } - return label; - - } } diff --git a/backend/services/syson-services/src/test/java/org/eclipse/syson/services/LabelServiceTest.java b/backend/services/syson-services/src/test/java/org/eclipse/syson/services/LabelServiceTest.java deleted file mode 100644 index e18e63a45..000000000 --- a/backend/services/syson-services/src/test/java/org/eclipse/syson/services/LabelServiceTest.java +++ /dev/null @@ -1,148 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2024, 2025 Obeo. - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * Obeo - initial API and implementation - *******************************************************************************/ -package org.eclipse.syson.services; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.eclipse.sirius.components.core.api.IFeedbackMessageService; -import org.eclipse.syson.sysml.AttributeUsage; -import org.eclipse.syson.sysml.FeatureDirectionKind; -import org.eclipse.syson.sysml.SysmlFactory; -import org.eclipse.syson.sysml.Usage; -import org.eclipse.syson.sysml.helper.LabelConstants; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -/** - * Label services tests. - * - * @author jmallet - */ -public class LabelServiceTest extends AbstractServiceTest { - - private static final String ATTRIBUTE_USAGE_NAME = "myAttributeUsage"; - - private static final String ATTRIBUTE_USAGE_SHORT_NAME = "shortName"; - - private LabelService labelService; - - @BeforeEach - void beforeEach() { - this.labelService = new LabelService(new IFeedbackMessageService.NoOp()); - } - - @DisplayName("Check Attribute label with name and short name") - @Test - void testAttributeLabelWithNameAndShortName() { - AttributeUsage attributeUsage = SysmlFactory.eINSTANCE.createAttributeUsage(); - attributeUsage.setDeclaredName(ATTRIBUTE_USAGE_NAME); - attributeUsage.setDeclaredShortName(ATTRIBUTE_USAGE_SHORT_NAME); - StringBuilder expectedLabel = new StringBuilder(); - expectedLabel.append(LabelConstants.OPEN_QUOTE) - .append("attribute") - .append(LabelConstants.CLOSE_QUOTE) - .append(LabelConstants.CR) - .append(LabelConstants.LESSER_THAN) - .append(ATTRIBUTE_USAGE_SHORT_NAME) - .append(LabelConstants.GREATER_THAN) - .append(LabelConstants.SPACE) - .append(ATTRIBUTE_USAGE_NAME); - assertEquals(expectedLabel.toString(), this.labelService.getContainerLabel(attributeUsage)); - } - - @DisplayName("Check Usage prefix label with default properties") - @Test - void testUsagePrefixLabelWithDefaultProperties() { - Usage usageWithNoDirection = SysmlFactory.eINSTANCE.createUsage(); - assertEquals(LabelConstants.REF + LabelConstants.SPACE, this.labelService.getUsageListItemPrefix(usageWithNoDirection)); - } - - @DisplayName("Check Usage prefix label with abstract property") - @Test - void testUsagePrefixLabelWithAbstractProperty() { - Usage usageWithNoDirection = SysmlFactory.eINSTANCE.createUsage(); - usageWithNoDirection.setIsAbstract(true); - assertEquals(LabelConstants.ABSTRACT + LabelConstants.SPACE + LabelConstants.REF + LabelConstants.SPACE, this.labelService.getUsageListItemPrefix(usageWithNoDirection)); - } - - @DisplayName("Check Usage prefix label with derived property") - @Test - void testUsagePrefixLabelWithDerivedProperty() { - Usage usageWithNoDirection = SysmlFactory.eINSTANCE.createUsage(); - usageWithNoDirection.setIsDerived(true); - assertEquals(LabelConstants.DERIVED + LabelConstants.SPACE + LabelConstants.REF + LabelConstants.SPACE, this.labelService.getUsageListItemPrefix(usageWithNoDirection)); - } - - @DisplayName("Check Usage prefix label with end property") - @Test - void testUsagePrefixLabelWithEndProperty() { - Usage usageWithNoDirection = SysmlFactory.eINSTANCE.createUsage(); - usageWithNoDirection.setIsEnd(true); - assertEquals(LabelConstants.END + LabelConstants.SPACE + LabelConstants.REF + LabelConstants.SPACE, this.labelService.getUsageListItemPrefix(usageWithNoDirection)); - } - - @DisplayName("Check Usage prefix label with constant property") - @Test - void testUsagePrefixLabelWithConstantProperty() { - Usage usageWithNoDirection = SysmlFactory.eINSTANCE.createUsage(); - usageWithNoDirection.setIsConstant(true); - assertEquals(LabelConstants.CONSTANT + LabelConstants.SPACE + LabelConstants.REF + LabelConstants.SPACE, this.labelService.getUsageListItemPrefix(usageWithNoDirection)); - } - - @DisplayName("Check Usage prefix label with readonly property") - @Test - void testUsagePrefixLabelWithVariationProperty() { - Usage usageWithNoDirection = SysmlFactory.eINSTANCE.createUsage(); - usageWithNoDirection.setIsVariation(true); - assertEquals(LabelConstants.VARIATION + LabelConstants.SPACE + LabelConstants.REF + LabelConstants.SPACE, this.labelService.getUsageListItemPrefix(usageWithNoDirection)); - } - - @DisplayName("Check Usage prefix label with many custom properties") - @Test - void testUsagePrefixLabelWithManyCustomProperties() { - Usage usageWithNoDirection = SysmlFactory.eINSTANCE.createUsage(); - usageWithNoDirection.setIsAbstract(true); - usageWithNoDirection.setIsDerived(true); - usageWithNoDirection.setIsEnd(true); - usageWithNoDirection.setIsConstant(true); - usageWithNoDirection.setIsVariation(true); - - assertEquals(LabelConstants.VARIATION + LabelConstants.SPACE + LabelConstants.CONSTANT + LabelConstants.SPACE - + LabelConstants.DERIVED + LabelConstants.SPACE - + LabelConstants.END - + LabelConstants.SPACE + LabelConstants.REF + LabelConstants.SPACE, this.labelService.getUsageListItemPrefix(usageWithNoDirection)); - } - - @DisplayName("Check Usage prefix label with direction properties") - @Test - void testUsagePrefixLabelWithDirectionProperties() { - Usage usageWithDirection = SysmlFactory.eINSTANCE.createUsage(); - - usageWithDirection.setDirection(FeatureDirectionKind.IN); - assertEquals(LabelConstants.IN + LabelConstants.SPACE + LabelConstants.REF + LabelConstants.SPACE, this.labelService.getUsageListItemPrefix(usageWithDirection)); - - usageWithDirection.setDirection(FeatureDirectionKind.OUT); - assertEquals(LabelConstants.OUT + LabelConstants.SPACE + LabelConstants.REF + LabelConstants.SPACE, this.labelService.getUsageListItemPrefix(usageWithDirection)); - - usageWithDirection.setDirection(FeatureDirectionKind.INOUT); - assertEquals(LabelConstants.INOUT + LabelConstants.SPACE + LabelConstants.REF + LabelConstants.SPACE, this.labelService.getUsageListItemPrefix(usageWithDirection)); - } - - @DisplayName("Check AttributeUsage prefix label with default properties") - @Test - void testAttributeUsagePrefixLabelWithDefaultProperties() { - AttributeUsage attributeUsageWithNoDirection = SysmlFactory.eINSTANCE.createAttributeUsage(); - assertEquals("", this.labelService.getUsageListItemPrefix(attributeUsageWithNoDirection)); - } -} diff --git a/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/ElementQueryService.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/MetamodelElementQueryService.java similarity index 97% rename from backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/ElementQueryService.java rename to backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/MetamodelElementQueryService.java index 85af19fe9..bc615e3c4 100644 --- a/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/ElementQueryService.java +++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/MetamodelElementQueryService.java @@ -21,7 +21,7 @@ * * @author arichard */ -public class ElementQueryService { +public class MetamodelElementQueryService { /** * Returns {@code true} if the provided {@code element} is an actor, {@code false} otherwise. diff --git a/backend/views/syson-diagram-common-view/pom.xml b/backend/views/syson-diagram-common-view/pom.xml index d9df22d2f..90330defa 100644 --- a/backend/views/syson-diagram-common-view/pom.xml +++ b/backend/views/syson-diagram-common-view/pom.xml @@ -95,6 +95,11 @@ syson-model-services 2025.10.1
+ + org.eclipse.syson + syson-diagram-services + 2025.10.1 + org.testcontainers diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractBindingConnectorAsUsageEdgeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractBindingConnectorAsUsageEdgeDescriptionProvider.java index 88a767bd4..6f4a4f71d 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractBindingConnectorAsUsageEdgeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractBindingConnectorAsUsageEdgeDescriptionProvider.java @@ -27,9 +27,12 @@ import org.eclipse.sirius.components.view.diagram.LineStyle; import org.eclipse.sirius.components.view.diagram.NodeDescription; import org.eclipse.sirius.components.view.diagram.SynchronizationPolicy; +import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; +import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService; import org.eclipse.syson.sysml.BindingConnectorAsUsage; import org.eclipse.syson.sysml.SysmlPackage; import org.eclipse.syson.util.AQLConstants; +import org.eclipse.syson.util.ServiceMethod; import org.eclipse.syson.util.SysMLMetamodelHelper; import org.eclipse.syson.util.ViewConstants; @@ -120,11 +123,11 @@ private EdgeStyle createEdgeStyle() { @Override protected LabelEditTool getEdgeEditTool() { var callEditService = this.viewBuilderHelper.newChangeContext() - .expression(AQLConstants.AQL_SELF + ".editEdgeCenterLabel(newLabel)"); + .expression(ServiceMethod.of1(DiagramMutationAQLService::editEdgeCenterLabel).aqlSelf("newLabel")); return this.diagramBuilderHelper.newLabelEditTool() .name("Edit") - .initialDirectEditLabelExpression(AQLConstants.AQL_SELF + ".getDefaultInitialDirectEditLabel()") + .initialDirectEditLabelExpression(ServiceMethod.of0(DiagramQueryAQLService::getDefaultInitialDirectEditLabel).aqlSelf()) .body(callEditService.build()) .build(); } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractDefinitionOwnedUsageEdgeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractDefinitionOwnedUsageEdgeDescriptionProvider.java index db95b0324..8aecfda89 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractDefinitionOwnedUsageEdgeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractDefinitionOwnedUsageEdgeDescriptionProvider.java @@ -30,9 +30,12 @@ import org.eclipse.sirius.components.view.diagram.LineStyle; import org.eclipse.sirius.components.view.diagram.NodeDescription; import org.eclipse.sirius.components.view.diagram.SynchronizationPolicy; +import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; +import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService; import org.eclipse.syson.util.AQLConstants; import org.eclipse.syson.util.AQLUtils; import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ServiceMethod; import org.eclipse.syson.util.SysMLMetamodelHelper; import org.eclipse.syson.util.ViewConstants; @@ -70,7 +73,7 @@ public EdgeDescription create() { return this.diagramBuilderHelper.newEdgeDescription() .domainType(domainType) .isDomainBasedEdge(false) - .centerLabelExpression(AQLConstants.AQL + org.eclipse.sirius.components.diagrams.description.EdgeDescription.SEMANTIC_EDGE_TARGET + ".getMultiplicityLabel()") + .centerLabelExpression(ServiceMethod.of0(DiagramQueryAQLService::getMultiplicityLabel).aql(org.eclipse.sirius.components.diagrams.description.EdgeDescription.SEMANTIC_EDGE_TARGET)) .name(this.descriptionNameGenerator.getEdgeName("Definition Owned " + this.eClass.getName())) .sourceExpression(AQLConstants.AQL_SELF) .style(this.createEdgeStyle()) @@ -118,12 +121,13 @@ protected DeleteTool getEdgeDeleteTool() { @Override protected LabelEditTool getEdgeEditTool() { var callEditService = this.viewBuilderHelper.newChangeContext() - .expression(AQLConstants.AQL + org.eclipse.sirius.components.diagrams.description.EdgeDescription.SEMANTIC_EDGE_TARGET + ".editMultiplicityRangeCenterLabel(newLabel)"); + .expression(ServiceMethod.of1(DiagramMutationAQLService::editMultiplicityRangeCenterLabel).aql(org.eclipse.sirius.components.diagrams.description.EdgeDescription.SEMANTIC_EDGE_TARGET, + "newLabel")); return this.diagramBuilderHelper.newLabelEditTool() .name("Edit") .initialDirectEditLabelExpression( - AQLConstants.AQL + org.eclipse.sirius.components.diagrams.description.EdgeDescription.SEMANTIC_EDGE_TARGET + ".getMultiplicityRangeInitialDirectEditLabel()") + ServiceMethod.of0(DiagramQueryAQLService::getMultiplicityLabel).aql(org.eclipse.sirius.components.diagrams.description.EdgeDescription.SEMANTIC_EDGE_TARGET)) .body(callEditService.build()).build(); } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractDependencyEdgeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractDependencyEdgeDescriptionProvider.java index 01268b27b..b6b0612e7 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractDependencyEdgeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractDependencyEdgeDescriptionProvider.java @@ -26,11 +26,14 @@ import org.eclipse.sirius.components.view.diagram.LineStyle; import org.eclipse.sirius.components.view.diagram.NodeDescription; import org.eclipse.sirius.components.view.diagram.SynchronizationPolicy; +import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; +import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService; import org.eclipse.syson.sysml.Dependency; import org.eclipse.syson.sysml.SysmlPackage; import org.eclipse.syson.util.AQLConstants; import org.eclipse.syson.util.AQLUtils; import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ServiceMethod; import org.eclipse.syson.util.SysMLMetamodelHelper; import org.eclipse.syson.util.ViewConstants; @@ -85,7 +88,7 @@ public EdgeDescription create() { return this.diagramBuilderHelper.newEdgeDescription() .domainType(domainType) .isDomainBasedEdge(true) - .centerLabelExpression(AQLUtils.getSelfServiceCallExpression("getDependencyLabel")) + .centerLabelExpression(ServiceMethod.of0(DiagramQueryAQLService::getDependencyLabel).aqlSelf()) .name(this.getName()) .semanticCandidatesExpression(AQLUtils.getSelfServiceCallExpression("getAllReachable", domainType)) .sourceExpression(AQLConstants.AQL_SELF + "." + SysmlPackage.eINSTANCE.getDependency_Client().getName()) @@ -121,7 +124,7 @@ private EdgeStyle createEdgeStyle() { @Override protected LabelEditTool getEdgeEditTool() { var changeContext = this.viewBuilderHelper.newChangeContext() - .expression(AQLUtils.getSelfServiceCallExpression("directEdit", "newLabel")) + .expression(ServiceMethod.of1(DiagramMutationAQLService::directEdit).aqlSelf("newLabel")) .build(); return this.diagramBuilderHelper.newLabelEditTool() diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractFlowUsageEdgeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractFlowUsageEdgeDescriptionProvider.java index 28f585dfa..d67f3440b 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractFlowUsageEdgeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractFlowUsageEdgeDescriptionProvider.java @@ -27,10 +27,13 @@ import org.eclipse.sirius.components.view.diagram.LineStyle; import org.eclipse.sirius.components.view.diagram.NodeDescription; import org.eclipse.sirius.components.view.diagram.SynchronizationPolicy; +import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; +import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService; import org.eclipse.syson.sysml.FlowUsage; import org.eclipse.syson.sysml.SysmlPackage; import org.eclipse.syson.util.AQLConstants; import org.eclipse.syson.util.AQLUtils; +import org.eclipse.syson.util.ServiceMethod; import org.eclipse.syson.util.SysMLMetamodelHelper; import org.eclipse.syson.util.ViewConstants; @@ -82,7 +85,7 @@ public EdgeDescription create() { return this.diagramBuilderHelper.newEdgeDescription() .domainType(domainType) .isDomainBasedEdge(true) - .centerLabelExpression(AQLUtils.getSelfServiceCallExpression("getEdgeLabel")) + .centerLabelExpression(ServiceMethod.of0(DiagramQueryAQLService::getEdgeLabel).aqlSelf()) .name(this.getName()) .semanticCandidatesExpression(AQLUtils.getSelfServiceCallExpression("getAllReachable", domainType)) .sourceExpression("aql:self.sourceOutputFeature.unwrapFeature()") @@ -121,11 +124,11 @@ private EdgeStyle createEdgeStyle() { @Override protected LabelEditTool getEdgeEditTool() { var callEditService = this.viewBuilderHelper.newChangeContext() - .expression(AQLUtils.getSelfServiceCallExpression("editEdgeCenterLabel", "newLabel")); + .expression(ServiceMethod.of1(DiagramMutationAQLService::editEdgeCenterLabel).aqlSelf("newLabel")); return this.diagramBuilderHelper.newLabelEditTool() .name("Edit") - .initialDirectEditLabelExpression(AQLUtils.getSelfServiceCallExpression("getDefaultInitialDirectEditLabel")) + .initialDirectEditLabelExpression(ServiceMethod.of0(DiagramQueryAQLService::getDefaultInitialDirectEditLabel).aqlSelf()) .body(callEditService.build()) .build(); } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractInterfaceUsageEdgeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractInterfaceUsageEdgeDescriptionProvider.java index f49df2295..0546940b4 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractInterfaceUsageEdgeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractInterfaceUsageEdgeDescriptionProvider.java @@ -27,9 +27,12 @@ import org.eclipse.sirius.components.view.diagram.LineStyle; import org.eclipse.sirius.components.view.diagram.NodeDescription; import org.eclipse.sirius.components.view.diagram.SynchronizationPolicy; +import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; +import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService; import org.eclipse.syson.sysml.InterfaceUsage; import org.eclipse.syson.sysml.SysmlPackage; import org.eclipse.syson.util.AQLConstants; +import org.eclipse.syson.util.ServiceMethod; import org.eclipse.syson.util.SysMLMetamodelHelper; import org.eclipse.syson.util.ViewConstants; @@ -81,7 +84,7 @@ public EdgeDescription create() { return this.diagramBuilderHelper.newEdgeDescription() .domainType(domainType) .isDomainBasedEdge(true) - .centerLabelExpression("aql:self.getEdgeLabel()") + .centerLabelExpression(ServiceMethod.of0(DiagramQueryAQLService::getEdgeLabel).aqlSelf()) .name(this.getName()) .semanticCandidatesExpression("aql:self.getAllReachable(" + domainType + ")") .sourceExpression("aql:self.getSource()") @@ -119,11 +122,11 @@ private EdgeStyle createEdgeStyle() { @Override protected LabelEditTool getEdgeEditTool() { var callEditService = this.viewBuilderHelper.newChangeContext() - .expression(AQLConstants.AQL_SELF + ".editEdgeCenterLabel(newLabel)"); + .expression(ServiceMethod.of1(DiagramMutationAQLService::editEdgeCenterLabel).aqlSelf("newLabel")); return this.diagramBuilderHelper.newLabelEditTool() .name("Edit") - .initialDirectEditLabelExpression(AQLConstants.AQL_SELF + ".getDefaultInitialDirectEditLabel()") + .initialDirectEditLabelExpression(ServiceMethod.of0(DiagramQueryAQLService::getDefaultInitialDirectEditLabel).aqlSelf()) .body(callEditService.build()) .build(); } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractSuccessionEdgeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractSuccessionEdgeDescriptionProvider.java index 814abcf28..e0104d498 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractSuccessionEdgeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractSuccessionEdgeDescriptionProvider.java @@ -25,10 +25,13 @@ import org.eclipse.sirius.components.view.diagram.LineStyle; import org.eclipse.sirius.components.view.diagram.NodeDescription; import org.eclipse.sirius.components.view.diagram.SynchronizationPolicy; +import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; +import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService; import org.eclipse.syson.sysml.SysmlPackage; import org.eclipse.syson.util.AQLConstants; import org.eclipse.syson.util.AQLUtils; import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ServiceMethod; import org.eclipse.syson.util.SysMLMetamodelHelper; import org.eclipse.syson.util.ViewConstants; @@ -73,7 +76,7 @@ public EdgeDescription create() { .domainType(domainType) .preconditionExpression("aql:graphicalEdgeSource.isInSameGraphicalContainer(graphicalEdgeTarget,cache) and not self.getOwningElement().oclIsKindOf(sysml::TransitionUsage)") .isDomainBasedEdge(true) - .centerLabelExpression(AQLUtils.getSelfServiceCallExpression("getEdgeLabel")) + .centerLabelExpression(ServiceMethod.of0(DiagramQueryAQLService::getEdgeLabel).aqlSelf()) .name(this.descriptionNameGenerator.getEdgeName(SysmlPackage.eINSTANCE.getSuccession())) .semanticCandidatesExpression(AQLUtils.getSelfServiceCallExpression("getAllReachable", domainType)) .style(this.createEdgeStyle()) @@ -110,7 +113,7 @@ protected ChangeContextBuilder getTargetReconnectToolBody() { @Override protected LabelEditTool getEdgeEditTool() { var changeContext = this.viewBuilderHelper.newChangeContext() - .expression(AQLUtils.getSelfServiceCallExpression("directEdit", "newLabel")) + .expression(ServiceMethod.of1(DiagramMutationAQLService::directEdit).aqlSelf("newLabel")) .build(); return this.diagramBuilderHelper.newLabelEditTool() diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractUsageNestedUsageEdgeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractUsageNestedUsageEdgeDescriptionProvider.java index 79dfd8009..60b4a4ade 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractUsageNestedUsageEdgeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractUsageNestedUsageEdgeDescriptionProvider.java @@ -31,8 +31,11 @@ import org.eclipse.sirius.components.view.diagram.LineStyle; import org.eclipse.sirius.components.view.diagram.NodeDescription; import org.eclipse.sirius.components.view.diagram.SynchronizationPolicy; +import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; +import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService; import org.eclipse.syson.util.AQLConstants; import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ServiceMethod; import org.eclipse.syson.util.SysMLMetamodelHelper; import org.eclipse.syson.util.ViewConstants; @@ -145,12 +148,13 @@ protected DeleteTool getEdgeDeleteTool() { @Override protected LabelEditTool getEdgeEditTool() { var callEditService = this.viewBuilderHelper.newChangeContext() - .expression(AQLConstants.AQL + org.eclipse.sirius.components.diagrams.description.EdgeDescription.SEMANTIC_EDGE_TARGET + ".editMultiplicityRangeCenterLabel(newLabel)"); + .expression(ServiceMethod.of1(DiagramMutationAQLService::editMultiplicityRangeCenterLabel).aql(org.eclipse.sirius.components.diagrams.description.EdgeDescription.SEMANTIC_EDGE_TARGET, + "newLabel")); return this.diagramBuilderHelper.newLabelEditTool() .name("Edit") .initialDirectEditLabelExpression( - AQLConstants.AQL + org.eclipse.sirius.components.diagrams.description.EdgeDescription.SEMANTIC_EDGE_TARGET + ".getMultiplicityRangeInitialDirectEditLabel()") + ServiceMethod.of0(DiagramQueryAQLService::getMultiplicityLabel).aql(org.eclipse.sirius.components.diagrams.description.EdgeDescription.SEMANTIC_EDGE_TARGET)) .body(callEditService.build()).build(); } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractControlNodeActionNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractControlNodeActionNodeDescriptionProvider.java index e28a4824f..b4191e73c 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractControlNodeActionNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractControlNodeActionNodeDescriptionProvider.java @@ -31,9 +31,12 @@ import org.eclipse.sirius.components.view.diagram.SynchronizationPolicy; import org.eclipse.sirius.components.view.diagram.UserResizableDirection; import org.eclipse.syson.diagram.common.view.services.ViewEdgeToolSwitch; +import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; +import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService; import org.eclipse.syson.util.AQLConstants; import org.eclipse.syson.util.AQLUtils; import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ServiceMethod; import org.eclipse.syson.util.SysMLMetamodelHelper; import org.eclipse.syson.util.ViewConstants; @@ -143,11 +146,11 @@ private NodePalette createNodePalette(IViewDiagramElementFinder cache) { .body(changeContext.build()); var callEditService = this.viewBuilderHelper.newChangeContext() - .expression(AQLUtils.getSelfServiceCallExpression("directEditNode", "newLabel")); + .expression(ServiceMethod.of1(DiagramMutationAQLService::directEditNode).aqlSelf("newLabel")); var editTool = this.diagramBuilderHelper.newLabelEditTool() .name("Edit") - .initialDirectEditLabelExpression(AQLUtils.getSelfServiceCallExpression("getDefaultInitialDirectEditLabel")) + .initialDirectEditLabelExpression(ServiceMethod.of0(DiagramQueryAQLService::getDefaultInitialDirectEditLabel).aqlSelf()) .body(callEditService.build()); var edgeTools = new ArrayList(); diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractDefinitionNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractDefinitionNodeDescriptionProvider.java index 98d817f66..8294fc901 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractDefinitionNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractDefinitionNodeDescriptionProvider.java @@ -43,9 +43,12 @@ import org.eclipse.sirius.components.view.diagram.UserResizableDirection; import org.eclipse.sirius.components.view.emf.diagram.ViewDiagramDescriptionConverter; import org.eclipse.syson.diagram.common.view.services.ViewEdgeToolSwitch; +import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; +import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService; import org.eclipse.syson.services.UtilService; import org.eclipse.syson.util.AQLUtils; import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ServiceMethod; import org.eclipse.syson.util.SysMLMetamodelHelper; import org.eclipse.syson.util.ViewConstants; @@ -182,7 +185,7 @@ protected List getDroppableNodes(IViewDiagramElementFinder cach protected InsideLabelDescription createInsideLabelDescription() { return this.diagramBuilderHelper.newInsideLabelDescription() - .labelExpression(AQLUtils.getSelfServiceCallExpression("getContainerLabel")) + .labelExpression(ServiceMethod.of0(DiagramQueryAQLService::getContainerLabel).aqlSelf()) .position(InsideLabelPosition.TOP_CENTER) .style(this.createInsideLabelStyle()) .textAlign(LabelTextAlign.CENTER) @@ -223,11 +226,11 @@ protected NodePalette createNodePalette(NodeDescription nodeDescription, IViewDi .body(changeContext.build()); var callEditService = this.viewBuilderHelper.newChangeContext() - .expression(AQLUtils.getSelfServiceCallExpression("directEditNode", "newLabel")); + .expression(ServiceMethod.of1(DiagramMutationAQLService::directEditNode).aqlSelf("newLabel")); var editTool = this.diagramBuilderHelper.newLabelEditTool() .name("Edit") - .initialDirectEditLabelExpression(AQLUtils.getSelfServiceCallExpression("getDefaultInitialDirectEditLabel")) + .initialDirectEditLabelExpression(ServiceMethod.of0(DiagramQueryAQLService::getDefaultInitialDirectEditLabel).aqlSelf()) .body(callEditService.build()); var edgeTools = new ArrayList(); diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractItemUsageBorderNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractItemUsageBorderNodeDescriptionProvider.java index d6396b169..41fbd6c62 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractItemUsageBorderNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractItemUsageBorderNodeDescriptionProvider.java @@ -29,9 +29,12 @@ import org.eclipse.sirius.components.view.diagram.OutsideLabelStyle; import org.eclipse.sirius.components.view.diagram.SynchronizationPolicy; import org.eclipse.sirius.components.view.diagram.UserResizableDirection; +import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; +import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService; import org.eclipse.syson.sysml.SysmlPackage; import org.eclipse.syson.util.AQLUtils; import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ServiceMethod; import org.eclipse.syson.util.SysMLMetamodelHelper; import org.eclipse.syson.util.ViewConstants; @@ -137,11 +140,11 @@ private NodePalette createNodePalette(IViewDiagramElementFinder cache, NodeDescr .body(changeContext.build()); var callEditService = this.viewBuilderHelper.newChangeContext() - .expression(AQLUtils.getSelfServiceCallExpression("directEdit", "newLabel")); + .expression(ServiceMethod.of1(DiagramMutationAQLService::directEdit).aqlSelf("newLabel")); var editTool = this.diagramBuilderHelper.newLabelEditTool() .name("Edit") - .initialDirectEditLabelExpression(AQLUtils.getSelfServiceCallExpression("getDefaultInitialDirectEditLabel")) + .initialDirectEditLabelExpression(ServiceMethod.of0(DiagramQueryAQLService::getDefaultInitialDirectEditLabel).aqlSelf()) .body(callEditService.build()); return this.diagramBuilderHelper.newNodePalette() diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractPackageNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractPackageNodeDescriptionProvider.java index 308a40ac6..8f65bd114 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractPackageNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractPackageNodeDescriptionProvider.java @@ -45,11 +45,14 @@ import org.eclipse.syson.diagram.common.view.services.description.ToolDescriptionService; import org.eclipse.syson.diagram.common.view.tools.NamespaceImportNodeToolProvider; import org.eclipse.syson.diagram.common.view.tools.ToolSectionDescription; +import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; +import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService; import org.eclipse.syson.sysml.SysmlPackage; import org.eclipse.syson.sysmlcustomnodes.SysMLCustomnodesFactory; import org.eclipse.syson.sysmlcustomnodes.SysMLPackageNodeStyleDescription; import org.eclipse.syson.util.AQLUtils; import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ServiceMethod; import org.eclipse.syson.util.SysMLMetamodelHelper; import org.eclipse.syson.util.ViewConstants; @@ -152,7 +155,7 @@ public void link(DiagramDescription diagramDescription, IViewDiagramElementFinde protected InsideLabelDescription createInsideLabelDescription() { return this.diagramBuilderHelper.newInsideLabelDescription() - .labelExpression("aql:self.getContainerLabel()") + .labelExpression(ServiceMethod.of0(DiagramQueryAQLService::getContainerLabel).aqlSelf()) .position(InsideLabelPosition.TOP_CENTER) .style(this.createInsideLabelStyle()) .textAlign(LabelTextAlign.CENTER) @@ -188,11 +191,11 @@ private NodePalette createNodePalette(NodeDescription nodeDescription, IViewDiag .body(changeContext.build()); var callEditService = this.viewBuilderHelper.newChangeContext() - .expression(AQLUtils.getSelfServiceCallExpression("directEditNode", "newLabel")); + .expression(ServiceMethod.of1(DiagramMutationAQLService::directEditNode).aqlSelf("newLabel")); var editTool = this.diagramBuilderHelper.newLabelEditTool() .name("Edit") - .initialDirectEditLabelExpression(AQLUtils.getSelfServiceCallExpression("getDefaultInitialDirectEditLabel")) + .initialDirectEditLabelExpression(ServiceMethod.of0(DiagramQueryAQLService::getDefaultInitialDirectEditLabel).aqlSelf()) .body(callEditService.build()); var edgeTools = new ArrayList<>(this.getEdgeTools(nodeDescription, cache)); diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractUsageNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractUsageNodeDescriptionProvider.java index f913a0f2b..107edbe68 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractUsageNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractUsageNodeDescriptionProvider.java @@ -41,9 +41,12 @@ import org.eclipse.sirius.components.view.diagram.ToolSection; import org.eclipse.sirius.components.view.diagram.UserResizableDirection; import org.eclipse.syson.diagram.common.view.services.ViewEdgeToolSwitch; +import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; +import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService; import org.eclipse.syson.services.UtilService; import org.eclipse.syson.util.AQLUtils; import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ServiceMethod; import org.eclipse.syson.util.SysMLMetamodelHelper; import org.eclipse.syson.util.ViewConstants; @@ -197,7 +200,7 @@ public void link(DiagramDescription diagramDescription, IViewDiagramElementFinde protected InsideLabelDescription createInsideLabelDescription() { return this.diagramBuilderHelper.newInsideLabelDescription() - .labelExpression(AQLUtils.getSelfServiceCallExpression("getContainerLabel")) + .labelExpression(ServiceMethod.of0(DiagramQueryAQLService::getContainerLabel).aqlSelf()) .position(InsideLabelPosition.TOP_CENTER) .style(this.createInsideLabelStyle()) .textAlign(LabelTextAlign.CENTER) @@ -250,11 +253,11 @@ private NodePalette createNodePalette(NodeDescription nodeDescription, IViewDiag .body(changeContext.build()); var callEditService = this.viewBuilderHelper.newChangeContext() - .expression(AQLUtils.getSelfServiceCallExpression("directEditNode", "newLabel")); + .expression(ServiceMethod.of1(DiagramMutationAQLService::directEditNode).aqlSelf("newLabel")); var editTool = this.diagramBuilderHelper.newLabelEditTool() .name("Edit") - .initialDirectEditLabelExpression(AQLUtils.getSelfServiceCallExpression("getDefaultInitialDirectEditLabel")) + .initialDirectEditLabelExpression(ServiceMethod.of0(DiagramQueryAQLService::getDefaultInitialDirectEditLabel).aqlSelf()) .body(callEditService.build()); var edgeTools = new ArrayList(); diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AnnotatingNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AnnotatingNodeDescriptionProvider.java index 44dbe1d5a..9b715dc05 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AnnotatingNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AnnotatingNodeDescriptionProvider.java @@ -35,11 +35,14 @@ import org.eclipse.sirius.components.view.diagram.NodeToolSection; import org.eclipse.sirius.components.view.diagram.SynchronizationPolicy; import org.eclipse.sirius.components.view.diagram.UserResizableDirection; +import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; +import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService; import org.eclipse.syson.services.UtilService; import org.eclipse.syson.sysmlcustomnodes.SysMLCustomnodesFactory; import org.eclipse.syson.sysmlcustomnodes.SysMLNoteNodeStyleDescription; import org.eclipse.syson.util.AQLUtils; import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ServiceMethod; import org.eclipse.syson.util.SysMLMetamodelHelper; import org.eclipse.syson.util.ViewConstants; @@ -93,7 +96,7 @@ public void link(DiagramDescription diagramDescription, IViewDiagramElementFinde private InsideLabelDescription createInsideLabelDescription() { return this.diagramBuilderHelper.newInsideLabelDescription() - .labelExpression(AQLUtils.getSelfServiceCallExpression("getContainerLabel")) + .labelExpression(ServiceMethod.of0(DiagramQueryAQLService::getContainerLabel).aqlSelf()) .position(InsideLabelPosition.TOP_CENTER) .style(this.createInsideLabelStyle()) .textAlign(LabelTextAlign.CENTER) @@ -155,11 +158,11 @@ private NodePalette createNodePalette(NodeDescription nodeDescription, IViewDiag .body(changeContext.build()); var callEditService = this.viewBuilderHelper.newChangeContext() - .expression(AQLUtils.getSelfServiceCallExpression("directEditNode", "newLabel")); + .expression(ServiceMethod.of1(DiagramMutationAQLService::directEditNode).aqlSelf("newLabel")); var editTool = this.diagramBuilderHelper.newLabelEditTool() .name("Edit") - .initialDirectEditLabelExpression(AQLUtils.getSelfServiceCallExpression("getDefaultInitialDirectEditLabel")) + .initialDirectEditLabelExpression(ServiceMethod.of0(DiagramQueryAQLService::getDefaultInitialDirectEditLabel).aqlSelf()) .body(callEditService.build()); var toolSections = new ArrayList(); diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/CompartmentItemNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/CompartmentItemNodeDescriptionProvider.java index 24cb3a676..9b857f4fb 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/CompartmentItemNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/CompartmentItemNodeDescriptionProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024 Obeo. + * Copyright (c) 2024, 2025 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -28,9 +28,12 @@ import org.eclipse.sirius.components.view.diagram.NodeStyleDescription; import org.eclipse.sirius.components.view.diagram.SynchronizationPolicy; import org.eclipse.sirius.components.view.diagram.UserResizableDirection; +import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; +import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService; import org.eclipse.syson.util.AQLConstants; import org.eclipse.syson.util.AQLUtils; import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ServiceMethod; import org.eclipse.syson.util.SysMLMetamodelHelper; import org.eclipse.syson.util.ViewConstants; @@ -76,7 +79,7 @@ protected String getSemanticCandidateExpression() { protected InsideLabelDescription createInsideLabelDescription() { return this.diagramBuilderHelper.newInsideLabelDescription() - .labelExpression(AQLUtils.getSelfServiceCallExpression("getCompartmentItemLabel")) + .labelExpression(ServiceMethod.of0(DiagramQueryAQLService::getCompartmentItemLabel).aqlSelf()) .overflowStrategy(LabelOverflowStrategy.WRAP) .position(InsideLabelPosition.TOP_LEFT) .style(this.createInsideLabelStyle()) @@ -119,11 +122,11 @@ private NodePalette createCompartmentItemNodePalette() { .body(callDeleteService.build()); var callEditService = this.viewBuilderHelper.newChangeContext() - .expression(AQLUtils.getSelfServiceCallExpression("directEditListItem", "newLabel")); + .expression(ServiceMethod.of1(DiagramMutationAQLService::directEditListItem).aqlSelf("newLabel")); var editTool = this.diagramBuilderHelper.newLabelEditTool() .name("Edit") - .initialDirectEditLabelExpression(AQLUtils.getSelfServiceCallExpression("getInitialDirectEditListItemLabel")) + .initialDirectEditLabelExpression(ServiceMethod.of0(DiagramQueryAQLService::getInitialDirectEditListItemLabel).aqlSelf()) .body(callEditService.build()); return this.diagramBuilderHelper.newNodePalette() diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/ImportedPackageNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/ImportedPackageNodeDescriptionProvider.java index 0b65f2e0e..e6ce9802c 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/ImportedPackageNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/ImportedPackageNodeDescriptionProvider.java @@ -31,10 +31,12 @@ import org.eclipse.sirius.components.view.diagram.SynchronizationPolicy; import org.eclipse.sirius.components.view.diagram.UserResizableDirection; import org.eclipse.syson.diagram.common.view.services.description.ToolDescriptionService; +import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService; import org.eclipse.syson.sysml.SysmlPackage; import org.eclipse.syson.sysmlcustomnodes.SysMLCustomnodesFactory; import org.eclipse.syson.util.AQLUtils; import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ServiceMethod; import org.eclipse.syson.util.SysMLMetamodelHelper; import org.eclipse.syson.util.ViewConstants; @@ -87,7 +89,7 @@ public void link(DiagramDescription diagramDescription, IViewDiagramElementFinde protected InsideLabelDescription createInsideLabelDescription() { return this.diagramBuilderHelper.newInsideLabelDescription() - .labelExpression(AQLUtils.getSelfServiceCallExpression("getContainerLabel")) + .labelExpression(ServiceMethod.of0(DiagramQueryAQLService::getContainerLabel).aqlSelf()) .position(InsideLabelPosition.TOP_CENTER) .style(this.createInsideLabelStyle()) .textAlign(LabelTextAlign.CENTER) diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewLabelService.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewLabelService.java index ee409e342..02b66f020 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewLabelService.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewLabelService.java @@ -12,60 +12,33 @@ *******************************************************************************/ package org.eclipse.syson.diagram.common.view.services; -import static java.util.stream.Collectors.joining; - import java.text.MessageFormat; import java.util.Objects; -import java.util.function.BinaryOperator; -import org.eclipse.emf.common.util.EList; import org.eclipse.sirius.components.core.api.IFeedbackMessageService; import org.eclipse.sirius.components.representations.Message; import org.eclipse.sirius.components.representations.MessageLevel; -import org.eclipse.syson.services.LabelService; -import org.eclipse.syson.sysml.AcceptActionUsage; -import org.eclipse.syson.sysml.ActionUsage; -import org.eclipse.syson.sysml.Comment; -import org.eclipse.syson.sysml.ConstraintUsage; -import org.eclipse.syson.sysml.Dependency; -import org.eclipse.syson.sysml.Documentation; +import org.eclipse.syson.services.SimpleNameDeresolver; import org.eclipse.syson.sysml.Element; -import org.eclipse.syson.sysml.Expression; -import org.eclipse.syson.sysml.RequirementConstraintMembership; -import org.eclipse.syson.sysml.TransitionUsage; -import org.eclipse.syson.sysml.Usage; -import org.eclipse.syson.sysml.helper.LabelConstants; import org.eclipse.syson.sysml.textual.SysMLElementSerializer; -import org.eclipse.syson.sysml.textual.utils.Appender; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Label-related Java services used in diagrams. * * @author arichard */ -public class ViewLabelService extends LabelService { - - private static final String SPACE = " "; - - /** - * The default separator used when printing a set of Trigger Actions for a TransitionUsage label - */ - private static final String TRIGGER_ACTION_SEPARATOR = " | "; - - /** - * The default separator used when printing a set of Guard Expressions for a TransitionUsage label - */ - private static final String GUARD_EXPRESSION_SEPARATOR = "& "; +public class ViewLabelService { - /** - * The default separator used when printing a set of Effect Action for a TransitionUsage label - */ - private static final String EFFECT_ACTION_SEPARATOR = ", "; + private final Logger logger = LoggerFactory.getLogger(ViewLabelService.class); private final ShowDiagramsIconsService showDiagramsIconsService; + private final IFeedbackMessageService feedbackMessageService; + public ViewLabelService(IFeedbackMessageService feedbackMessageService, ShowDiagramsIconsService showDiagramsIconsService) { - super(feedbackMessageService); + this.feedbackMessageService = Objects.requireNonNull(feedbackMessageService); this.showDiagramsIconsService = Objects.requireNonNull(showDiagramsIconsService); } @@ -80,267 +53,6 @@ public boolean showIcon(Object object) { return this.showDiagramsIconsService.getShowIcons(); } - /** - * Return the label for the given {@link Usage} when displayed as a compartment item. - * - * @param usage - * the given {@link Usage}. - * @return the label for the given {@link Usage}. - */ - public String getCompartmentItemLabel(Usage usage) { - return this.getCompartmentItemStringRepresentation(usage, false); - } - - private String getCompartmentItemStringRepresentation(Usage usage, boolean directEditInput) { - StringBuilder label = new StringBuilder(); - if (usage instanceof ConstraintUsage constraintUsage - && usage.getOwningMembership() instanceof RequirementConstraintMembership) { - // Use the constraint-specific rendering only if the element is a constraint owned by a requirement. Other - // constraints (including requirements) are rendered as regular elements. - label.append(this.getCompartmentItemLabel(constraintUsage, directEditInput)); - } else { - label.append(this.getUsageListItemPrefix(usage)); - label.append(this.getIdentificationLabel(usage)); - label.append(this.getMultiplicityStringRepresentation(usage, directEditInput)); - label.append(this.getTypingLabel(usage)); - label.append(this.getRedefinitionLabel(usage)); - label.append(this.getSubsettingLabel(usage)); - label.append(this.getValueStringRepresentation(usage, directEditInput)); - } - return label.toString(); - } - - /** - * The default label for edges. - * - * @param element - * the element to get the edge label from - * @return the edge label - */ - public String getEdgeLabel(Element element) { - StringBuilder label = new StringBuilder(); - label.append(this.getIdentificationLabel(element)); - label.append(this.getTypingLabel(element)); - return label.toString(); - } - - /** - * Returns the label for the given {@code dependency}. - * - * @param dependency - * the dependency to get the edge label from - * @return the edge label - */ - public String getDependencyLabel(Dependency dependency) { - return this.getIdentificationLabel(dependency); - } - - /** - * Returns the label for the given {@link ConstraintUsage} when displayed as compartment item. - * - * @param constraintUsage - * the given {@link ConstraintUsage} - * @return the label for the given {@link ConstraintUsage} - */ - private String getCompartmentItemLabel(ConstraintUsage constraintUsage, boolean directEditInput) { - StringBuilder label = new StringBuilder(); - if (constraintUsage == null) { - label.append(""); - } else if (!constraintUsage.getOwnedMember().isEmpty() && constraintUsage.getOwnedMember().get(0) instanceof Expression expression) { - label.append(this.getSysmlTextualRepresentation(expression, directEditInput)); - } else { - // The constraint doesn't have an expression, we use its name as default label. - label.append(this.getIdentificationLabel(constraintUsage)); - } - return label.toString(); - } - - /** - * Return the label for the given {@link Documentation} when displayed as a compartment item. - * - * @param documentation - * the given {@link Documentation}. - * @return the label for the given {@link Documentation}. - */ - public String getCompartmentItemLabel(Documentation documentation) { - StringBuilder label = new StringBuilder(); - String declaredName = documentation.getDeclaredName(); - if (declaredName != null && !declaredName.isBlank()) { - label.append(declaredName); - label.append(LabelConstants.CR); - } - String body = documentation.getBody(); - if (body != null) { - label.append(body); - } - return label.toString(); - } - - /** - * Get the value to display when a direct edit has been called on the given item list {@link Usage}. - * - * @param usage - * the given {@link Usage}. - * @return the value to display. - */ - public String getInitialDirectEditListItemLabel(Usage usage) { - String result; - if (usage instanceof ConstraintUsage constraintUsage && - usage.getOwningMembership() instanceof RequirementConstraintMembership) { - result = this.getInitialDirectEditListItemLabel(constraintUsage, true); - } else { - result = this.getCompartmentItemStringRepresentation(usage, true); - } - return result; - } - - private String getInitialDirectEditListItemLabel(ConstraintUsage constraintUsage, boolean directEditInput) { - String result; - if (!constraintUsage.getOwnedMember().isEmpty() && constraintUsage.getOwnedMember().get(0) instanceof Expression expression) { - result = this.getSysmlTextualRepresentation(expression, directEditInput); - } else { - // The constraint doesn't have an expression, we set an initial empty string for the direct edit. - result = ""; - } - return result; - } - - /** - * Get the value to display when a direct edit has been called on the given {@link Documentation}. - * - * @param documentation - * the given {@link Documentation}. - * @return the value to display. - */ - public String getInitialDirectEditListItemLabel(Documentation documentation) { - return documentation.getBody(); - } - - /** - * Get the value to display when a direct edit has been called on the given {@link Comment}. - * - * @param comment - * the given {@link comment}. - * @return the value to display. - */ - public String getInitialDirectEditListItemLabel(Comment comment) { - return comment.getBody(); - } - - public String getMultiplicityRangeInitialDirectEditLabel(Element element) { - return this.getMultiplicityRangeInitialDirectEditLabel(element); - } - - public Element editMultiplicityRangeCenterLabel(Element element, String newLabel) { - return this.directEdit(element, newLabel, LabelService.NAME_OFF, LabelService.REDEFINITION_OFF, LabelService.SUBSETTING_OFF, LabelService.TYPING_OFF, LabelService.VALUE_OFF); - } - - public Element editEdgeCenterLabel(Element element, String newLabel) { - return this.directEdit(element, newLabel, LabelService.REDEFINITION_OFF, LabelService.SUBSETTING_OFF, LabelService.VALUE_OFF); - } - - /** - * Computes the label for a {@link TransitionUsage}. - * - * @param transition - * The {@link TransitionUsage} to compute the label for - * @param displayGuard - * holds true to display the guard - */ - public String getTransitionLabel(TransitionUsage transition, boolean displayGuard) { - // trigger-expression '/' ActionUsage - Appender appender = new Appender(SPACE, SPACE); - - this.handleTransitionTriggerExpression(transition, displayGuard, appender, false); - - EList effectActions = transition.getEffectAction(); - if (!effectActions.isEmpty()) { - String effectLabel = this.getEffectActionsDefaultDirectEditLabel(effectActions); - if (!effectLabel.isBlank()) { - appender.appendWithSpaceIfNeeded("/ " + effectLabel); - } - } - return appender.toString(); - } - - /** - * Computes the label for a {@link TransitionUsage}. - * - * @param transition - * The {@link TransitionUsage} to compute the label for - */ - public String getTransitionLabel(TransitionUsage transition) { - return this.getTransitionLabel(transition, true); - } - - /** - * Return the label for the given {@link Usage} represented as a border node. - * - * @param usage - * the given {@link Usage}. - * @return the label for the given {@link Usage}. - */ - public String getBorderNodeUsageLabel(Usage usage) { - StringBuilder label = new StringBuilder(); - String declaredName = usage.getDeclaredName(); - if (declaredName != null && !declaredName.isBlank()) { - label.append(declaredName); - } - label - .append(this.getTypingLabel(usage)) - .append(this.getRedefinitionLabel(usage)) - .append(this.getSubsettingLabel(usage)); - return label.toString(); - } - - private String getElementsDefaultInitialDirectEditLabel(EList elements, BinaryOperator reduceOperator) { - return elements.stream() - .map(action -> { - if (action instanceof AcceptActionUsage aau && aau.getPayloadParameter() != null) { - return this.getDefaultInitialDirectEditLabel(aau.getPayloadParameter()); - } - return this.getDefaultInitialDirectEditLabel(action); - }) - .reduce(reduceOperator) - .orElse(""); - } - - private String getEffectActionsDefaultDirectEditLabel(EList effectActions) { - String effectLabel; - effectLabel = this.getElementsDefaultInitialDirectEditLabel(effectActions, (a, b) -> { - return a + EFFECT_ACTION_SEPARATOR + b; - }); - return effectLabel; - } - - private void handleTransitionTriggerExpression(TransitionUsage transition, boolean displayGuard, Appender appender, boolean directEditInput) { - this.handleAcceptParameterPart(transition, appender, directEditInput); - if (displayGuard) { - this.handleGuardExpression(transition, appender, directEditInput); - } - } - - private void handleGuardExpression(TransitionUsage transition, Appender appender, boolean directEditInput) { - EList guardExpressions = transition.getGuardExpression(); - if (!guardExpressions.isEmpty()) { - String textGuardExpression = guardExpressions.stream().map(exp -> this.getSysmlTextualRepresentation(exp, directEditInput)) - .filter(Objects::nonNull) - .collect(joining(GUARD_EXPRESSION_SEPARATOR)); - appender.appendWithSpaceIfNeeded("[").append(textGuardExpression).append("]"); - } - } - - private void handleAcceptParameterPart(TransitionUsage transition, Appender appender, boolean directEditInput) { - EList triggerActions = transition.getTriggerAction(); - if (!triggerActions.isEmpty()) { - SysMLElementSerializer sysmlSerializer = this.buildSerializer(directEditInput); - String textGuardExpression = triggerActions.stream().map(sysmlSerializer::getAcceptParameterPart) - .filter(Objects::nonNull) - .collect(joining(TRIGGER_ACTION_SEPARATOR)); - appender.append(textGuardExpression); - } - } - /** * Send a feedback message to the user with a textual representation of an element. * @@ -354,7 +66,10 @@ private void handleAcceptParameterPart(TransitionUsage transition, Appender appe * @return the element itself */ public Element sendMessageWithTextualRepresentation(String msg, String level, Element element) { - this.getFeedbackMessageService().addFeedbackMessage(new Message(MessageFormat.format(msg, this.getSysmlTextualRepresentation(element, false)), MessageLevel.valueOf(level))); + var serializer = new SysMLElementSerializer("\n", " ", new SimpleNameDeresolver(), s -> { + this.logger.info(s.message()); + }); + this.feedbackMessageService.addFeedbackMessage(new Message(MessageFormat.format(msg, serializer.doSwitch(element)), MessageLevel.valueOf(level))); return element; } } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewNodeService.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewNodeService.java index 096891268..f7ac331a2 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewNodeService.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewNodeService.java @@ -56,7 +56,7 @@ import org.eclipse.syson.sysml.ViewDefinition; import org.eclipse.syson.sysml.ViewUsage; import org.eclipse.syson.sysml.helper.EMFUtils; -import org.eclipse.syson.sysml.metamodel.services.ElementQueryService; +import org.eclipse.syson.sysml.metamodel.services.MetamodelElementQueryService; import org.eclipse.syson.sysml.util.ElementUtil; import org.eclipse.syson.util.NodeFinder; import org.slf4j.Logger; @@ -77,13 +77,13 @@ public class ViewNodeService { private final ElementUtil elementUtil; - private final ElementQueryService elementQueryService; + private final MetamodelElementQueryService elementQueryService; public ViewNodeService(IObjectSearchService objectSearchService) { this.objectSearchService = Objects.requireNonNull(objectSearchService); this.utilService = new UtilService(); this.elementUtil = new ElementUtil(); - this.elementQueryService = new ElementQueryService(); + this.elementQueryService = new MetamodelElementQueryService(); } /** diff --git a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/ActorNodeDescriptionProvider.java b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/ActorNodeDescriptionProvider.java index e5943a71a..c4f7494d8 100644 --- a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/ActorNodeDescriptionProvider.java +++ b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/ActorNodeDescriptionProvider.java @@ -28,6 +28,7 @@ import org.eclipse.sirius.components.view.diagram.OutsideLabelPosition; import org.eclipse.sirius.components.view.diagram.SynchronizationPolicy; import org.eclipse.sirius.components.view.diagram.UserResizableDirection; +import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService; import org.eclipse.syson.model.services.aql.ModelQueryAQLService; import org.eclipse.syson.standard.diagrams.view.SDVDescriptionNameGenerator; import org.eclipse.syson.sysml.PartUsage; @@ -94,7 +95,7 @@ public NodeDescription create() { @Override protected List createOutsideLabelDescriptions() { return List.of(this.diagramBuilderHelper.newOutsideLabelDescription() - .labelExpression(AQLUtils.getSelfServiceCallExpression("getContainerLabel")) + .labelExpression(ServiceMethod.of0(DiagramQueryAQLService::getContainerLabel).aqlSelf()) .position(OutsideLabelPosition.BOTTOM_CENTER) .textAlign(LabelTextAlign.CENTER) .style(this.diagramBuilderHelper.newOutsideLabelStyle() diff --git a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/PortUsageBorderNodeDescriptionProvider.java b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/PortUsageBorderNodeDescriptionProvider.java index d6808fc18..5e1a06d9b 100644 --- a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/PortUsageBorderNodeDescriptionProvider.java +++ b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/PortUsageBorderNodeDescriptionProvider.java @@ -26,10 +26,13 @@ import org.eclipse.sirius.components.view.diagram.OutsideLabelPosition; import org.eclipse.sirius.web.application.editingcontext.EditingContext; import org.eclipse.syson.diagram.common.view.nodes.AbstractPortUsageBorderNodeDescriptionProvider; +import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; +import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService; import org.eclipse.syson.sysml.SysmlPackage; import org.eclipse.syson.util.AQLConstants; import org.eclipse.syson.util.AQLUtils; import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ServiceMethod; /** * Used to create the port usage border node description for general view diagram. @@ -71,11 +74,11 @@ protected NodePalette createNodePalette(IViewDiagramElementFinder cache, NodeDes .body(changeContext.build()); var callEditService = this.viewBuilderHelper.newChangeContext() - .expression(AQLUtils.getSelfServiceCallExpression("directEdit", "newLabel")); + .expression(ServiceMethod.of1(DiagramMutationAQLService::directEdit).aqlSelf("newLabel")); var editTool = this.diagramBuilderHelper.newLabelEditTool() .name("Edit") - .initialDirectEditLabelExpression(AQLUtils.getSelfServiceCallExpression("getDefaultInitialDirectEditLabel")) + .initialDirectEditLabelExpression(ServiceMethod.of0(DiagramQueryAQLService::getDefaultInitialDirectEditLabel).aqlSelf()) .body(callEditService.build()); return this.diagramBuilderHelper.newNodePalette() diff --git a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/ReferenceUsageBorderNodeDescriptionProvider.java b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/ReferenceUsageBorderNodeDescriptionProvider.java index a966a963c..2cc446062 100644 --- a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/ReferenceUsageBorderNodeDescriptionProvider.java +++ b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/ReferenceUsageBorderNodeDescriptionProvider.java @@ -31,10 +31,13 @@ import org.eclipse.sirius.components.view.diagram.SynchronizationPolicy; import org.eclipse.sirius.components.view.diagram.UserResizableDirection; import org.eclipse.syson.diagram.common.view.nodes.AbstractNodeDescriptionProvider; +import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; +import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService; import org.eclipse.syson.sysml.SysmlPackage; import org.eclipse.syson.util.AQLConstants; import org.eclipse.syson.util.AQLUtils; import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ServiceMethod; import org.eclipse.syson.util.SysMLMetamodelHelper; import org.eclipse.syson.util.ViewConstants; @@ -112,11 +115,11 @@ protected NodePalette createNodePalette(IViewDiagramElementFinder cache, NodeDes .body(changeContext.build()); var callEditService = this.viewBuilderHelper.newChangeContext() - .expression(AQLUtils.getSelfServiceCallExpression("directEdit", "newLabel")); + .expression(ServiceMethod.of1(DiagramMutationAQLService::directEdit).aqlSelf("newLabel")); var editTool = this.diagramBuilderHelper.newLabelEditTool() .name("Edit") - .initialDirectEditLabelExpression(AQLUtils.getSelfServiceCallExpression("getDefaultInitialDirectEditLabel")) + .initialDirectEditLabelExpression(ServiceMethod.of0(DiagramQueryAQLService::getDefaultInitialDirectEditLabel).aqlSelf()) .body(callEditService.build()); return this.diagramBuilderHelper.newNodePalette() diff --git a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/ViewUsageNodeDescriptionProvider.java b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/ViewUsageNodeDescriptionProvider.java index 583e44a6f..207bda14e 100644 --- a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/ViewUsageNodeDescriptionProvider.java +++ b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/nodes/ViewUsageNodeDescriptionProvider.java @@ -47,11 +47,14 @@ import org.eclipse.syson.diagram.common.view.tools.NamespaceImportNodeToolProvider; import org.eclipse.syson.diagram.common.view.tools.SetAsViewToolProvider; import org.eclipse.syson.diagram.common.view.tools.ToolSectionDescription; +import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService; +import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService; import org.eclipse.syson.standard.diagrams.view.SDVDiagramDescriptionProvider; import org.eclipse.syson.sysml.SysmlPackage; import org.eclipse.syson.sysmlcustomnodes.SysMLCustomnodesFactory; import org.eclipse.syson.util.AQLUtils; import org.eclipse.syson.util.IDescriptionNameGenerator; +import org.eclipse.syson.util.ServiceMethod; import org.eclipse.syson.util.SysMLMetamodelHelper; import org.eclipse.syson.util.ViewConstants; @@ -116,7 +119,7 @@ protected NodeStyleDescription createViewFrameNodeStyle() { protected InsideLabelDescription createInsideLabelDescription() { return this.diagramBuilderHelper.newInsideLabelDescription() - .labelExpression(AQLUtils.getSelfServiceCallExpression("getContainerLabel")) + .labelExpression(ServiceMethod.of0(DiagramQueryAQLService::getContainerLabel).aqlSelf()) .position(InsideLabelPosition.TOP_CENTER) .style(this.createInsideLabelStyle()) .textAlign(LabelTextAlign.CENTER) @@ -143,11 +146,11 @@ protected NodePalette createNodePalette(NodeDescription nodeDescription, IViewDi .body(changeContext.build()); var callEditService = this.viewBuilderHelper.newChangeContext() - .expression(AQLUtils.getSelfServiceCallExpression("directEditNode", "newLabel")); + .expression(ServiceMethod.of1(DiagramMutationAQLService::directEditNode).aqlSelf("newLabel")); var editTool = this.diagramBuilderHelper.newLabelEditTool() .name("Edit") - .initialDirectEditLabelExpression(AQLUtils.getSelfServiceCallExpression("getDefaultInitialDirectEditLabel")) + .initialDirectEditLabelExpression(ServiceMethod.of0(DiagramQueryAQLService::getDefaultInitialDirectEditLabel).aqlSelf()) .body(callEditService.build()); var nodesWithoutSection = new ArrayList<>(); diff --git a/scripts/check-coverage.jsh b/scripts/check-coverage.jsh index b542111d3..ffceb6f4e 100755 --- a/scripts/check-coverage.jsh +++ b/scripts/check-coverage.jsh @@ -35,20 +35,20 @@ var moduleCoverageData = List.of( new ModuleCoverage("syson-sysml-metamodel-edit", 16.0), new ModuleCoverage("syson-siriusweb-customnodes-metamodel", 44.0), new ModuleCoverage("syson-siriusweb-customnodes-metamodel-edit", 0.0), - new ModuleCoverage("syson-direct-edit-grammar", 66.0), - new ModuleCoverage("syson-services", 72.0), + new ModuleCoverage("syson-direct-edit-grammar", 67.0), + new ModuleCoverage("syson-services", 67.0), new ModuleCoverage("syson-sysml-rest-api-services", 93.0), new ModuleCoverage("syson-sysml-import", 84.0), new ModuleCoverage("syson-sysml-export", 64.0), new ModuleCoverage("syson-sysml-validation", 99.0), new ModuleCoverage("syson-common-view", 100.0), - new ModuleCoverage("syson-diagram-common-view", 85.0), + new ModuleCoverage("syson-diagram-common-view", 86.0), new ModuleCoverage("syson-standard-diagrams-view", 97.0), new ModuleCoverage("syson-table-requirements-view", 77.0), new ModuleCoverage("syson-tree-explorer-view", 84.0), new ModuleCoverage("syson-application-configuration", 71.0), new ModuleCoverage("syson-application", 37.0), - new ModuleCoverage("syson-diagram-services", 100.0), + new ModuleCoverage("syson-diagram-services", 85.0), new ModuleCoverage("syson-form-services", 100.0), new ModuleCoverage("syson-model-services", 100.0), new ModuleCoverage("syson-representation-services", 100.0),