blob: 8cfa9c383bc2071307026b355b95909aa308c370 [file] [log] [blame]
/**
* Copyright (c) 2008, 2013 itemis AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* itemis AG - Initial API and implementation
* Lorenzo Bettini - refactoring for EmfParsley
*
*/
package org.eclipse.emf.parsley.binding;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.databinding.EMFDataBindingContext;
import org.eclipse.emf.databinding.EMFProperties;
import org.eclipse.emf.databinding.edit.EMFEditProperties;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.impl.EEnumImpl;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.parsley.EmfParsleyActivator;
import org.eclipse.emf.parsley.edit.IEditingStrategy;
import org.eclipse.emf.parsley.edit.TextUndoRedo;
import org.eclipse.emf.parsley.runtime.util.PolymorphicDispatcher;
import org.eclipse.jface.bindings.keys.KeyStroke;
import org.eclipse.jface.bindings.keys.ParseException;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.jface.databinding.viewers.ViewersObservables;
import org.eclipse.jface.fieldassist.ContentProposalAdapter;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.FieldDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
import org.eclipse.jface.fieldassist.SimpleContentProposalProvider;
import org.eclipse.jface.fieldassist.TextContentAdapter;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.swt.SWT;
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.Text;
import com.google.common.base.Predicate;
import com.google.inject.Inject;
import com.google.inject.Provider;
/**
*
* Creates Control for an {@link EStructuralFeature}
*
* @author Dennis Huebner initial code
* @author Lorenzo Bettini refactoring for EmfParsley
*
*/
public abstract class AbstractControlFactory extends AbstractWidgetFactory {
@Inject
protected Provider<ILabelProvider> labelProviderProvider;
@Inject
protected ProposalCreator proposalcreator;
protected EObject owner;
protected Resource resource;
protected EditingDomain domain;
protected EMFDataBindingContext edbc;
protected boolean readonly = false;
public static final String EOBJECT_KEY = EcorePackage.Literals.EOBJECT
.getName();
public static final String ESTRUCTURALFEATURE_KEY = EcorePackage.Literals.ESTRUCTURAL_FEATURE
.getName();
private PolymorphicDispatcher.ErrorHandler<ControlObservablePair> control_errorHandler = new PolymorphicDispatcher.NullErrorHandler<ControlObservablePair>();
private PolymorphicDispatcher.ErrorHandler<Control> createControl_errorHandler = new PolymorphicDispatcher.NullErrorHandler<Control>();
private PolymorphicDispatcher.ErrorHandler<IObservableValue> observeable_errorHandler = new PolymorphicDispatcher.NullErrorHandler<IObservableValue>();
public AbstractControlFactory() {
}
public boolean isReadonly() {
return readonly;
}
public void setReadonly(boolean readonly) {
this.readonly = readonly;
}
/**
* Initializes this factory for creating {@link Control}s with
* Data Binding.
*
* The passed {@link EditingDomain} can be null; in that case
* Data Binding will be implemented through {@link EMFProperties}, instead
* of {@link EMFEditProperties}. If the {@link EditingDomain} is null
* views and editors will not be notified about changes to the passed
* {@link EObject}. This is useful when you want to create {@link Control}s
* that act on a copy of the original object (see also {@link IEditingStrategy}).
*
* @param domain
* @param owner
* @param parent
* @see IEditingStrategy
*/
public void init(EditingDomain domain, EObject owner, Composite parent) {
super.init(parent);
this.edbc = new EMFDataBindingContext();
this.domain = domain;
this.owner = owner;
}
protected Resource getResource() {
if (resource == null) {
resource = owner.eResource();
}
return resource;
}
public Control create(EStructuralFeature feature) {
Control control = null;
control = polymorphicCreateControl(feature);
if (control == null) {
if (feature.isMany()) {
control = bindList(feature);
} else {
control = bindValue(feature);
}
setupControl(feature, control);
}
registerUndo(control);
return control;
}
protected void registerUndo(Control control) {
if (control instanceof Text) {
Text text = (Text) control;
new TextUndoRedo(text);
}
}
protected Predicate<Method> getControlPredicate(EStructuralFeature feature) {
String methodName = "control_" + owner.eClass().getName() + "_"
+ feature.getName();
return PolymorphicDispatcher.Predicates.forName(methodName, 1);
}
protected Control bindList(final EStructuralFeature feature) {
IObservableValue source = createFeatureObserveable(feature);
ControlObservablePair retValAndTargetPair = createControlForList(feature);
Control retVal = retValAndTargetPair.getControl();
IObservableValue target = retValAndTargetPair.getObservableValue();
Binding binding = edbc.bindValue(target, source);
binding.updateModelToTarget();
return retVal;
}
private IObservableValue createFeatureObserveable(final EStructuralFeature feature) {
IObservableValue source=polymorphicCreateObserveable(domain, owner, feature);
if(source==null){
if (domain != null){
source = EMFEditProperties.value(domain, feature).observe(owner);
}else{
source = EMFProperties.value(feature).observe(owner);
}
}
return source;
}
protected ControlObservablePair createControlForList(
final EStructuralFeature feature) {
ControlObservablePair result = polymorphicGetObservableControl(feature);
if (result != null)
return result;
MultipleFeatureControl mfc = new MultipleFeatureControl(parent,
this, labelProviderProvider.get(), owner,
feature, proposalcreator, readonly);
IObservableValue target = new MultipleFeatureControlObservable(mfc);
ControlObservablePair retValAndTargetPair = new ControlObservablePair(
mfc, target);
return retValAndTargetPair;
}
private Control bindValue(EStructuralFeature feature) {
IObservableValue featureObservable = createFeatureObserveable(feature);
Control control = polymorphicCreateControl(feature, featureObservable);
if (control != null)
return control;
ControlObservablePair retValAndTargetPair = createControlAndObservableValue(feature);
Control retVal = retValAndTargetPair.getControl();
IObservableValue controlObservable = retValAndTargetPair
.getObservableValue();
if (retVal != null && controlObservable != null) {
edbc.bindValue(controlObservable, featureObservable, null, null);
}
return retVal;
}
protected ControlObservablePair createControlAndObservableValue(
EStructuralFeature feature) {
ControlObservablePair result = polymorphicGetObservableControl(feature);
if (result != null)
return result;
if (isBooleanFeature(feature)) {
return createControlAndObservableValueForBoolean();
} else {
return createControlAndObservableValueForNonBooleanFeature(feature);
}
}
protected boolean isBooleanFeature(EStructuralFeature feature) {
return feature.getEType().equals(EcorePackage.Literals.EBOOLEAN)
|| feature.getEType().equals(
EcorePackage.Literals.EBOOLEAN_OBJECT)
|| (feature.getEType() instanceof EDataType && (feature
.getEType().getInstanceClass() == Boolean.class || feature
.getEType().getInstanceClass() == Boolean.TYPE));
}
protected ControlObservablePair createControlAndObservableValueForBoolean() {
ControlObservablePair retValAndTargetPair = new ControlObservablePair();
Button b = createButton("", SWT.CHECK);
b.setEnabled(!readonly);
retValAndTargetPair.setControl(b);
retValAndTargetPair.setObservableValue(SWTObservables
.observeSelection(b));
return retValAndTargetPair;
}
protected boolean hasPredefinedProposals(EStructuralFeature feature) {
return feature instanceof EReference
|| feature.getEType() instanceof EEnumImpl;
}
protected ControlObservablePair createControlAndObservableValueForNonBooleanFeature(
EStructuralFeature feature) {
List<?> proposals = null;
if (!readonly)
proposals = createProposals(feature);
if (hasPredefinedProposals(feature) && !readonly) {
return createControlAndObservableWithPredefinedProposals(proposals);
} else {
if (readonly && feature instanceof EReference)
return createControlAndObservableForEObject();
return createControlAndObservableWithoutPredefinedProposals(proposals);
}
}
public List<?> createProposals(EStructuralFeature feature) {
proposalcreator.setResource(getResource());
return proposalcreator.proposals(owner, feature);
}
protected ControlObservablePair createControlAndObservableWithPredefinedProposals(
List<?> proposals) {
ComboViewer combo = createComboViewer(SWT.READ_ONLY);
combo.setContentProvider(new ArrayContentProvider());
combo.setLabelProvider(labelProviderProvider.get());
combo.setInput(proposals);
ControlObservablePair retValAndTargetPair = new ControlObservablePair();
retValAndTargetPair.setControl(combo.getCombo());
retValAndTargetPair.setObservableValue(ViewersObservables
.observeSingleSelection(combo));
return retValAndTargetPair;
}
protected ControlObservablePair createControlAndObservableWithoutPredefinedProposals(
List<?> proposals) {
ControlObservablePair retValAndTargetPair = new ControlObservablePair();
Text t = createText("");
t.setEditable(!readonly);
addContentProposalAdapter(t, proposals);
retValAndTargetPair.setControl(t);
retValAndTargetPair.setObservableValue(SWTObservables.observeText(t,
SWT.Modify));
return retValAndTargetPair;
}
protected ControlObservablePair createControlAndObservableForEObject() {
ControlObservablePair retValAndTargetPair = new ControlObservablePair();
Text t = createText("");
t.setEditable(!readonly);
retValAndTargetPair.setControl(t);
retValAndTargetPair.setObservableValue(new EObjectTextObservable(
labelProviderProvider.get(), t));
return retValAndTargetPair;
}
protected void addContentProposalAdapter(Text t, List<?> proposals) {
if (proposals != null && !proposals.isEmpty()) {
// TODO prevent adding null to a list, for example a Collection Type
while (proposals.remove(null)) {
// clear null entries
}
ControlDecoration field = new ControlDecoration(t, SWT.BORDER);
FieldDecoration requiredFieldIndicator = FieldDecorationRegistry
.getDefault().getFieldDecoration(
FieldDecorationRegistry.DEC_CONTENT_PROPOSAL);
field.setImage(requiredFieldIndicator.getImage());
field.setDescriptionText(requiredFieldIndicator.getDescription());
KeyStroke keyStroke = null;
String string = "Ctrl+Space";
try {
keyStroke = KeyStroke.getInstance(string);
} catch (ParseException e) {
EmfParsleyActivator
.getDefault()
.getLog()
.log(new Status(IStatus.ERROR,
EmfParsleyActivator.PLUGIN_ID,
"Error while parse: " + string, e));
}
new ContentProposalAdapter(t, new TextContentAdapter(),
new SimpleContentProposalProvider(proposals
.toArray(new String[] {})), keyStroke,
null);
}
}
private void setupControl(EStructuralFeature f, Control c) {
// disable unchangeable and unserializable
if (c != null) {
// don't override readonly behavior
if (c.isEnabled())
c.setEnabled(f.isChangeable()
&& (!(f.getEType() instanceof EDataType && !((EDataType) f
.getEType()).isSerializable())));
c.setData(AbstractControlFactory.ESTRUCTURALFEATURE_KEY, f);
c.setData(AbstractControlFactory.EOBJECT_KEY, owner);
c.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
}
}
public void dispose() {
edbc.dispose();
parent.dispose();
// toolkit.dispose();
}
public Composite getParent() {
return parent;
}
private ControlObservablePair polymorphicGetObservableControl(EStructuralFeature element) {
PolymorphicDispatcher<ControlObservablePair> dispatcher = new PolymorphicDispatcher<ControlObservablePair>(
Collections.singletonList(this),
getObservableControlPredicate(element), control_errorHandler) {
@Override
protected ControlObservablePair handleNoSuchMethod(Object... params) {
if (PolymorphicDispatcher.NullErrorHandler.class
.equals(control_errorHandler.getClass()))
return null;
return super.handleNoSuchMethod(params);
}
};
return dispatcher.invoke(element);
}
/**
* Polymorphically invokes a create_EClass_feature(DataBindingContext, IObservableValue)
* @param element
* @param featureObservable
* @return
*/
private Control polymorphicCreateControl(EStructuralFeature element,
IObservableValue featureObservable) {
PolymorphicDispatcher<Control> dispatcher = createPolymorphicDispatcherForCreateControl(
element, 2);
return dispatcher.invoke(edbc, featureObservable);
}
/**
* Polymorphically invokes a create_EClass_feature(EObject)
* @param element
* @return
*/
private Control polymorphicCreateControl(EStructuralFeature element) {
PolymorphicDispatcher<Control> dispatcher = createPolymorphicDispatcherForCreateControl(
element, 1);
return dispatcher.invoke(owner);
}
private PolymorphicDispatcher<Control> createPolymorphicDispatcherForCreateControl(
EStructuralFeature element, int numOfParams) {
return new PolymorphicDispatcher<Control>(
Collections.singletonList(this),
getCreateControlMethodPredicate(element, numOfParams),
createControl_errorHandler) {
@Override
protected Control handleNoSuchMethod(Object... params) {
if (PolymorphicDispatcher.NullErrorHandler.class
.equals(createControl_errorHandler.getClass()))
return null;
return super.handleNoSuchMethod(params);
}
};
}
protected Predicate<Method> getObservableControlPredicate(
EStructuralFeature feature) {
String methodName = "control_"
+ feature.getEContainingClass().getName() + "_"
+ feature.getName();
return PolymorphicDispatcher.Predicates.forName(methodName, 1);
}
protected Predicate<Method> getCreateControlMethodPredicate(
EStructuralFeature feature, int numOfParams) {
String methodName = "control_"
+ feature.getEContainingClass().getName() + "_"
+ feature.getName();
return PolymorphicDispatcher.Predicates.forName(methodName, numOfParams);
}
private IObservableValue polymorphicCreateObserveable(EditingDomain domain, EObject element,
EStructuralFeature feature) {
PolymorphicDispatcher<IObservableValue> dispatcher = new PolymorphicDispatcher<IObservableValue>(
Collections.singletonList(this),
getCreateObserveablePredicate(feature),
new PolymorphicDispatcher.NullErrorHandler<IObservableValue>()) {
@Override
protected IObservableValue handleNoSuchMethod(Object... params) {
if (PolymorphicDispatcher.NullErrorHandler.class
.equals(observeable_errorHandler.getClass()))
return null;
return super.handleNoSuchMethod(params);
}
};
return dispatcher.invoke(domain, element);
}
protected Predicate<Method> getCreateObserveablePredicate(
EStructuralFeature feature) {
String methodName = "observe_"
+ feature.getEContainingClass().getName() + "_"
+ feature.getName();
return PolymorphicDispatcher.Predicates.forName(methodName, 2);
}
}