Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added @layout annotation to add arbitrary layout options an elements #1951

Merged
merged 6 commits into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions core/src/main/java/org/lflang/AttributeUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@

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;
Expand Down Expand Up @@ -101,6 +103,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<Attribute> findAttributesByName(EObject node, String name) {
List<Attribute> 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.
*
Expand Down Expand Up @@ -133,6 +153,25 @@ 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.
*
* <p>This should only be used on attributes that are expected to have a single argument.
*
* <p>Returns null if the attribute is not found or if it does not have any arguments.
*/
public static Map<String, String> getAttributeValues(EObject node, String attrName) {
final List<Attribute> attrs = findAttributesByName(node, attrName);
HashMap<String, String> 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.
*
Expand Down Expand Up @@ -231,6 +270,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<String, String> getLayoutOption(EObject node) {
return getAttributeValues(node, "layout");
}

/**
* Return the {@code @enclave} attribute annotated on the given node.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -74,6 +75,8 @@
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;
Expand Down Expand Up @@ -159,6 +162,10 @@ public class LinguaFrancaSynthesis extends AbstractDiagramSynthesis<Model> {

// -------------------------------------------------------------------------

/** 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 --
Expand Down Expand Up @@ -483,6 +490,7 @@ private Collection<KNode> createReactorNode(
Iterables.addAll(nodes, createUserComments(reactor, node));
configureReactorNodeLayout(node, true);
_layoutPostProcessing.configureMainReactor(node);
setAnnotatedLayoutOptions(reactor, node);
} else {
ReactorInstance instance = reactorInstance;

Expand Down Expand Up @@ -723,6 +731,7 @@ private Collection<KNode> createReactorNode(
}
configureReactorNodeLayout(node, false);
_layoutPostProcessing.configureReactor(node);
setAnnotatedLayoutOptions(reactor, node);
}

// Find and annotate cycles
Expand All @@ -732,7 +741,6 @@ private Collection<KNode> createReactorNode(
nodes.add(errNode);
}
}

return nodes;
}

Expand Down Expand Up @@ -1035,6 +1043,7 @@ private Collection<KNode> transformReactorNetwork(
timerNodes.put(timer, node);
_linguaFrancaShapeExtensions.addTimerFigure(node, timer);
_layoutPostProcessing.configureTimer(node);
setAnnotatedLayoutOptions(timer.getDefinition(), node);
}

// Create reactions
Expand All @@ -1049,6 +1058,7 @@ private Collection<KNode> transformReactorNetwork(

setLayoutOption(node, CoreOptions.PORT_CONSTRAINTS, PortConstraints.FIXED_SIDE);
_layoutPostProcessing.configureReaction(node);
setAnnotatedLayoutOptions(reaction.getDefinition(), node);
setLayoutOption(
node,
LayeredOptions.POSITION,
Expand Down Expand Up @@ -1202,6 +1212,7 @@ private Collection<KNode> transformReactorNetwork(
Iterables.addAll(nodes, createUserComments(action.getDefinition(), node));
setLayoutOption(node, CoreOptions.PORT_CONSTRAINTS, PortConstraints.FIXED_SIDE);
_layoutPostProcessing.configureAction(node);
setAnnotatedLayoutOptions(action.getDefinition(), node);
Pair<KPort, KPort> ports =
_linguaFrancaShapeExtensions.addActionFigureAndPorts(
node, action.isPhysical() ? "P" : "L");
Expand Down Expand Up @@ -1658,6 +1669,7 @@ private KPort addIOPort(
}
}
associateWith(_kLabelExtensions.addOutsidePortLabel(port, label, 8), lfPort.getDefinition());
setAnnotatedLayoutOptions(lfPort.getDefinition(), port);
return port;
}

Expand Down Expand Up @@ -1722,4 +1734,20 @@ private Iterable<KNode> 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.
*/
public void setAnnotatedLayoutOptions(EObject modelElement, EMapPropertyHolder kgraphElement) {
Map<String, String> 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)));
}
}
}
}
8 changes: 8 additions & 0 deletions core/src/main/java/org/lflang/validation/AttributeSpec.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public class AttributeSpec {

public static final String VALUE_ATTR = "value";
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<String, AttributeSpec> ATTRIBUTE_SPECS_BY_NAME = new HashMap<>();
Expand Down Expand Up @@ -212,6 +213,13 @@ enum AttrParamType {
ATTRIBUTE_SPECS_BY_NAME.put(
"side",
new AttributeSpec(List.of(new AttrParamSpec(VALUE_ATTR, AttrParamType.STRING, false))));
// @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))));
// @enclave(each=boolean)
ATTRIBUTE_SPECS_BY_NAME.put(
"enclave",
Expand Down