From 9e898ec3ced32771fd55550278c52ce07c4214d4 Mon Sep 17 00:00:00 2001 From: Soeren Domroes Date: Wed, 16 Aug 2023 16:26:05 +0200 Subject: [PATCH 1/3] First prototype @layout annotation. --- .../main/java/org/lflang/AttributeUtils.java | 47 +++++++++++++++++++ .../synthesis/LinguaFrancaSynthesis.java | 26 +++++++++- .../org/lflang/validation/AttributeSpec.java | 5 ++ 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/lflang/AttributeUtils.java b/core/src/main/java/org/lflang/AttributeUtils.java index f611e92cd7..c10258abe8 100644 --- a/core/src/main/java/org/lflang/AttributeUtils.java +++ b/core/src/main/java/org/lflang/AttributeUtils.java @@ -27,8 +27,11 @@ import static org.lflang.ast.ASTUtils.factory; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; + import org.eclipse.emf.ecore.EObject; import org.eclipse.xtext.nodemodel.ICompositeNode; import org.eclipse.xtext.nodemodel.util.NodeModelUtils; @@ -101,6 +104,24 @@ public static Attribute findAttributeByName(EObject node, String name) { .orElse(null); } + /** + * Return the attributes with the given name. + * + * @throws IllegalArgumentException If the node cannot have attributes + */ + public static List findAttributesByName(EObject node, String name) { + List attrs = getAttributes(node); + if (!attrs.isEmpty()) { + System.out.println("Fun"); + } + return attrs.stream() + .filter( + it -> + it.getAttrName() + .equalsIgnoreCase(name)) // case-insensitive search (more user-friendly) + .toList(); + } + /** * Return the first argument specified for the attribute. * @@ -133,6 +154,24 @@ public static String getAttributeValue(EObject node, String attrName) { return value; } + /** + * Search for an attribute with the given name on the given AST node and return its first argument + * as a String. + * + *

This should only be used on attributes that are expected to have a single argument. + * + *

Returns null if the attribute is not found or if it does not have any arguments. + */ + public static Map getAttributeValues(EObject node, String attrName) { + final List attrs = findAttributesByName(node, attrName); + HashMap layoutOptions = new HashMap<>(); + for (Attribute attribute : attrs) { + layoutOptions.put(StringUtil.removeQuotes(attribute.getAttrParms().get(0).getValue()), + StringUtil.removeQuotes(attribute.getAttrParms().get(1).getValue())); + } + return layoutOptions; + } + /** * Retrieve a specific annotation in a comment associated with the given model element in the AST. * @@ -241,6 +280,14 @@ public static String getPortSide(EObject node) { return getAttributeValue(node, "side"); } + /** + * Return the {@code layout} annotation for the given element or null if there is + * no such annotation. + */ + public static Map getLayoutOption(EObject node) { + return getAttributeValues(node, "layout"); + } + /** * Return the {@code @enclave} attribute annotated on the given node. * diff --git a/core/src/main/java/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java b/core/src/main/java/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java index 272b483607..0d0b5b93ad 100644 --- a/core/src/main/java/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java +++ b/core/src/main/java/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java @@ -32,6 +32,7 @@ import de.cau.cs.kieler.klighd.DisplayedActionData; import de.cau.cs.kieler.klighd.Klighd; import de.cau.cs.kieler.klighd.SynthesisOption; +import de.cau.cs.kieler.klighd.kgraph.EMapPropertyHolder; import de.cau.cs.kieler.klighd.kgraph.KEdge; import de.cau.cs.kieler.klighd.kgraph.KLabel; import de.cau.cs.kieler.klighd.kgraph.KNode; @@ -68,12 +69,16 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; + import javax.inject.Inject; + import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.elk.alg.layered.options.LayerConstraint; import org.eclipse.elk.alg.layered.options.LayeredOptions; import org.eclipse.elk.alg.layered.options.NodePlacementStrategy; +import org.eclipse.elk.core.data.LayoutMetaDataService; +import org.eclipse.elk.core.data.LayoutOptionData; import org.eclipse.elk.core.math.ElkMargin; import org.eclipse.elk.core.math.ElkPadding; import org.eclipse.elk.core.math.KVector; @@ -159,6 +164,9 @@ public class LinguaFrancaSynthesis extends AbstractDiagramSynthesis { // ------------------------------------------------------------------------- + /** Service class for accessing layout options by name */ + private static final LayoutMetaDataService LAYOUT_OPTIONS_SERVICE = LayoutMetaDataService.getInstance(); + public static final String ID = "org.lflang.diagram.synthesis.LinguaFrancaSynthesis"; // -- INTERNAL -- @@ -732,7 +740,7 @@ private Collection createReactorNode( nodes.add(errNode); } } - + setAnnotatedLayoutOptions(reactor, node); return nodes; } @@ -1722,4 +1730,20 @@ private Iterable createUserComments(EObject element, KNode targetNode) { } return List.of(); } + + /** + * Searches the "@layout" annotations and applies them to the corresponding element. + * + * @param kgraphElement The view model element to apply the layout options to, e.g. a KNode. + * @param modelElement The model element that has the annotations, e.g. a reactor. + */ + private void setAnnotatedLayoutOptions(EObject modelElement, EMapPropertyHolder kgraphElement) { + Map options = AttributeUtils.getLayoutOption(modelElement); + for (String key : options.keySet()) { + LayoutOptionData data = LAYOUT_OPTIONS_SERVICE.getOptionDataBySuffix(key); + if (data != null) { + kgraphElement.setProperty(data, data.parseValue(options.get(key))); + } + } + } } diff --git a/core/src/main/java/org/lflang/validation/AttributeSpec.java b/core/src/main/java/org/lflang/validation/AttributeSpec.java index 90e7f9cf6d..1df9a0c6ef 100644 --- a/core/src/main/java/org/lflang/validation/AttributeSpec.java +++ b/core/src/main/java/org/lflang/validation/AttributeSpec.java @@ -213,6 +213,11 @@ enum AttrParamType { ATTRIBUTE_SPECS_BY_NAME.put( "side", new AttributeSpec(List.of(new AttrParamSpec(VALUE_ATTR, AttrParamType.STRING, false)))); + // @layout("string", "any") e.g. @layout("port.side", "WEST") + ATTRIBUTE_SPECS_BY_NAME.put( + "layout", + new AttributeSpec(List.of(new AttrParamSpec(VALUE_ATTR, AttrParamType.STRING, false), + new AttrParamSpec(VALUE_ATTR, AttrParamType.STRING, false)))); // @enclave(each=boolean) ATTRIBUTE_SPECS_BY_NAME.put( "enclave", From 6de9d85b425e050e26bc4005c9aaaa09334f27e5 Mon Sep 17 00:00:00 2001 From: Soeren Domroes Date: Wed, 16 Aug 2023 17:18:25 +0200 Subject: [PATCH 2/3] Added annotation for all annotatable elements. --- .../lflang/diagram/synthesis/LinguaFrancaSynthesis.java | 9 +++++++-- .../main/java/org/lflang/validation/AttributeSpec.java | 5 +++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java b/core/src/main/java/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java index 0d0b5b93ad..d10597f44a 100644 --- a/core/src/main/java/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java +++ b/core/src/main/java/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java @@ -491,6 +491,7 @@ private Collection createReactorNode( Iterables.addAll(nodes, createUserComments(reactor, node)); configureReactorNodeLayout(node, true); _layoutPostProcessing.configureMainReactor(node); + setAnnotatedLayoutOptions(reactor, node); } else { ReactorInstance instance = reactorInstance; @@ -731,6 +732,7 @@ private Collection createReactorNode( } configureReactorNodeLayout(node, false); _layoutPostProcessing.configureReactor(node); + setAnnotatedLayoutOptions(reactor, node); } // Find and annotate cycles @@ -740,7 +742,6 @@ private Collection createReactorNode( nodes.add(errNode); } } - setAnnotatedLayoutOptions(reactor, node); return nodes; } @@ -1043,6 +1044,7 @@ private Collection transformReactorNetwork( timerNodes.put(timer, node); _linguaFrancaShapeExtensions.addTimerFigure(node, timer); _layoutPostProcessing.configureTimer(node); + setAnnotatedLayoutOptions(timer.getDefinition(), node); } // Create reactions @@ -1057,6 +1059,7 @@ private Collection transformReactorNetwork( setLayoutOption(node, CoreOptions.PORT_CONSTRAINTS, PortConstraints.FIXED_SIDE); _layoutPostProcessing.configureReaction(node); + setAnnotatedLayoutOptions(reaction.getDefinition(), node); setLayoutOption( node, LayeredOptions.POSITION, @@ -1210,6 +1213,7 @@ private Collection transformReactorNetwork( Iterables.addAll(nodes, createUserComments(action.getDefinition(), node)); setLayoutOption(node, CoreOptions.PORT_CONSTRAINTS, PortConstraints.FIXED_SIDE); _layoutPostProcessing.configureAction(node); + setAnnotatedLayoutOptions(action.getDefinition(), node); Pair ports = _linguaFrancaShapeExtensions.addActionFigureAndPorts( node, action.isPhysical() ? "P" : "L"); @@ -1666,6 +1670,7 @@ private KPort addIOPort( } } associateWith(_kLabelExtensions.addOutsidePortLabel(port, label, 8), lfPort.getDefinition()); + setAnnotatedLayoutOptions(lfPort.getDefinition(), port); return port; } @@ -1737,7 +1742,7 @@ private Iterable createUserComments(EObject element, KNode targetNode) { * @param kgraphElement The view model element to apply the layout options to, e.g. a KNode. * @param modelElement The model element that has the annotations, e.g. a reactor. */ - private void setAnnotatedLayoutOptions(EObject modelElement, EMapPropertyHolder kgraphElement) { + public void setAnnotatedLayoutOptions(EObject modelElement, EMapPropertyHolder kgraphElement) { Map options = AttributeUtils.getLayoutOption(modelElement); for (String key : options.keySet()) { LayoutOptionData data = LAYOUT_OPTIONS_SERVICE.getOptionDataBySuffix(key); diff --git a/core/src/main/java/org/lflang/validation/AttributeSpec.java b/core/src/main/java/org/lflang/validation/AttributeSpec.java index 1df9a0c6ef..6d1da3e0a3 100644 --- a/core/src/main/java/org/lflang/validation/AttributeSpec.java +++ b/core/src/main/java/org/lflang/validation/AttributeSpec.java @@ -50,6 +50,7 @@ public class AttributeSpec { public static final String VALUE_ATTR = "value"; public static final String NETWORK_MESSAGE_ACTIONS = "network_message_actions"; public static final String EACH_ATTR = "each"; + public static final String OPTION_ATTR = "option"; /** A map from a string to a supported AttributeSpec */ public static final Map ATTRIBUTE_SPECS_BY_NAME = new HashMap<>(); @@ -213,10 +214,10 @@ enum AttrParamType { ATTRIBUTE_SPECS_BY_NAME.put( "side", new AttributeSpec(List.of(new AttrParamSpec(VALUE_ATTR, AttrParamType.STRING, false)))); - // @layout("string", "any") e.g. @layout("port.side", "WEST") + // @layout(option="string", value="any") e.g. @layout(option="port.side", value="WEST") ATTRIBUTE_SPECS_BY_NAME.put( "layout", - new AttributeSpec(List.of(new AttrParamSpec(VALUE_ATTR, AttrParamType.STRING, false), + new AttributeSpec(List.of(new AttrParamSpec(OPTION_ATTR, AttrParamType.STRING, false), new AttrParamSpec(VALUE_ATTR, AttrParamType.STRING, false)))); // @enclave(each=boolean) ATTRIBUTE_SPECS_BY_NAME.put( From 294b32f85239b9b0b1579a50452443c8bfed28e7 Mon Sep 17 00:00:00 2001 From: Soeren Domroes Date: Mon, 28 Aug 2023 08:21:48 +0200 Subject: [PATCH 3/3] Ran spotlessJavaApply. --- core/src/main/java/org/lflang/AttributeUtils.java | 8 ++++---- .../lflang/diagram/synthesis/LinguaFrancaSynthesis.java | 5 ++--- .../main/java/org/lflang/validation/AttributeSpec.java | 6 ++++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/lflang/AttributeUtils.java b/core/src/main/java/org/lflang/AttributeUtils.java index c10258abe8..ee9666f0b8 100644 --- a/core/src/main/java/org/lflang/AttributeUtils.java +++ b/core/src/main/java/org/lflang/AttributeUtils.java @@ -31,7 +31,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; - import org.eclipse.emf.ecore.EObject; import org.eclipse.xtext.nodemodel.ICompositeNode; import org.eclipse.xtext.nodemodel.util.NodeModelUtils; @@ -166,7 +165,8 @@ public static Map getAttributeValues(EObject node, String attrNa final List attrs = findAttributesByName(node, attrName); HashMap layoutOptions = new HashMap<>(); for (Attribute attribute : attrs) { - layoutOptions.put(StringUtil.removeQuotes(attribute.getAttrParms().get(0).getValue()), + layoutOptions.put( + StringUtil.removeQuotes(attribute.getAttrParms().get(0).getValue()), StringUtil.removeQuotes(attribute.getAttrParms().get(1).getValue())); } return layoutOptions; @@ -281,8 +281,8 @@ public static String getPortSide(EObject node) { } /** - * Return the {@code layout} annotation for the given element or null if there is - * no such annotation. + * Return the {@code layout} annotation for the given element or null if there is no such + * annotation. */ public static Map getLayoutOption(EObject node) { return getAttributeValues(node, "layout"); diff --git a/core/src/main/java/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java b/core/src/main/java/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java index d10597f44a..a0f73dbabf 100644 --- a/core/src/main/java/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java +++ b/core/src/main/java/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java @@ -69,9 +69,7 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; - import javax.inject.Inject; - import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.elk.alg.layered.options.LayerConstraint; @@ -165,7 +163,8 @@ public class LinguaFrancaSynthesis extends AbstractDiagramSynthesis { // ------------------------------------------------------------------------- /** Service class for accessing layout options by name */ - private static final LayoutMetaDataService LAYOUT_OPTIONS_SERVICE = LayoutMetaDataService.getInstance(); + private static final LayoutMetaDataService LAYOUT_OPTIONS_SERVICE = + LayoutMetaDataService.getInstance(); public static final String ID = "org.lflang.diagram.synthesis.LinguaFrancaSynthesis"; diff --git a/core/src/main/java/org/lflang/validation/AttributeSpec.java b/core/src/main/java/org/lflang/validation/AttributeSpec.java index 6d1da3e0a3..5b9d6dc51b 100644 --- a/core/src/main/java/org/lflang/validation/AttributeSpec.java +++ b/core/src/main/java/org/lflang/validation/AttributeSpec.java @@ -217,8 +217,10 @@ enum AttrParamType { // @layout(option="string", value="any") e.g. @layout(option="port.side", value="WEST") ATTRIBUTE_SPECS_BY_NAME.put( "layout", - new AttributeSpec(List.of(new AttrParamSpec(OPTION_ATTR, AttrParamType.STRING, false), - new AttrParamSpec(VALUE_ATTR, AttrParamType.STRING, false)))); + new AttributeSpec( + List.of( + new AttrParamSpec(OPTION_ATTR, AttrParamType.STRING, false), + new AttrParamSpec(VALUE_ATTR, AttrParamType.STRING, false)))); // @enclave(each=boolean) ATTRIBUTE_SPECS_BY_NAME.put( "enclave",