From 6896767ac84209e8d71fd25bfba9a731c9b9fbdf Mon Sep 17 00:00:00 2001 From: leodennis Date: Sat, 4 Jan 2020 16:30:51 +0100 Subject: [PATCH] Add support for changing the label of references These changes make it possible to rename references and to change the multiplicity. Resolves: #72 Signed-off-by: leodennis Added support for literal editing Added subclasses of SLabelNode for attributes and literals. Changed EcoreLabelOperationHandler to support server side editing. Resolves:#61 Signed-off-by: leodennis Fix two Typos in the Create New Ecore Command. Change of structure Signed-off-by: leodennis --- client/sprotty-ecore/src/di.config.ts | 3 +- .../glsp/ecore/EcoreDiagramConfiguration.java | 3 +- .../glsp/ecore/gmodel/GModelFactory.java | 25 +++- .../EcoreLabelEditOperationHandler.java | 129 +++++++++++------- .../glsp/ecore/util/EcoreConfig.java | 3 +- 5 files changed, 107 insertions(+), 56 deletions(-) diff --git a/client/sprotty-ecore/src/di.config.ts b/client/sprotty-ecore/src/di.config.ts index c8e9878..8d49989 100644 --- a/client/sprotty-ecore/src/di.config.ts +++ b/client/sprotty-ecore/src/di.config.ts @@ -87,7 +87,8 @@ export default (containerId: string) => { configureModelElement(context, 'node:enum', LabeledNode, ClassNodeView); configureModelElement(context, 'node:datatype', LabeledNode, ClassNodeView); configureModelElement(context, 'label:name', SEditableLabel, SLabelView); - configureModelElement(context, 'label:edge', SLabel, SLabelView); + configureModelElement(context, 'label:edge-name', SEditableLabel, SLabelView); + configureModelElement(context, 'label:edge-multiplicity', SEditableLabel, SLabelView); configureModelElement(context, 'node:attribute', SLabelNodeAttribute, LabelNodeView); configureModelElement(context, 'node:enumliteral', SLabelNodeLiteral, LabelNodeView); configureModelElement(context, 'node:operation', SNode, RectangularNodeView); diff --git a/server/ecore-glsp/src/main/java/com/eclipsesource/glsp/ecore/EcoreDiagramConfiguration.java b/server/ecore-glsp/src/main/java/com/eclipsesource/glsp/ecore/EcoreDiagramConfiguration.java index 4dcbe51..4582ef3 100644 --- a/server/ecore-glsp/src/main/java/com/eclipsesource/glsp/ecore/EcoreDiagramConfiguration.java +++ b/server/ecore-glsp/src/main/java/com/eclipsesource/glsp/ecore/EcoreDiagramConfiguration.java @@ -99,7 +99,8 @@ public Map getTypeMappings() { mappings.put(Types.LABEL_NAME, GraphPackage.Literals.GLABEL); mappings.put(Types.LABEL_TEXT, GraphPackage.Literals.GLABEL); - mappings.put(Types.LABEL_EDGE, GraphPackage.Literals.GLABEL); + mappings.put(Types.LABEL_EDGE_NAME, GraphPackage.Literals.GLABEL); + mappings.put(Types.LABEL_EDGE_MULTIPLICITY, GraphPackage.Literals.GLABEL); mappings.put(Types.COMP, GraphPackage.Literals.GCOMPARTMENT); mappings.put(Types.COMP_HEADER, GraphPackage.Literals.GCOMPARTMENT); mappings.put(Types.LABEL_ICON, GraphPackage.Literals.GCOMPARTMENT); diff --git a/server/ecore-glsp/src/main/java/com/eclipsesource/glsp/ecore/gmodel/GModelFactory.java b/server/ecore-glsp/src/main/java/com/eclipsesource/glsp/ecore/gmodel/GModelFactory.java index c03285d..f1ca6f2 100644 --- a/server/ecore-glsp/src/main/java/com/eclipsesource/glsp/ecore/gmodel/GModelFactory.java +++ b/server/ecore-glsp/src/main/java/com/eclipsesource/glsp/ecore/gmodel/GModelFactory.java @@ -102,8 +102,10 @@ private List createEdges(EClass eClass) { } public GEdge create(EReference eReference) { - String label = String.format("[%s..%s] %s", eReference.getLowerBound(), - eReference.getUpperBound() == -1 ? "*" : eReference.getUpperBound(), eReference.getName()); + String labelMultiplicity = String.format("[%s..%s]", eReference.getLowerBound(), + eReference.getUpperBound() == -1 ? "*" : eReference.getUpperBound()); + String labelName = eReference.getName(); + String source = toId(eReference.getEContainingClass()); String target = toId(eReference.getEReferenceType()); @@ -113,15 +115,24 @@ public GEdge create(EReference eReference) { .id(id) // .addCssClass(CSS.ECORE_EDGE) // .addCssClass(eReference.isContainment() ? CSS.COMPOSITION : null) // - .add(new GLabelBuilder(Types.LABEL_EDGE) // + .add(new GLabelBuilder(Types.LABEL_EDGE_MULTIPLICITY) // .edgePlacement(new GEdgePlacementBuilder()// - .side(GConstants.EdgeSide.TOP)// + .side(GConstants.EdgeSide.BOTTOM)// .position(0.5d)// - .offset(0) // + .offset(2d) // .rotate(false) // .build())// - .id(id + "_label") // - .text(label).build()) + .id(id + "_label_multiplicity") // + .text(labelMultiplicity).build()) + .add(new GLabelBuilder(Types.LABEL_EDGE_NAME) // + .edgePlacement(new GEdgePlacementBuilder()// + .side(GConstants.EdgeSide.TOP)// + .position(0.5d)// + .offset(2d) // + .rotate(false) // + .build())// + .id(id + "_label_name") // + .text(labelName).build()) .sourceId(source) // .targetId(target) // .routerKind(GConstants.RouterKind.MANHATTAN)// diff --git a/server/ecore-glsp/src/main/java/com/eclipsesource/glsp/ecore/operationhandler/EcoreLabelEditOperationHandler.java b/server/ecore-glsp/src/main/java/com/eclipsesource/glsp/ecore/operationhandler/EcoreLabelEditOperationHandler.java index f03d8b4..ee70005 100644 --- a/server/ecore-glsp/src/main/java/com/eclipsesource/glsp/ecore/operationhandler/EcoreLabelEditOperationHandler.java +++ b/server/ecore-glsp/src/main/java/com/eclipsesource/glsp/ecore/operationhandler/EcoreLabelEditOperationHandler.java @@ -20,6 +20,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.eclipse.emf.common.notify.Notifier; import org.eclipse.emf.common.util.TreeIterator; @@ -28,19 +30,24 @@ import org.eclipse.emf.ecore.EDataType; import org.eclipse.emf.ecore.EEnumLiteral; import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EcorePackage; import com.eclipsesource.glsp.api.action.Action; import com.eclipsesource.glsp.api.action.kind.AbstractOperationAction; import com.eclipsesource.glsp.api.action.kind.ApplyLabelEditOperationAction; import com.eclipsesource.glsp.api.handler.OperationHandler; +import com.eclipsesource.glsp.api.jsonrpc.GLSPServerException; import com.eclipsesource.glsp.api.model.GraphicalModelState; import com.eclipsesource.glsp.ecore.EcoreFacade; import com.eclipsesource.glsp.ecore.EcoreModelIndex; import com.eclipsesource.glsp.ecore.ResourceManager; import com.eclipsesource.glsp.ecore.enotation.Shape; import com.eclipsesource.glsp.ecore.model.EcoreModelState; +import com.eclipsesource.glsp.graph.GEdge; +import com.eclipsesource.glsp.graph.GModelElement; import com.eclipsesource.glsp.graph.GNode; +import com.eclipsesource.glsp.ecore.util.EcoreConfig.Types; public class EcoreLabelEditOperationHandler implements OperationHandler { @@ -55,52 +62,83 @@ public void execute(AbstractOperationAction action, GraphicalModelState graphica EcoreFacade facade = EcoreModelState.getEcoreFacade(graphicalModelState); EcoreModelIndex index = EcoreModelState.getModelState(graphicalModelState).getIndex(); - // If we edit a Label Node (e.g. EAttribute, EEnumLiteral...), eObject will be - // defined. - // Otherwise, we're editing a Label that isn't a separate semantic element (e.g. - // Classifier Name Label), - // and we'll need to retrieve the top-level semantic element - EObject eObject = index.getSemantic(editLabelAction.getLabelId()).orElse(null); - if (eObject != null) { // Label Node (List Item) - if (eObject instanceof EAttribute) { - String inputText = editLabelAction.getText(); - String attributeName; - if (inputText.contains(":")) { - String[] split = inputText.split(":"); - attributeName = split[0].trim(); - - Optional type = parseStringToEType(split[1].trim(), - EcoreModelState.getResourceManager(graphicalModelState)); - if (type.isPresent()) { - ((EAttribute) eObject).setEType(type.get()); + Optional type = index.findElementByClass(editLabelAction.getLabelId(), GModelElement.class).map(e -> e.getType()); + if (type.isPresent()) { + switch (type.get()) { + case Types.LABEL_NAME: + GNode node = getOrThrow(index.findElementByClass(editLabelAction.getLabelId(), GNode.class), + "No parent Node for element with id " + editLabelAction.getLabelId() + " found"); + + EObject node_semantic = getOrThrow(index.getSemantic(node), + "No semantic element for labelContainer with id " + node.getId() + " found"); + + Shape shape = getOrThrow(index.getNotation(node_semantic), Shape.class, + "No shape element for label with id " + editLabelAction.getLabelId() + " found"); + + if (node_semantic instanceof EClassifier) { + ((EClassifier) node_semantic).setName(editLabelAction.getText().trim()); + // nameChange== uri change so we have to recreate the proxy here + shape.setSemanticElement(facade.createProxy(node_semantic)); + } + break; + + case Types.ATTRIBUTE: + EAttribute attribute_semantic = (EAttribute) getOrThrow(index.getSemantic(editLabelAction.getLabelId()), + "No semantic element for label with id " + editLabelAction.getLabelId() + " found"); + + String inputText = editLabelAction.getText(); + String attributeName; + if (inputText.contains(":")) { + String[] split = inputText.split(":"); + attributeName = split[0].trim(); + + Optional datatype = parseStringToEType(split[1].trim(), + EcoreModelState.getResourceManager(graphicalModelState)); + if (datatype.isPresent()) { + attribute_semantic.setEType(datatype.get()); + } + } else { + attributeName = inputText.trim(); } - } else { - attributeName = inputText.trim(); - } - if (!attributeName.isEmpty()) { - ((EAttribute) eObject).setName(attributeName); - } - } else if (eObject instanceof EEnumLiteral) { - String inputText = editLabelAction.getText().trim(); - - if (!inputText.isEmpty()) { - ((EEnumLiteral) eObject).setName(inputText); - } - } - } else { // Main Label of a Node - GNode node = getOrThrow(index.findElementByClass(editLabelAction.getLabelId(), GNode.class), - "No label container for label with id " + editLabelAction.getLabelId() + " found"); - - eObject = getOrThrow(index.getSemantic(node), - "No semantic element for labelContainer with id " + node.getId() + " found"); - - Shape shape = getOrThrow(index.getNotation(eObject), Shape.class, - "No shape element for label with id " + editLabelAction.getLabelId() + " found"); + if (!inputText.isEmpty()) { + attribute_semantic.setName(attributeName); + } + break; + + case Types.ENUMLITERAL: + EEnumLiteral literal_semantic = (EEnumLiteral) getOrThrow(index.getSemantic(editLabelAction.getLabelId()), + "No semantic element for label with id " + editLabelAction.getLabelId() + " found"); + String text = editLabelAction.getText().trim(); + if (!text.isEmpty()) { + literal_semantic.setName(text); + } + break; + + case Types.LABEL_EDGE_NAME: + GEdge edge = getOrThrow(index.findElementByClass(editLabelAction.getLabelId(), GEdge.class), + "No edge for label with id " + editLabelAction.getLabelId() + " found"); + EReference reference_semantic = (EReference) getOrThrow(index.getSemantic(edge), + "No semantic element for labelContainer with id " + edge.getId() + " found"); + reference_semantic.setName(editLabelAction.getText().trim()); + break; + + case Types.LABEL_EDGE_MULTIPLICITY: + edge = getOrThrow(index.findElementByClass(editLabelAction.getLabelId(), GEdge.class), + "No edge for label with id " + editLabelAction.getLabelId() + " found"); + reference_semantic = (EReference) getOrThrow(index.getSemantic(edge), + "No semantic element for labelContainer with id " + edge.getId() + " found"); + Pattern pattern = Pattern.compile("\\s*\\[\\s*(\\d+)\\s*\\.+\\s*(\\*|\\d+|\\-1)\\s*\\]\\s*"); + Matcher matcher = pattern.matcher(editLabelAction.getText()); + if (matcher.matches()) { + String lowerBound = matcher.group(1); + String upperBound = matcher.group(2); + reference_semantic.setLowerBound((lowerBound.equals("*")) ? -1 : Integer.valueOf(lowerBound)); + reference_semantic.setUpperBound((upperBound.equals("*")) ? -1 : Integer.valueOf(upperBound)); + } else { + throw new GLSPServerException("Multiplicity of reference with id " + editLabelAction.getLabelId() + " has a wrong input format", new IllegalArgumentException()); + } + break; - if (eObject instanceof EClassifier) { - ((EClassifier) eObject).setName(editLabelAction.getText().trim()); - // nameChange== uri change so we have to recreate the proxy here - shape.setSemanticElement(facade.createProxy(eObject)); } } } @@ -131,5 +169,4 @@ public static List getAllEAttributeTypes(ResourceManager resManager public String getLabel(AbstractOperationAction action) { return "Apply label"; } - -} \ No newline at end of file +} diff --git a/server/ecore-glsp/src/main/java/com/eclipsesource/glsp/ecore/util/EcoreConfig.java b/server/ecore-glsp/src/main/java/com/eclipsesource/glsp/ecore/util/EcoreConfig.java index 9ac6ea6..6c10d20 100644 --- a/server/ecore-glsp/src/main/java/com/eclipsesource/glsp/ecore/util/EcoreConfig.java +++ b/server/ecore-glsp/src/main/java/com/eclipsesource/glsp/ecore/util/EcoreConfig.java @@ -21,7 +21,8 @@ public static final class Types { public static final String LABEL_NAME = "label:name"; public static final String LABEL_TEXT = "label:text"; - public static final String LABEL_EDGE = "label:edge"; + public static final String LABEL_EDGE_NAME = "label:edge-name"; + public static final String LABEL_EDGE_MULTIPLICITY = "label:edge-multiplicity"; public static final String COMP = "comp:comp"; public static final String COMP_HEADER = "comp:header"; public static final String ICON_CLASS = "icon:class";