blob: 7a87b6ee16a7f63da68044b5b53bcd71a25c5cee [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011-2013 EclipseSource Muenchen GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Eugen Neufeld - initial API and implementation
******************************************************************************/
package org.eclipse.emf.ecp.view.internal.editor.controls;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.observable.IObserving;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.Enumerator;
import org.eclipse.emf.databinding.EMFUpdateValueStrategy;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.EEnumImpl;
import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
import org.eclipse.emf.ecp.view.spi.core.swt.SimpleControlSWTControlSWTRenderer;
import org.eclipse.emf.ecp.view.spi.model.VControl;
import org.eclipse.emf.ecp.view.spi.provider.ECPTooltipModifierHelper;
import org.eclipse.emf.ecp.view.template.model.VTViewTemplateProvider;
import org.eclipse.emfforms.spi.common.report.ReportService;
import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedException;
import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedReport;
import org.eclipse.emfforms.spi.core.services.databinding.EMFFormsDatabinding;
import org.eclipse.emfforms.spi.core.services.label.EMFFormsLabelProvider;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
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.ui.dialogs.ListDialog;
/**
* A control for defining an value in a leaf condition.
*
* @author Eugen Neufeld
*
*/
// APITODO no api yet
public abstract class ExpectedValueControlRenderer extends SimpleControlSWTControlSWTRenderer {
/**
* Default constructor.
*
* @param vElement the view model element to be rendered
* @param viewContext the view context
* @param reportService The {@link ReportService}
* @param databindingService The {@link EMFFormsDatabinding}
* @param labelProvider The {@link EMFFormsLabelProvider}
* @param viewTemplateProvider The {@link VTViewTemplateProvider}
*/
public ExpectedValueControlRenderer(VControl vElement, ViewModelContext viewContext,
ReportService reportService, EMFFormsDatabinding databindingService, EMFFormsLabelProvider labelProvider,
VTViewTemplateProvider viewTemplateProvider) {
super(vElement, viewContext, reportService, databindingService, labelProvider, viewTemplateProvider);
}
private Label text;
private Shell shell;
private String getTextVariantID() {
return "org_eclipse_emf_ecp_view_editor_controls_ruleattribute"; //$NON-NLS-1$
}
@Override
public void finalizeRendering(Composite parent) {
super.finalizeRendering(parent);
shell = parent.getShell();
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.view.spi.core.swt.SimpleControlSWTControlSWTRenderer#createSWTControl(org.eclipse.swt.widgets.Composite)
*/
@Override
protected Control createSWTControl(Composite parent) {
final Composite composite = new Composite(parent, SWT.NONE);
GridLayoutFactory.fillDefaults().numColumns(2).applyTo(composite);
GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.BEGINNING).applyTo(composite);
final Button bSelectObject = new Button(composite, SWT.PUSH);
bSelectObject.setText("Select Object"); //$NON-NLS-1$
text = new Label(composite, SWT.SINGLE | SWT.BORDER);
text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
text.setData(CUSTOM_VARIANT, getTextVariantID());
bSelectObject.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
super.widgetSelected(e);
onSelectButton(text);
}
});
return text;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.view.spi.core.swt.SimpleControlSWTControlSWTRenderer#createBindings(org.eclipse.swt.widgets.Control,
* org.eclipse.emf.ecore.EStructuralFeature.Setting)
*/
@Override
protected Binding[] createBindings(Control control) throws DatabindingFailedException {
final Label text = (Label) control;
final TargetToModelUpdateStrategy targetToModelUpdateStrategy = new TargetToModelUpdateStrategy();
final ModelToTargetUpdateStrategy modelToTargetUpdateStrategy = new ModelToTargetUpdateStrategy();
final IObservableValue value = org.eclipse.jface.databinding.swt.WidgetProperties.text().observe(text);
final Binding binding = getDataBindingContext().bindValue(value, getModelValue(),
withPreSetValidation(targetToModelUpdateStrategy), modelToTargetUpdateStrategy);
return new Binding[] { binding };
}
/**
* Creates a tooltip binding for this control.
*
* @param text the {@link Text} to bind
* @param modelValue the {@link IObservableValue} to bind
* @param dataBindingContext the {@link DataBindingContext} to use
* @param targetToModel the {@link UpdateValueStrategy} from target to Model
* @param modelToTarget the {@link UpdateValueStrategy} from model to target
* @return the created {@link Binding}
*/
protected Binding createTooltipBinding(Control text, IObservableValue modelValue,
DataBindingContext dataBindingContext, UpdateValueStrategy targetToModel, UpdateValueStrategy modelToTarget) {
final IObservableValue toolTip = org.eclipse.jface.databinding.swt.WidgetProperties.tooltipText().observe(text);
return dataBindingContext.bindValue(toolTip, modelValue, targetToModel, modelToTarget);
}
/**
* Lets the user select an object and returns the selection.
*
* @param attribute the attribute for which an object is needed
* @return the object
*/
protected Object getSelectedObject(EAttribute attribute) {
Object object = null;
// final EAttribute attribute = (EAttribute) structuralFeature;
if (attribute == null) {
showError(shell, "Missing Attribute", //$NON-NLS-1$
"Please select an attribute before attempting to select its value."); //$NON-NLS-1$
return object;
}
Class<?> attribuetClazz = attribute.getEAttributeType().getInstanceClass();
if (attribuetClazz == null) {
if (EcorePackage.eINSTANCE.getEEnum().isInstance(attribute.getEType())) {
attribuetClazz = Enum.class;
final EList<EEnumLiteral> eLiterals = EEnumImpl.class.cast(attribute.getEType()).getELiterals();
final Object[] enumValues = eLiterals.toArray();
final ListDialog ld = new ListDialog(shell);
ld.setLabelProvider(new LabelProvider());
ld.setContentProvider(ArrayContentProvider.getInstance());
ld.setInput(enumValues);
ld.setInitialSelections(new Object[] { enumValues[0] });
ld.setMessage("Please select the enum value to set."); //$NON-NLS-1$
ld.setTitle("Select a value"); //$NON-NLS-1$
final int enumSelectionResult = ld.open();
if (Window.OK == enumSelectionResult) {
object = EEnumLiteral.class.cast(ld.getResult()[0]).getLiteral();
}
} else {
return null;
}
} else {
if (attribuetClazz.isPrimitive()) {
attribuetClazz = getAttributeClass(attribuetClazz);
}
if (Enum.class.isAssignableFrom(attribuetClazz)) {
final Object[] enumValues = attribuetClazz.getEnumConstants();
final ListDialog ld = new ListDialog(shell);
ld.setLabelProvider(new LabelProvider());
ld.setContentProvider(ArrayContentProvider.getInstance());
ld.setInput(enumValues);
ld.setInitialSelections(new Object[] { enumValues[0] });
ld.setMessage("Please select the enum value to set."); //$NON-NLS-1$
ld.setTitle("Select a value"); //$NON-NLS-1$
final int enumSelectionResult = ld.open();
if (Window.OK == enumSelectionResult) {
object = Enumerator.class.cast(ld.getResult()[0]).getLiteral();
}
} else if (String.class.isAssignableFrom(attribuetClazz)
|| Number.class.isAssignableFrom(attribuetClazz)
|| Boolean.class.isAssignableFrom(attribuetClazz)) {
object = promptForValue(shell, object, attribuetClazz);
} else {
showError(shell, "Not primitive Attribute selected", //$NON-NLS-1$
"The selected attribute has a not primitive type. We can't provide you support for it!"); //$NON-NLS-1$
}
}
return object;
}
/**
* Shows an error message to the user.
*
* @param shell The Shell to show the error on
* @param title The title of the error message
* @param description The error description
*/
protected void showError(Shell shell, String title, String description) {
MessageDialog.openError(shell, title, description);
}
/**
* Prompts the user to input a value. Return the original object if the input is cancelled.
*
* @param shell The Shell on which an input dialog will be opened
* @param object The current value
* @param attributeClazz The Class for which we want a value
* @return the prompted value, or the input object if the prompt was cancelled
*/
Object promptForValue(Shell shell, Object object, Class<?> attributeClazz) {
try {
final Constructor<?> constructor = attributeClazz.getConstructor(String.class);
final InputDialog id = new InputDialog(
shell,
"Insert the value", //$NON-NLS-1$
"The value must be parseable by the " //$NON-NLS-1$
+ attributeClazz.getSimpleName()
+ " class. For a double value please use the #.# format. For boolean values 'true' or 'false'.", //$NON-NLS-1$
null, null);
final int inputResult = id.open();
if (Window.OK == inputResult) {
object = constructor.newInstance(id.getValue());
}
if (Boolean.class.isAssignableFrom(attributeClazz) && !Boolean.class.cast(object)
&& !"false".equalsIgnoreCase(id.getValue())) { //$NON-NLS-1$
showError(shell, "Invalid boolean value", //$NON-NLS-1$
"You have entered an invalid value. False has been chosen instead."); //$NON-NLS-1$
}
} catch (final IllegalArgumentException ex) {
openInvalidValueMessage();
} catch (final SecurityException ex) {
openInvalidValueMessage();
} catch (final NoSuchMethodException ex) {
openInvalidValueMessage();
} catch (final InstantiationException ex) {
openInvalidValueMessage();
} catch (final IllegalAccessException ex) {
openInvalidValueMessage();
} catch (final InvocationTargetException ex) {
openInvalidValueMessage();
}
return object;
}
private Class<?> getAttributeClass(Class<?> attributeClazz) {
if (int.class.isAssignableFrom(attributeClazz)) {
attributeClazz = Integer.class;
} else if (long.class.isAssignableFrom(attributeClazz)) {
attributeClazz = Long.class;
} else if (float.class.isAssignableFrom(attributeClazz)) {
attributeClazz = Float.class;
} else if (double.class.isAssignableFrom(attributeClazz)) {
attributeClazz = Double.class;
} else if (boolean.class.isAssignableFrom(attributeClazz)) {
attributeClazz = Boolean.class;
} else if (char.class.isAssignableFrom(attributeClazz)) {
attributeClazz = Character.class;
}
return attributeClazz;
}
private void openInvalidValueMessage() {
MessageDialog.openError(shell, "Invalid value", //$NON-NLS-1$
"You have entered an invalid value. The previsous value will be kept."); //$NON-NLS-1$
}
/**
* Called when the select value button is pressed.
*
* @param text the label which should be used to set the value
*/
protected abstract void onSelectButton(Label text);
/**
* Returns the model object representing the value for this renderer's domain model reference.
*
* @return the EObject
* @throws DatabindingFailedException if the databinding fails
*/
protected EObject getObservedEObject() throws DatabindingFailedException {
final IObservableValue observableValue = getEMFFormsDatabinding()
.getObservableValue(getVElement().getDomainModelReference(), getViewModelContext().getDomainModel());
final EObject result = (EObject) ((IObserving) observableValue).getObserved();
observableValue.dispose();
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecp.view.spi.core.swt.SimpleControlSWTRenderer#getUnsetText()
*/
@Override
protected String getUnsetText() {
// TODO Auto-generated method stub
return null;
}
/**
* The strategy to convert from model to target.
*
* @author Eugen Neufeld
*
*/
protected class ModelToTargetUpdateStrategy extends EMFUpdateValueStrategy {
@Override
public Object convert(Object value) {
final Object converted = value.toString();
if (String.class.isInstance(converted)) {
IObservableValue observableValue;
try {
observableValue = getEMFFormsDatabinding()
.getObservableValue(getVElement().getDomainModelReference(),
getViewModelContext().getDomainModel());
} catch (final DatabindingFailedException ex) {
getReportService().report(new DatabindingFailedReport(ex));
return converted;
}
final InternalEObject internalEObject = (InternalEObject) ((IObserving) observableValue).getObserved();
final EStructuralFeature structuralFeature = (EStructuralFeature) observableValue.getValueType();
observableValue.dispose();
return ECPTooltipModifierHelper.modifyString(String.class.cast(converted),
internalEObject.eSetting(structuralFeature));
}
return converted;
}
}
/**
* The strategy to convert from target to model.
*
* @author Eugen
*
*/
protected class TargetToModelUpdateStrategy extends EMFUpdateValueStrategy {
/**
* Constructor for indicating whether a value is unsettable.
*/
public TargetToModelUpdateStrategy() {
}
/**
* {@inheritDoc}
*/
@Override
public Object convert(Object value) {
return value;
}
}
}