Commit c9d571fe authored by Alexander Diewald's avatar Alexander Diewald
Browse files

Improve the annotation action "framework".

- Add a mechanism to create input dialogs: The action descriptor passes a map-type object that defines the expected parameters.
- Refine the container classes.
- Add an annotation action that allows to create random valued entries for a selected annotation build around an average value and the given bounds.
refs 2361
parent 9b274ef6
......@@ -70,6 +70,8 @@ import org.fortiss.tooling.base.annotation.IAnnotationValueService;
import org.fortiss.tooling.base.annotation.valueprovider.AnnotationInstSpec;
import org.fortiss.tooling.base.annotation.valueprovider.IAnnotationValueProvider;
import org.fortiss.tooling.base.annotation.valueprovider.IAnnotationValueProvider.AnnotationActionEntry;
import org.fortiss.tooling.base.annotation.valueprovider.IAnnotationValueProvider.AnnotationActionInputParameters;
import org.fortiss.tooling.base.annotation.valueprovider.IAnnotationValueProvider.AnnotationActionParameters;
import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
import org.fortiss.tooling.base.model.element.IModelElement;
import org.fortiss.tooling.base.ui.annotation.editingsupport.EditingSupportFactory;
......@@ -81,6 +83,7 @@ import org.fortiss.tooling.base.ui.annotation.labelprovider.ElementNameLabelProv
import org.fortiss.tooling.base.ui.annotation.view.AnnotationViewPartBase;
import org.fortiss.tooling.base.ui.annotation.view.generic.filter.AnnotationFilter;
import org.fortiss.tooling.base.ui.annotation.view.generic.filter.AnnotationFilterWidget;
import org.fortiss.tooling.base.ui.dialog.MultiFieldInputDialog;
import org.fortiss.tooling.kernel.extension.data.ITopLevelElement;
import org.fortiss.tooling.kernel.service.IPersistencyService;
......@@ -447,7 +450,7 @@ public class GenericAnnotationView extends AnnotationViewPartBase {
*/
private <T extends IAnnotatedSpecification, S extends IModelElement> void
addContextMenuEntryListener(MenuItem entry, AnnotationActionEntry actionEntry,
BiConsumer<AnnotationInstSpec<T>, T> action,
BiConsumer<AnnotationInstSpec<T>, AnnotationActionParameters<T>> action,
AnnotationInstSpec<T> entrySpecification,
Stream<AnnotationEntry> annotationEntries) {
entry.addSelectionListener(new SelectionAdapter() {
......@@ -460,9 +463,19 @@ public class GenericAnnotationView extends AnnotationViewPartBase {
IPersistencyService.getInstance().getTopLevelElementFor(
entry.getModelElement());
AnnotationActionInputParameters parameterList = null;
if(actionEntry.getInputParameters() != null) {
MultiFieldInputDialog paramDialog =
new MultiFieldInputDialog(rootComposite.getShell());
paramDialog.loadAllInputFields(actionEntry.getInputParameters());
paramDialog.setBlockOnOpen(true);
paramDialog.open();
parameterList = paramDialog.getParameterValues();
}
Runnable r =
createAnnoationActionRunnable(entry, action, actionEntry,
entrySpecification, annotationEntries);
entrySpecification, parameterList, annotationEntries);
boolean updateEnabled = isUpdateEnabled();
setUpdateEnabled(false);
......@@ -487,13 +500,17 @@ public class GenericAnnotationView extends AnnotationViewPartBase {
* {@link AnnotationActionEntry} describing the parameters of the action.
* @param specificationType
* Type of the Annotation.
* @param inputParameters
* List of input parameters for the executed BiConsumer.
* @param annotationEntries
* {@link AnnotationEntry}s that are currently visible in the AnnotationView.
* @return Runnable that is executed when the corresponding menu entry is selected.
*/
private <T extends IAnnotatedSpecification> Runnable createAnnoationActionRunnable(
AnnotationEntry entry, BiConsumer<AnnotationInstSpec<T>, T> action,
AnnotationEntry entry,
BiConsumer<AnnotationInstSpec<T>, AnnotationActionParameters<T>> action,
AnnotationActionEntry actionEntry, AnnotationInstSpec<T> specificationType,
AnnotationActionInputParameters inputParameters,
Stream<AnnotationEntry> annotationEntries) {
Runnable r;
if(actionEntry.getScope() == AnnotationActionEntry.ActionScope.ALL_VISIBLE_ITEMS) {
......@@ -503,15 +520,18 @@ public class GenericAnnotationView extends AnnotationViewPartBase {
e -> e.getSpecification(specificationType
.getAnnotatedSpecificationType())).collect(
Collectors.toList())) {
action.accept(specificationType, annotation);
action.accept(specificationType, new AnnotationActionParameters<>(
annotation, inputParameters));
}
};
} else if(actionEntry.getScope() == AnnotationActionEntry.ActionScope.SINGLE_ITEM) {
T annotation =
entry.getSpecification(specificationType.getAnnotatedSpecificationType());
r = () -> {
action.accept(specificationType, annotation);
};
r =
() -> {
action.accept(specificationType, new AnnotationActionParameters<>(
annotation, inputParameters));
};
} else {
r = () -> {/* NOP */
};
......@@ -529,7 +549,7 @@ public class GenericAnnotationView extends AnnotationViewPartBase {
final Stream<AnnotationEntry> annotationEntriesMatchingFilter =
getLastAnnotationEntries().stream()
.filter(e -> annotationFilter.passesRowFilter(e));
for(Pair<AnnotationActionEntry, BiConsumer<AnnotationInstSpec<T>, T>> ctxMenuEntry : valueProvider
for(Pair<AnnotationActionEntry, BiConsumer<AnnotationInstSpec<T>, AnnotationActionParameters<T>>> ctxMenuEntry : valueProvider
.getContextMenuEntries()) {
MenuItem mItem = new MenuItem(contextMenu, SWT.None);
mItem.setText(ctxMenuEntry.getFirst().getName());
......
/*--------------------------------------------------------------------------+
$Id: codetemplates.xml 1 2011-01-01 00:00:01Z hoelzl $
| |
| Copyright 2016 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.dialog;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.lang.math.NumberUtils;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.fortiss.tooling.base.annotation.valueprovider.IAnnotationValueProvider.AnnotationActionInputParameter;
import org.fortiss.tooling.base.annotation.valueprovider.IAnnotationValueProvider.AnnotationActionInputParameters;
/**
* {@link Dialog} that has a list of {@link Label}s and {@link Text} fields whose number matches the
* size of the loaded {@link AnnotationActionInputParameters}. The entries of the loaded
* {@link AnnotationActionInputParameters} define the displayed text and the expected value types.
*
* For now only a subset of {@link Number}s are allowed as values.
*
* @author diewald
* @author $Author: hoelzl $
* @version $Rev: 18709 $
* @ConQAT.Rating RED Hash: 88FE42EEA15A676930456D288A66C084
*/
public class MultiFieldInputDialog extends Dialog {
/** Container composite for the fields of this dialog. */
private Composite container;
/** List of textual descriptors and the expected types. */
private AnnotationActionInputParameters parameters;
/** List of entered parameters. */
private Map<Text, AnnotationActionInputParameter> fieldParamAssoc = new HashMap<>();
/** Constructor. */
public MultiFieldInputDialog(Shell parentShell) {
super(parentShell);
}
/** {@inheritDoc} */
@Override
protected Control createDialogArea(Composite parent) {
Composite area = (Composite)super.createDialogArea(parent);
container = new Composite(area, SWT.NONE);
container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
GridLayout layout = new GridLayout(2, false);
container.setLayout(layout);
addAllInputFields(parameters);
return area;
}
/**
* Loads the given list of input parameter descriptors whose gui elements are created after the
* dialog has been opened.
*/
public void loadAllInputFields(AnnotationActionInputParameters inputParameterList) {
parameters = inputParameterList;
}
/** Adds all labels and input fields described in the {@code parameterList}. */
protected void addAllInputFields(AnnotationActionInputParameters inputParameterList) {
for(AnnotationActionInputParameter curParameter : inputParameterList.keySet()) {
addInputFields(curParameter);
}
}
/**
* Add the gui elements of an input field consisting of a textual descriptor and a Text box.
*
* @param parameterSpec
* describes the expected input.
*/
protected void addInputFields(AnnotationActionInputParameter parameterSpec) {
Label parameterLabel = new Label(container, SWT.NONE);
parameterLabel.setText(parameterSpec.getFirst());
GridData gridInputParameter = new GridData();
gridInputParameter.grabExcessHorizontalSpace = true;
gridInputParameter.horizontalAlignment = GridData.FILL;
Text parameterInput = new Text(container, SWT.BORDER);
parameterInput.setLayoutData(gridInputParameter);
fieldParamAssoc.put(parameterInput, parameterSpec);
}
/** Returns the list of parameters entered in the dialog. */
public AnnotationActionInputParameters getParameterValues() {
return parameters;
}
/** {@inheritDoc} */
@Override
protected void okPressed() {
for(Entry<Text, AnnotationActionInputParameter> curInputField : fieldParamAssoc.entrySet()) {
String enteredValue = curInputField.getKey().getText();
@SuppressWarnings("unchecked") Class<Number> expectedType =
(Class<Number>)curInputField.getValue().getSecond();
Number value = null;
// TODO: Throw an Exception if the enteredString is not a number.
if(NumberUtils.isNumber(enteredValue)) {
// TODO: Avoid this cast to make the method more type-safe.
if(expectedType.isAssignableFrom(Byte.class)) {
value = Byte.valueOf(enteredValue);
} else if(expectedType.isAssignableFrom(Double.class)) {
value = Double.valueOf(enteredValue);
} else if(expectedType.isAssignableFrom(Float.class)) {
value = Float.valueOf(enteredValue);
} else if(expectedType.isAssignableFrom(Integer.class)) {
value = Integer.valueOf(enteredValue);
} else if(expectedType.isAssignableFrom(Long.class)) {
value = Long.valueOf(enteredValue);
} else if(expectedType.isAssignableFrom(Short.class)) {
value = Short.valueOf(enteredValue);
}
}
parameters.put(curInputField.getValue().getFirst(), expectedType, value);
}
super.okPressed();
}
}
......@@ -17,11 +17,10 @@ $Id: codetemplates.xml 1 2011-01-01 00:00:01Z hoelzl $
+--------------------------------------------------------------------------*/
package org.fortiss.tooling.base.annotation.valueprovider;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.BiConsumer;
import java.util.stream.DoubleStream;
import org.conqat.lib.commons.collections.Pair;
import org.fortiss.tooling.base.ToolingBaseActivator;
import org.fortiss.tooling.base.annotation.valueprovider.IAnnotationValueProvider.AnnotationActionEntry.ActionScope;
import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
......@@ -41,15 +40,14 @@ public interface DefaultAnnotationContextActions<T extends IAnnotatedSpecificati
/** {@inheritDoc} */
@Override
public default List<Pair<AnnotationActionEntry, BiConsumer<AnnotationInstSpec<T>, T>>>
getContextMenuEntries() {
List<Pair<AnnotationActionEntry, BiConsumer<AnnotationInstSpec<T>, T>>> retList =
new ArrayList<>();
public default AnnotationActions<T> getContextMenuEntries() {
AnnotationActions<T> retList = new AnnotationActions<>();
// Propagates values from the selected entry to all entries in the same ITopLevelElement
// with the same type.
BiConsumer<AnnotationInstSpec<T>, T> propagateToAll =
(AnnotationInstSpec<T> spec, T annotation) -> {
BiConsumer<AnnotationInstSpec<T>, AnnotationActionParameters<T>> propagateToAll =
(AnnotationInstSpec<T> spec, AnnotationActionParameters<T> parameters) -> {
T annotation = parameters.getAnnotation();
Object value =
spec.getEntry().getSpecificationValue(
spec.getAnnotatedSpecificationType(), spec.getInstanceKey());
......@@ -61,12 +59,14 @@ public interface DefaultAnnotationContextActions<T extends IAnnotatedSpecificati
setAnnotationValue(value, annotation, spec.getInstanceKey());
}
} catch(Exception e) {
// @CodeFormatterOff
LoggingUtils
.error(ToolingBaseActivator.getDefault(),
"Could not set the value of the annotation " +
annotation.getName() +
" when trying to propagate the value form the selected annotation.",
e);
.error(ToolingBaseActivator.getDefault(),
"Could not set the value of the annotation " +
annotation.getName() +
" when trying to propagate the value form the selected annotation.",
e);
// @CodeFormatterOn
}
};
......@@ -74,8 +74,50 @@ public interface DefaultAnnotationContextActions<T extends IAnnotatedSpecificati
new AnnotationActionEntry("Propagate to all", ActionScope.ALL_VISIBLE_ITEMS);
// Add the propagate action to the list of context menu entries.
retList.add(new Pair<AnnotationActionEntry, BiConsumer<AnnotationInstSpec<T>, T>>(
propToAllDescriptor, propagateToAll));
retList.add(new AnnotationAction<T>(propToAllDescriptor, propagateToAll));
// Generates random entries around the given value and the fixed bound for all visible
// annotations of the passed type.
BiConsumer<AnnotationInstSpec<T>, AnnotationActionParameters<T>> randomValuePropagation =
(AnnotationInstSpec<T> spec, AnnotationActionParameters<T> parameters) -> {
T annotation = parameters.getAnnotation();
AnnotationActionInputParameters params = parameters.getInputParameters();
Double value = params.get("Average Value:", Double.class);
Double bound = params.get("Bound: ", Double.class);
Random rand = new Random();
DoubleStream doubleStream = rand.doubles(value - bound, value + bound);
double randVal = doubleStream.findAny().getAsDouble();
try {
if(spec.getInstanceKey() == null || spec.getInstanceKey().equals("")) {
setAnnotationValue(randVal, annotation);
} else {
setAnnotationValue(randVal, annotation, spec.getInstanceKey());
}
} catch(Exception e) {
// @CodeFormatterOff
LoggingUtils
.error(ToolingBaseActivator.getDefault(),
"Could not set the value of the annotation " +
annotation.getName() +
" when trying to propagate the value form the selected annotation.",
e);
// @CodeFormatterOn
}
};
AnnotationActionInputParameters randInputParameters = new AnnotationActionInputParameters();
randInputParameters.add("Average Value:", Double.class);
randInputParameters.add("Bound: ", Double.class);
AnnotationActionEntry randValuePropDescriptor =
new AnnotationActionEntry("Create random values", ActionScope.ALL_VISIBLE_ITEMS,
randInputParameters);
// Add the propagate action to the list of context menu entries.
retList.add(new AnnotationAction<T>(randValuePropDescriptor, randomValuePropagation));
return retList;
}
......
......@@ -17,9 +17,12 @@ $Id$
+--------------------------------------------------------------------------*/
package org.fortiss.tooling.base.annotation.valueprovider;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import org.conqat.lib.commons.collections.Pair;
......@@ -92,12 +95,25 @@ public interface IAnnotationValueProvider<T extends IAnnotatedSpecification> ext
/** {@link AnnotationEntry} elements to apply the associated action to. */
private ActionScope scope;
/** List of required input parameters to the annotation specific action. */
private AnnotationActionInputParameters inputParameters;
/** Constructor defining the name and the scope of the corresponding action. */
public AnnotationActionEntry(String entryName, ActionScope scope) {
this.entryName = entryName;
this.scope = scope;
}
/**
* Constructor defining the name and the scope of the corresponding action. Additionally, a
* list of required input parameters types and their display name must be given.
*/
public AnnotationActionEntry(String entryName, ActionScope scope,
AnnotationActionInputParameters inputParameters) {
this(entryName, scope);
this.inputParameters = inputParameters;
}
/** Returns the name of the corresponding action. */
public String getName() {
return entryName;
......@@ -107,6 +123,140 @@ public interface IAnnotationValueProvider<T extends IAnnotatedSpecification> ext
public ActionScope getScope() {
return scope;
}
/** Returns a list of expected input parameters. */
public AnnotationActionInputParameters getInputParameters() {
return inputParameters;
}
}
/**
* Defines the the parameters required by an annotation action. Further the parameters can be
* passed to the action using the same object (this).
*/
public class AnnotationActionInputParameters {
/** References the action-specific parameters and their values. */
Map<AnnotationActionInputParameter, Object> parameterValueMap = new LinkedHashMap<>();
/**
* Adds the given parameter identifier (Name and Type). Until no associated value is set via
* the {@link #put(String, Class, Number)} method, the associated value will be {@code null}
* .
*
* @param parameterName
* Name of the parameter.
* @param parameterType
* Type of the parameter.
*/
public void add(String parameterName, Class<? extends Number> parameterType) {
parameterValueMap.put(new AnnotationActionInputParameter(parameterName, parameterType),
null);
}
/**
* Adds the value associated with the given parameter description and value type.
*
* @param parameterName
* Name of the parameter.
* @param parameterType
* Type of the parameter.
* @param value
* Value to be added.
*/
public <T extends Number> void put(String parameterName, Class<T> parameterType, T value) {
parameterValueMap.put(new AnnotationActionInputParameter(parameterName, parameterType),
value);
}
/**
* Returns the value associated with the given parameter description and value type.
*
* @param parameterName
* Name of the parameter.
* @param parameterType
* Type of the parameter.
* @return the contained value, or {@code null} if none exists.
*/
@SuppressWarnings("unchecked")
public <T extends Number> T get(String parameterName, Class<T> parameterType) {
Object retVal =
parameterValueMap.get(new AnnotationActionInputParameter(parameterName,
parameterType));
if(parameterType.isAssignableFrom(retVal.getClass())) {
return (T)retVal;
}
return null;
}
/** Returns the keys of {@code this} AnnotationActionInputParameter. */
public Set<AnnotationActionInputParameter> keySet() {
return parameterValueMap.keySet();
}
}
/** Wrapper class to specify a single input parameter of an annotation action. */
public class AnnotationActionInputParameter extends Pair<String, Class<? extends Number>> {
/** Constructor. */
public AnnotationActionInputParameter(String first, Class<? extends Number> second) {
super(first, second);
}
}
/**
* Container class to specify the parameters passed to a {@link BiConsumer} executed on
* annotations.
*/
public class AnnotationActionParameters<T extends IAnnotatedSpecification> {
/** {@link IAnnotatedSpecification} for which the action was triggered. */
private T annotation;
/** List of input parameters for the executing {@link BiConsumer}. */
private AnnotationActionInputParameters inputParameters;
/**
* Constructor.
*
* @param annotation
* {@link IAnnotatedSpecification} for which the action was triggered.
* @param inputParameters
* Possibly empty list of input parameters to the executing {@link BiConsumer}.
*/
public AnnotationActionParameters(T annotation,
AnnotationActionInputParameters inputParameters) {
this.annotation = annotation;
this.inputParameters = inputParameters;
}
/** Returns the {@link IAnnotatedSpecification} for which the action was triggered. */
public T getAnnotation() {
return annotation;
}
/** Returns the list of input parameters to the executed {@link BiConsumer} */
public AnnotationActionInputParameters getInputParameters() {
return inputParameters;
}
}
/** Class to define actions executed on defined annotation. */
public class AnnotationAction<T extends IAnnotatedSpecification>
extends
Pair<AnnotationActionEntry, BiConsumer<AnnotationInstSpec<T>, AnnotationActionParameters<T>>> {
/** Constructor. */
public AnnotationAction(AnnotationActionEntry first,
BiConsumer<AnnotationInstSpec<T>, AnnotationActionParameters<T>> second) {
super(first, second);
}
}
/**
* {@link IAnnotationValueProvider.AnnotationAction}s defined for a specific
* {@link IAnnotatedSpecification}.
*/
public class AnnotationActions<T extends IAnnotatedSpecification> extends
ArrayList<AnnotationAction<T>> {
// Wrapper class.
}
/**
......@@ -248,8 +398,7 @@ public interface IAnnotationValueProvider<T extends IAnnotatedSpecification> ext
* context menu entries that are available for specific entries of an
* {@link IAnnotatedSpecification}.
*/
default List<Pair<AnnotationActionEntry, BiConsumer<AnnotationInstSpec<T>, T>>>
getContextMenuEntries() {
return Collections.emptyList();
default AnnotationActions<T> getContextMenuEntries() {
return new AnnotationActions<>();
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment