From 528fb9781f6fabf27381939223dc708bfc1e5899 Mon Sep 17 00:00:00 2001 From: Simon Barner <barner@fortiss.org> Date: Tue, 4 Dec 2018 11:29:19 +0100 Subject: [PATCH] Enable editing of multi-valued EAttribute annotations * Add new classes * MultiValueAnnotationTextEditingSupport, an editing support that allows for textual editing of multi-value annotation. * MultiValueAnnotationTextEditingDialog, a dialog to that allows to edit multiple values * Adapt * EditingSupportFactory to instantiate MultiValueAnnotationTextEditingSupport instead of throwning an exception about an unsupported case * EStructuralFeatureValueProviderBase to set values using the new method setAnnotationValueFromString(Collection<String>, T) Issue-Ref: 3578 Issue-Url: https://af3-developer.fortiss.org/issues/3578 Signed-off-by: Simon Barner <barner@fortiss.org> --- .../ui/annotation/editingsupport/.ratings | 4 +- .../editingsupport/EditingSupportFactory.java | 4 +- ...MultiValueAnnotationTextEditingDialog.java | 244 ++++++++++++++++++ ...ultiValueAnnotationTextEditingSupport.java | 54 ++++ .../base/annotation/valueprovider/.ratings | 2 +- .../EStructuralFeatureValueProviderBase.java | 55 +++- 6 files changed, 356 insertions(+), 7 deletions(-) create mode 100644 org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/editingsupport/MultiValueAnnotationTextEditingDialog.java create mode 100644 org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/editingsupport/MultiValueAnnotationTextEditingSupport.java diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/editingsupport/.ratings b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/editingsupport/.ratings index c98809460..5101c3fd2 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/editingsupport/.ratings +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/editingsupport/.ratings @@ -1,7 +1,7 @@ AnnotationEditingSupportBase.java a5ecf54616b50f947d251f45cbb5789df5234170 GREEN CheckBoxEditingSupport.java 1d8d9dd444f0e52767c65fd2711321438d3a9b29 GREEN ComboBoxEditingSupport.java 6b6a23be327ebdea9bdaf007304bd3d7c14b2cef GREEN -EditingSupportFactory.java db05c9f4c1d48f9858827051b0e9e58214645c0a YELLOW +EditingSupportFactory.java e42347692ef23a8cb71532edadcf176ae49e992d YELLOW ElementCommentEditingSupport.java 4be366924a040caf3f80e35b383e796e84aedcac GREEN ElementEditingSupportBase.java a6360f99ee149276f0fbd299820ebd1c9731ea97 GREEN ElementNameEditingSupport.java 0dcaecf4ba5f8ddefa3ccb7d6f4e4506f7f09b26 GREEN @@ -12,4 +12,6 @@ MultiValueAnnotationDialogBase.java 910a7e1203f84e462d25d4063264fbd19b5ae22e YEL MultiValueAnnotationEditingSupportBase.java ac228c1a4dec5d7035729585c2dcb9799da6aba9 YELLOW MultiValueAnnotationSelectionDialog.java d5639aee0d5b4220d5dae92df0d87062dfe17bd6 YELLOW MultiValueAnnotationSelectionEditingSupport.java a4c3c3f9c3cc4f43f27c95f9ba676b4bef01e157 YELLOW +MultiValueAnnotationTextEditingDialog.java 8d8ee46724f52175233e74ec7f0a5bed4f7d3636 YELLOW +MultiValueAnnotationTextEditingSupport.java 38c780819396fc75d10fbb660832652d89d59378 YELLOW TextEditingSupport.java e761ea393816f23ca41157f2a9a9a8d8ef30b858 GREEN diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/editingsupport/EditingSupportFactory.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/editingsupport/EditingSupportFactory.java index db05c9f4c..e42347692 100644 --- a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/editingsupport/EditingSupportFactory.java +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/editingsupport/EditingSupportFactory.java @@ -117,8 +117,8 @@ public class EditingSupportFactory { specification, eStructuralFeatureDescriptor); } - throw new Exception( - "For feature multiplicity > 1, EStructuralValueProvider has been implemented for EReferences and EEnumns, only ."); + return new MultiValueAnnotationTextEditingSupport<String>(viewer, clazz, specification, + eStructuralFeatureDescriptor); } /** diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/editingsupport/MultiValueAnnotationTextEditingDialog.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/editingsupport/MultiValueAnnotationTextEditingDialog.java new file mode 100644 index 000000000..8d8ee4672 --- /dev/null +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/editingsupport/MultiValueAnnotationTextEditingDialog.java @@ -0,0 +1,244 @@ +/*-------------------------------------------------------------------------+ +| Copyright 2018 fortiss GmbH | +| | +| Licensed under the Apache License, Version 2.0 (the "License"); | +| you may not use this file except in compliance with the License. | +| You may obtain a copy of the License at | +| | +| http://www.apache.org/licenses/LICENSE-2.0 | +| | +| Unless required by applicable law or agreed to in writing, software | +| distributed under the License is distributed on an "AS IS" BASIS, | +| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | +| See the License for the specific language governing permissions and | +| limitations under the License. | ++--------------------------------------------------------------------------*/ +package org.fortiss.tooling.base.ui.annotation.editingsupport; + +import static java.lang.Integer.compare; +import static java.lang.Math.min; +import static java.util.Arrays.asList; +import static java.util.Arrays.stream; + +import java.util.Collection; +import java.util.OptionalInt; +import java.util.stream.IntStream; + +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EFactory; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SegmentEvent; +import org.eclipse.swt.events.SegmentListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.List; +import org.eclipse.swt.widgets.Text; + +/** + * Dialog to that allows to edit multiple values + * + * @author barner + */ +public class MultiValueAnnotationTextEditingDialog<T> extends MultiValueAnnotationDialogBase<T> { + + /** The {@link Text} into which input is entered. */ + private Text textInput; + + /** {@link List} containing the current input. */ + private List list; + + /** {@link Button} to add the current value of {@link #textInput} to {@link #list}. */ + private Button addButton; + + /** {@link Button} to remove the currently selected item from {@link #list}. */ + private Button removeButton; + + /** {@link Button} to move up the currently selected item of {@link #list}. */ + private Button upButton; + + /** {@link Button} to move down the currently selected item of {@link #list}. */ + private Button downButton; + + /** Flag if dialog is currently set up in {@link #createDialogArea(Composite)}. */ + private boolean isCreateDialogArea; + + /** Underlying {@link EAttribute}. */ + private EAttribute eAttribute; + + /** The {@link Collection} of initial values. */ + private Collection<T> initialValues; + + /** Constructs a new {@link MultiValueAnnotationTextEditingDialog}. */ + public MultiValueAnnotationTextEditingDialog(EAttribute eAttribute, + Collection<T> initialValues) { + super("Add", eAttribute.getLowerBound(), eAttribute.getUpperBound()); + this.eAttribute = eAttribute; + this.initialValues = initialValues; + } + + /** Returns the {@link EFactory} for the underlying {@link #eAttribute}. */ + protected EFactory getEFactory() { + return eAttribute.getEType().getEPackage().getEFactoryInstance(); + } + + /** Refreshes the status of the dialog's UI elements. */ + protected void refresh() { + if(isCreateDialogArea) { + return; + } + + boolean isValidText = true; + try { + EFactory eFactory = getEFactory(); + eFactory.createFromString(eAttribute.getEAttributeType(), textInput.getText()); + } catch(Exception e) { + isValidText = false; + } + + addButton.setEnabled(isValidText); + + OptionalInt min = stream(list.getSelectionIndices()).reduce(Integer::min); + OptionalInt max = stream(list.getSelectionIndices()).reduce(Integer::max); + removeButton.setEnabled(min.isPresent()); + + upButton.setEnabled(min.isPresent() && min.getAsInt() > 0); + downButton.setEnabled(max.isPresent() && max.getAsInt() < list.getItemCount() - 1); + } + + /** {@inheritDoc} */ + @Override + protected Composite createDialogArea(Composite parent) { + isCreateDialogArea = true; + Composite area = (Composite)super.createDialogArea(parent); + area.setLayout(new GridLayout(2, false)); + + textInput = new Text(area, SWT.BORDER | SWT.LEFT); + GridData gd_Text = new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1); + gd_Text.widthHint = 300; + gd_Text.minimumWidth = 300; + textInput.setLayoutData(gd_Text); + textInput.addSegmentListener(new SegmentListener() { + /** {@inheritDoc} */ + @Override + public void getSegments(SegmentEvent event) { + refresh(); + } + }); + + addButton = new Button(area, SWT.PUSH); + GridData gd_Buttons = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1); + gd_Buttons.widthHint = 100; + gd_Buttons.minimumWidth = 100; + addButton.setLayoutData(gd_Buttons); + addButton.setText("Add"); + + addButton.addSelectionListener(new SelectionAdapter() { + /** {@inheritDoc} */ + @Override + public void widgetSelected(SelectionEvent e) { + list.add(textInput.getText()); + textInput.setText(""); + refresh(); + } + }); + + list = new List(area, SWT.BORDER | SWT.LEFT | SWT.MULTI); + GridData gd_List = new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 4); + gd_List.minimumWidth = 310; + gd_List.heightHint = 310; + list.setLayoutData(gd_List); + list.addSelectionListener(new SelectionAdapter() { + /** {@inheritDoc} */ + @Override + public void widgetSelected(SelectionEvent e) { + refresh(); + } + }); + for(Object value : initialValues) { + EFactory eFactory = getEFactory(); + String valueLabel = eFactory.convertToString(eAttribute.getEAttributeType(), value); + list.add(valueLabel); + } + + removeButton = new Button(area, SWT.PUSH); + removeButton.setLayoutData(gd_Buttons); + removeButton.setText("Remove"); + removeButton.addSelectionListener(new SelectionAdapter() { + /** {@inheritDoc} */ + @Override + public void widgetSelected(SelectionEvent e) { + int[] selectionIndices = list.getSelectionIndices(); + int index = -1; + if(selectionIndices.length == 1) { + index = selectionIndices[0]; + } + for(int i : selectionIndices) { + list.remove(i); + } + list.setSelection(min(index, list.getItemCount())); + refresh(); + } + }); + + upButton = new Button(area, SWT.PUSH); + upButton.setLayoutData(gd_Buttons); + upButton.setText("Up"); + upButton.addSelectionListener(new SelectionAdapter() { + /** {@inheritDoc} */ + @Override + public void widgetSelected(SelectionEvent e) { + stream(list.getSelectionIndices()).sorted().forEach(i -> { + String swap = list.getItem(i - 1); + list.setItem(i - 1, list.getItem(i)); + list.setItem(i, swap); + }); + IntStream oldIndices = stream(list.getSelectionIndices()); + list.deselectAll(); + list.select(oldIndices.map(i -> i - 1).toArray()); + refresh(); + } + }); + + downButton = new Button(area, SWT.PUSH); + downButton.setLayoutData(gd_Buttons); + downButton.setText("Down"); + downButton.addSelectionListener(new SelectionAdapter() { + /** {@inheritDoc} */ + @Override + public void widgetSelected(SelectionEvent e) { + stream(list.getSelectionIndices()).boxed().sorted((i1, i2) -> compare(i2, i1)) + .forEach(i -> { + String swap = list.getItem(i + 1); + list.setItem(i + 1, list.getItem(i)); + list.setItem(i, swap); + }); + IntStream oldIndices = stream(list.getSelectionIndices()); + list.deselectAll(); + list.select(oldIndices.map(i -> i + 1).toArray()); + refresh(); + } + }); + + isCreateDialogArea = false; + refresh(); + return area; + } + + /** {@inheritDoc} */ + @Override + public void create() { + super.create(); + getShell().setDefaultButton(addButton); + } + + /** {@inheritDoc} */ + @SuppressWarnings("unchecked") + @Override + protected Collection<T> getElementsFromDialog() { + return (Collection<T>)asList(list.getItems()); + } +} diff --git a/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/editingsupport/MultiValueAnnotationTextEditingSupport.java b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/editingsupport/MultiValueAnnotationTextEditingSupport.java new file mode 100644 index 000000000..38c780819 --- /dev/null +++ b/org.fortiss.tooling.base.ui/src/org/fortiss/tooling/base/ui/annotation/editingsupport/MultiValueAnnotationTextEditingSupport.java @@ -0,0 +1,54 @@ +/*-------------------------------------------------------------------------+ +| Copyright 2018 fortiss GmbH | +| | +| Licensed under the Apache License, Version 2.0 (the "License"); | +| you may not use this file except in compliance with the License. | +| You may obtain a copy of the License at | +| | +| http://www.apache.org/licenses/LICENSE-2.0 | +| | +| Unless required by applicable law or agreed to in writing, software | +| distributed under the License is distributed on an "AS IS" BASIS, | +| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | +| See the License for the specific language governing permissions and | +| limitations under the License. | ++--------------------------------------------------------------------------*/ +package org.fortiss.tooling.base.ui.annotation.editingsupport; + +import java.util.Collection; + +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.jface.viewers.ColumnViewer; +import org.eclipse.jface.viewers.EditingSupport; +import org.eclipse.swt.widgets.Composite; +import org.fortiss.tooling.base.annotation.valueprovider.EStructuralFeatureDescriptor; +import org.fortiss.tooling.base.model.element.IAnnotatedSpecification; +import org.fortiss.tooling.base.model.element.IModelElement; + +/** + * {@link EditingSupport} for textual editing of multi-value annotations. + * + * @author barner + */ +public class MultiValueAnnotationTextEditingSupport<T> + extends MultiValueAnnotationEditingSupportBase<T> { + + /** Constructs a new {@link MultiValueAnnotationTextEditingSupport}. */ + public MultiValueAnnotationTextEditingSupport(ColumnViewer viewer, + Class<? extends IAnnotatedSpecification> clazz, IAnnotatedSpecification specification, + EStructuralFeatureDescriptor eStructuralFeatureDescriptor) { + super(viewer, clazz, specification, eStructuralFeatureDescriptor); + } + + /** {@inheritDoc} */ + @Override + public IMultiValueAnnotationDialog<T> createMultiEditingDialog(Composite parent, + IModelElement modelElement, Collection<T> values) { + + EStructuralFeature eStructuralFeature = + eStructuralFeatureDescriptor.getEStructuralFeature(specification); + + return new MultiValueAnnotationTextEditingDialog<T>((EAttribute)eStructuralFeature, values); + } +} diff --git a/org.fortiss.tooling.base/src/org/fortiss/tooling/base/annotation/valueprovider/.ratings b/org.fortiss.tooling.base/src/org/fortiss/tooling/base/annotation/valueprovider/.ratings index 8a68adfa5..f5eb48ac6 100644 --- a/org.fortiss.tooling.base/src/org/fortiss/tooling/base/annotation/valueprovider/.ratings +++ b/org.fortiss.tooling.base/src/org/fortiss/tooling/base/annotation/valueprovider/.ratings @@ -1,6 +1,6 @@ AnnotationInstSpec.java b4f2ed47a8984e751e04049de5bdb3cad2c0a933 GREEN DerivedAnnotationValueProviderBase.java 15da44b7b92b7fd351aa48422ff5957a2ce34e35 GREEN EStructuralFeatureDescriptor.java 2e14df3830d854bc1693382727b2033b23d0051c GREEN -EStructuralFeatureValueProviderBase.java 1077fa87bfb21dc3041a1695a4247b50b0409fd1 YELLOW +EStructuralFeatureValueProviderBase.java 7e3f41a7e5c22fda63058fb5cd1c8036df0e8a3f YELLOW IAnnotationValueProvider.java d093cb522e7c484420331f0c77690bebe7e131b4 GREEN ValueProviderBase.java e4e866840845346ec99a4304048f5327c4890996 GREEN diff --git a/org.fortiss.tooling.base/src/org/fortiss/tooling/base/annotation/valueprovider/EStructuralFeatureValueProviderBase.java b/org.fortiss.tooling.base/src/org/fortiss/tooling/base/annotation/valueprovider/EStructuralFeatureValueProviderBase.java index 1077fa87b..7e3f41a7e 100644 --- a/org.fortiss.tooling.base/src/org/fortiss/tooling/base/annotation/valueprovider/EStructuralFeatureValueProviderBase.java +++ b/org.fortiss.tooling.base/src/org/fortiss/tooling/base/annotation/valueprovider/EStructuralFeatureValueProviderBase.java @@ -15,9 +15,14 @@ +--------------------------------------------------------------------------*/ package org.fortiss.tooling.base.annotation.valueprovider; +import static java.util.Collections.emptyList; +import static org.fortiss.tooling.kernel.utils.LoggingUtils.error; + import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import org.eclipse.emf.common.util.BasicEList; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EClass; @@ -25,6 +30,7 @@ import org.eclipse.emf.ecore.EDataType; import org.eclipse.emf.ecore.EFactory; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; +import org.fortiss.tooling.base.ToolingBaseActivator; import org.fortiss.tooling.base.annotation.valueprovider.EStructuralFeatureDescriptor.EReferenceScope; import org.fortiss.tooling.base.model.element.IAnnotatedSpecification; import org.fortiss.tooling.base.model.element.IModelElement; @@ -161,7 +167,39 @@ public abstract class EStructuralFeatureValueProviderBase<T extends IAnnotatedSp } } else { throw new Exception( - "setAnnotationValueFromString() is only availabe for EAttributes. Use setAnnotationValue(U value, T specification) instead."); + "setAnnotationValueFromString(String, T) is only availabe for EAttributes. Use setAnnotationValue(U value, T specification) instead."); + } + } + + /** + * Sets a value for a {@link IAnnotatedSpecification} from a {@code Collection<String>} + * representation of the input. + */ + private void setAnnotationValueFromString(Collection<String> collection, T specification) + throws Exception { + EStructuralFeature structuralFeature = + getEStructuralFeatureDescriptor().getEStructuralFeature(specification); + + if(structuralFeature instanceof EAttribute) { + EFactory eFactory = getEFactory(structuralFeature); + EDataType eAttributeType = ((EAttribute)structuralFeature).getEAttributeType(); + EList<Object> values = new BasicEList<>(); + for(String s : collection) { + Object o = null; + try { + o = eFactory.createFromString(eAttributeType, s); + } catch(Exception e) { + error(ToolingBaseActivator.getDefault(), "Could not convert " + + (s != null ? s : "<null>") + " to datatype " + eAttributeType + ".", e); + } + if(o != null) { + values.add(o); + } + } + specification.eSet(structuralFeature, values); + } else { + throw new Exception( + "setAnnotationValueFromString(Collection<String>, T) is only availabe for EAttributes."); } } @@ -175,6 +213,8 @@ public abstract class EStructuralFeatureValueProviderBase<T extends IAnnotatedSp @SuppressWarnings("unchecked") @Override public <U> void setAnnotationValue(U value, T specification) throws Exception { + final EStructuralFeature eStructuralFeature = + getEStructuralFeatureDescriptor().getEStructuralFeature(specification); if(value instanceof String) { // In case the input choice is presented in dynamically managed combo box (i.e., where // new value choices can be entered by the user), the empty String indicates that the @@ -190,9 +230,18 @@ public abstract class EStructuralFeatureValueProviderBase<T extends IAnnotatedSp } setAnnotationValueFromString((String)value, specification); + } else if(value instanceof Collection<?>) { + Collection<?> collection = (Collection<?>)value; + if(collection.isEmpty()) { + specification.eSet(eStructuralFeature, emptyList()); + } else { + if(collection.iterator().next() instanceof String) { + setAnnotationValueFromString((Collection<String>)value, specification); + } else { + specification.eSet(eStructuralFeature, value); + } + } } else { - final EStructuralFeature eStructuralFeature = - getEStructuralFeatureDescriptor().getEStructuralFeature(specification); if(value != null) { specification.eSet(eStructuralFeature, value); } else { -- GitLab