/*******************************************************************************
* Copyright (c) 2006-2013 The RCP Company 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:
* The RCP Company - initial API and implementation
*******************************************************************************/
package com.rcpcompany.uibindings.internal;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.observable.ChangeEvent;
import org.eclipse.core.databinding.observable.IChangeListener;
import org.eclipse.core.databinding.observable.IObservable;
import org.eclipse.core.databinding.observable.IObserving;
import org.eclipse.core.databinding.observable.Observables;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.jface.databinding.swt.ISWTObservableValue;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Dialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Hyperlink;
import org.eclipse.ui.forms.widgets.Section;
import com.rcpcompany.uibindings.BindingMessageSeverity;
import com.rcpcompany.uibindings.BindingState;
import com.rcpcompany.uibindings.Constants;
import com.rcpcompany.uibindings.IArgumentContext;
import com.rcpcompany.uibindings.IBinding;
import com.rcpcompany.uibindings.IBindingDataType;
import com.rcpcompany.uibindings.IBindingMessage;
import com.rcpcompany.uibindings.IControlFactory;
import com.rcpcompany.uibindings.IControlFactoryContext;
import com.rcpcompany.uibindings.IDecoratorProvider;
import com.rcpcompany.uibindings.IManager;
import com.rcpcompany.uibindings.ISourceProviderStateContext;
import com.rcpcompany.uibindings.IUIAttribute;
import com.rcpcompany.uibindings.IUIBindingDecorator;
import com.rcpcompany.uibindings.IUIBindingDecoratorExtender;
import com.rcpcompany.uibindings.IUIBindingDecoratorExtenderDescriptor;
import com.rcpcompany.uibindings.IUIBindingsPackage;
import com.rcpcompany.uibindings.IValueBinding;
import com.rcpcompany.uibindings.IValueBindingCell;
import com.rcpcompany.uibindings.ModelValueKind;
import com.rcpcompany.uibindings.UIBindingsEMFObservables;
import com.rcpcompany.uibindings.internal.bindingMessages.IContextMessageProvider;
import com.rcpcompany.uibindings.observables.DisposePendingEvent;
import com.rcpcompany.uibindings.observables.IDisposePendingListener;
import com.rcpcompany.uibindings.observables.IDisposePendingObservable;
import com.rcpcompany.uibindings.uiAttributes.SimpleUIAttribute;
import com.rcpcompany.uibindings.uiAttributes.VirtualUIAttribute;
import com.rcpcompany.utils.extensionpoints.CEObjectHolder;
import com.rcpcompany.utils.logging.ITimedTask;
import com.rcpcompany.utils.logging.LogUtils;
/**
* <!-- begin-user-doc --> An implementation of the model object '<em><b>Value Binding</b></em>'.
* <!-- end-user-doc -->
* <p>
* The following features are implemented:
* <ul>
* <li>{@link com.rcpcompany.uibindings.internal.ValueBindingImpl#getModelObservable <em>Model
* Observable</em>}</li>
* <li>{@link com.rcpcompany.uibindings.internal.ValueBindingImpl#getModelKind <em>Model Kind</em>}</li>
* <li>{@link com.rcpcompany.uibindings.internal.ValueBindingImpl#getModelObservableValue <em>Model
* Observable Value</em>}</li>
* <li>{@link com.rcpcompany.uibindings.internal.ValueBindingImpl#getModelObservableList <em>Model
* Observable List</em>}</li>
* <li>{@link com.rcpcompany.uibindings.internal.ValueBindingImpl#getModelObject <em>Model Object
* </em>}</li>
* <li>{@link com.rcpcompany.uibindings.internal.ValueBindingImpl#getModelFeature <em>Model Feature
* </em>}</li>
* <li>{@link com.rcpcompany.uibindings.internal.ValueBindingImpl#getDecoratorProvider <em>Decorator
* Provider</em>}</li>
* <li>{@link com.rcpcompany.uibindings.internal.ValueBindingImpl#getDecorator <em>Decorator</em>}</li>
* <li>{@link com.rcpcompany.uibindings.internal.ValueBindingImpl#getUIAttribute <em>UI Attribute
* </em>}</li>
* <li>{@link com.rcpcompany.uibindings.internal.ValueBindingImpl#getUIObservable <em>UI Observable
* </em>}</li>
* <li>{@link com.rcpcompany.uibindings.internal.ValueBindingImpl#getCell <em>Cell</em>}</li>
* <li>{@link com.rcpcompany.uibindings.internal.ValueBindingImpl#getMessagePrefix <em>Message
* Prefix</em>}</li>
* <li>{@link com.rcpcompany.uibindings.internal.ValueBindingImpl#isDynamic <em>Dynamic</em>}</li>
* </ul>
* </p>
*
* @generated
*/
public class ValueBindingImpl extends BindingImpl implements IValueBinding {
/**
* Whether this is a dynamic binding or not.
*/
private boolean isDynamic = false;
@Override
public boolean isDynamic() {
return isDynamic;
}
@Override
public IBindingDataType getDataType() {
/*
* Use cached result until the value changes...
*
* See finish1() and myTypeListener.
*/
if (myCachedDataType == null) {
myCachedDataType = calculateDataType();
}
return myCachedDataType;
}
protected IBindingDataType myCachedDataType = null;
/**
* Calculates the current data type for this binding.
*
* @return the current data type
*/
private IBindingDataType calculateDataType() {
if (!isDynamic()) return super.getDataType();
final IObservableValue ov = getModelObservableValue();
if (ov != null && !ov.isDisposed()) {
/*
* Find the data type based on the OV:
*
* This will use the value type of the inner observable, if set...
*/
IBindingDataType dt = null;
final Object v = ov.getValue();
dt = IBindingDataType.Factory.create(ov);
/*
* If we have both a value and the data type from the OV, AND the two agree exactly,
* then use the data type from the OV, as this is more rich.
*
* Also automatically used for Strings, etc...
*/
if (v != null && dt != null && v.getClass() == dt.getDataType()) return dt;
/*
* Use the real type of the current value... if set...
*/
if (v != null) return IBindingDataType.Factory.create(null, v.getClass());
/*
* If we have the data type, then use this...
*/
if (dt != null) return dt;
}
final IObservableList ol = getModelObservableList();
if (ol != null && !ol.isDisposed()) {
/*
* Find the data type based on the OL:
*/
final IBindingDataType dt = IBindingDataType.Factory.create(ol);
/*
* If we have the data type, then use this...
*/
if (dt != null) return dt;
}
/*
* No observable? We fall back on the static type
*/
return super.getDataType();
}
/**
* Listener used to monitor dynamic bindings for changes in the basic observable value.
*/
private IChangeListener myTypeListener = null;
@Override
public void updateUI() {
// final Control c = getControl();
// if (c == null || c.isDisposed()) return;
/*
* We have to update the values backward to handle the chains set up in
* BaseUIBindingDecorator.decorate()
*/
final ListIterator<Binding> iterator = getDBBindings().listIterator(getDBBindings().size());
while (iterator.hasPrevious()) {
final Binding b = iterator.previous();
/*
* A binding might have been disposed... we don't see that, so lazily update the list
*/
if (b.isDisposed()) {
iterator.remove();
continue;
}
b.updateModelToTarget();
}
}
@Override
public void setFocus() {
Control con = getControl();
if (con == null) {
final IValueBindingCell ci = getCell();
if (ci != null) {
con = ci.setFocus();
}
}
if (con == null) return;
if (con.isDisposed()) return;
/*
* Make sure all Sections and ExpandableComposite are expanded
*/
final Composite top = getContext().getTop();
Control c = con;
while (c != null && !(c instanceof Shell) && c != top) {
if (c instanceof ExpandableComposite) {
final ExpandableComposite comp = (ExpandableComposite) c;
comp.setExpanded(true);
} else if (c instanceof Section) {
final Section comp = (Section) c;
comp.setExpanded(true);
}
final Composite p = c.getParent();
if (p != null) {
if (p instanceof TabFolder) {
final TabFolder comp = (TabFolder) p;
for (final TabItem ti : comp.getItems()) {
if (ti.getControl() == c) {
comp.setSelection(ti);
break;
}
}
} else if (p instanceof CTabFolder) {
final CTabFolder comp = (CTabFolder) p;
for (final CTabItem ti : comp.getItems()) {
if (ti.getControl() == c) {
comp.setSelection(ti);
break;
}
}
}
}
c = p;
}
con.setFocus();
}
@Override
public void addErrorCondition(String error) {
super.addErrorCondition(error);
final Control c = getControl();
if (c == null) return;
final List<String> ecs = getErrorConditions();
final StringBuilder sb = new StringBuilder(300);
for (final String e : ecs) {
sb.append(e).append('\n');
}
c.setBackground(c.getDisplay().getSystemColor(SWT.COLOR_RED));
c.setToolTipText(sb.toString());
}
/**
* The generic "simple" {@link Control} factory.
*/
private static final IControlFactory SIMPLE_CONTROL_FACTORY = new IControlFactory() {
@Override
public Control create(IControlFactoryContext context) {
int style = context.getStyle();
String className = null;
if (context.isCellEditor()) {
className = context.getBinding().getArgument(Constants.ARG_PREFERRED_CELL_EDITOR, String.class, null);
}
if (className == null || className.equals(Control.class.getName())
|| className.equals(Dialog.class.getName())) {
className = context.getBinding().getArgument(Constants.ARG_PREFERRED_CONTROL, String.class, null);
}
if (className == null || className.equals(Control.class.getName())
|| className.equals(Dialog.class.getName())) {
className = Text.class.getName();
}
Class<?> controlClass = null;
try {
controlClass = Class.forName(className);
} catch (final ClassNotFoundException ex) {
LogUtils.throwException(this, "Cannot find class for '" + className + "'", context.getBinding() //$NON-NLS-1$ //$NON-NLS-2$
.getCreationPoint());
}
if (!Control.class.isAssignableFrom(controlClass)) {
LogUtils.throwException(this, "Class not sub-class of Control: '" + className + "'", context //$NON-NLS-1$ //$NON-NLS-2$
.getBinding().getCreationPoint());
}
Constructor<?> constructor = null;
try {
constructor = controlClass.getConstructor(Composite.class, Integer.TYPE);
} catch (final SecurityException ex) {
LogUtils.throwException(this, ex.getMessage() + ": '" + className + "'", context.getBinding() //$NON-NLS-1$ //$NON-NLS-2$
.getCreationPoint());
} catch (final NoSuchMethodException ex) {
LogUtils.throwException(this, "Class does not have (Composite, int) constructor: '" + className + "'", //$NON-NLS-1$ //$NON-NLS-2$
context.getBinding().getCreationPoint());
}
/*
* A little special case code to handle a few corner cases...
*/
if (className.equals("org.eclipse.nebula.widgets.radiogroup.RadioGroup")) { //$NON-NLS-1$
style &= ~SWT.BORDER;
}
if (controlClass.equals(Label.class)) {
style &= ~SWT.BORDER;
}
/*
* Create the control itself..
*/
try {
final Control c = (Control) constructor.newInstance(context.getParent(), style);
final FormToolkit formToolkit = IManager.Factory.getManager().getFormToolkit(context.getParent());
formToolkit.adapt(c, false, true);
return c;
} catch (final Exception ex) {
LogUtils.throwException(this, ex.getClass().getSimpleName()
+ ": " + ex.getMessage() + ": '" + className + "'", context.getBinding() //$NON-NLS-1$ //$NON-NLS-2$
.getCreationPoint());
}
return null;
}
};
@Override
public Control createPreferredControl(final Composite parent, final int style, final boolean cellEditor) {
final IControlFactory factory = getArgument(Constants.ARG_PREFERRED_CONTROL_FACTORY, IControlFactory.class,
SIMPLE_CONTROL_FACTORY);
return factory.create(new IControlFactoryContext() {
@Override
public IValueBinding getBinding() {
return ValueBindingImpl.this;
}
@Override
public Composite getParent() {
return parent;
}
@Override
public int getStyle() {
return style;
}
@Override
public boolean isCellEditor() {
return cellEditor;
}
});
}
@Override
public IValueBinding model(IObservableValue observable) {
setModelObservable(observable);
setStaticDataType(IBindingDataType.Factory.create(observable));
assertTrue(getStaticDataType() != null, "Observable not supported, got value type: " //$NON-NLS-1$
+ observable.getValueType());
// assertTrue(getDataType().getEType() != null,
// "Observable not supported, need EType, got value type: "
// + observable.getValueType());
/*
* As we have this observable from the caller, we should not dispose it later!
*/
myModelObservableDispose = false;
return this;
}
@Override
public IValueBinding model(IObservableList observable) {
setModelObservable(observable);
setStaticDataType(IBindingDataType.Factory.create(observable));
assertTrue(getStaticDataType() != null, "Observable not supported, got value type: " //$NON-NLS-1$
+ observable.getElementType());
myModelObservableDispose = false;
return this;
}
@Override
public IValueBinding model(EObject object, EStructuralFeature feature) {
assertTrue(object != null, "Object must be non-null");
assertTrue(feature != null, "Feature must be non-null");
setStaticDataType(IBindingDataType.Factory.create(object.eClass(), feature));
if (!feature.isMany()) {
setModelObservable(UIBindingsEMFObservables.observeValue(null, getEditingDomain(), object, feature));
} else {
setModelObservable(UIBindingsEMFObservables.observeList(null, getEditingDomain(), object, feature));
// LogUtils.throwException(this, "Many valued feature not supported yet", getCreationPoint()); //$NON-NLS-1$
}
return this;
}
@Override
public IValueBinding model(IObservableValue value, EStructuralFeature feature) {
assertTrue(value != null, "Value must be non-null");
assertTrue(feature != null, "Feature must be non-null");
setStaticDataType(IBindingDataType.Factory.create(value, feature));
if (!feature.isMany()) {
setModelObservable(UIBindingsEMFObservables.observeDetailValue(value.getRealm(), getEditingDomain(), value,
feature));
} else {
// TODO setModelObservable( UIBindingsEMFObservables.observeDetailList(modelObject,
// feature));
LogUtils.throwException(this, "Many valued feature not supported yet", getCreationPoint()); //$NON-NLS-1$
}
return this;
}
@Override
public IValueBinding ui(IUIAttribute attribute) {
setUIAttribute(attribute);
return this;
}
@Override
public IValueBinding ui(ISWTObservableValue observable) {
return ui(new SimpleUIAttribute(observable.getWidget(), null, observable));
}
@Override
public IValueBinding ui(Widget widget) {
return ui(widget, ""); //$NON-NLS-1$
}
@Override
public IValueBinding ui(Widget widget, String attribute) {
return ui(IManager.Factory.getManager().createUIAttribute(widget, attribute));
}
@Override
public IValueBinding arg(String name, Object value) {
assertTrue(name != null, "name must be non-null"); //$NON-NLS-1$
getArguments().put(name.intern(), value);
return this;
}
@Override
public IValueBinding args(Map<String, Object> arguments) {
setArguments(arguments);
return this;
}
@Override
public IValueBinding readonly() {
return arg(Constants.ARG_READONLY, true);
}
@Override
public IValueBinding id(String id) {
setId(id);
return this;
}
@Override
public IValueBinding dynamic() {
return arg(Constants.ARG_DYNAMIC, true);
}
@Override
public IValueBinding label(String label) {
return arg(Constants.ARG_LABEL, label);
}
@Override
public IValueBinding validValues(IObservableList list) {
return arg(Constants.ARG_VALID_VALUES, list);
}
@Override
public IValueBinding validValues(List<?> list, Object type) {
return validValues(Observables.staticObservableList(list, type));
}
@Override
public IValueBinding validValues(EObject obj, EReference ref) {
return validValues(UIBindingsEMFObservables.observeList(getEditingDomain(), obj, ref));
}
@Override
public IValueBinding type(String type) {
return arg(Constants.ARG_TYPE, type);
}
@Override
public Class<?> getUIType() {
if (getUIAttribute() == null) return null;
return (Class<?>) getUIAttribute().getCurrentValue().getValueType();
}
@Override
public void finish1() {
assertTrue(getStaticDataType() != null, "No data type set"); //$NON-NLS-1$
assertTrue(getUIAttribute() != null, "No UI attribute set"); //$NON-NLS-1$
assertTrue(getModelObservable() != null, "No model observable set"); //$NON-NLS-1$
assertTrue(!getModelObservable().isDisposed(), "Model observable disposed"); //$NON-NLS-1$
isDynamic = getArgument(ARG_DYNAMIC, Boolean.class, false);
final IObservableValue ov = getModelObservableValue();
if (ov != null && isDynamic) {
/*
* Forget whatever value we have found until now.
*/
myCachedDataType = null;
myTypeListener = new IChangeListener() {
@Override
public void handleChange(ChangeEvent event) {
/*
* Make sure that we recalculate the data type based on the current value
*/
myCachedDataType = null;
decorateIfNeeded();
}
};
ov.addChangeListener(myTypeListener);
}
final Control control = getControl();
if (control != null) {
final String attribute = getUIAttribute().getAttribute();
if (attribute == null || attribute.length() == 0) {
registerWidget(control);
}
if (Activator.getDefault().ASSERTS_CONTROLS) {
control.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
control.removeDisposeListener(this);
switch (getState()) {
case DISPOSE_PENDING:
case DISPOSED:
break;
default:
LogUtils.debug(ValueBindingImpl.this, ValueBindingImpl.this + ": control disposed");
break;
}
}
});
}
}
decorateIfNeeded();
/*
* This need to be as late in the process as possible! And only for the observables we want
* to dispose later.
*/
if (getModelObservable() != null && myModelObservableDispose) {
IManager.Factory.getManager().startMonitorObservableDispose(getModelObservable(), this);
}
/*
* If we have a model observable that implement the pending interface, we add a listener
* that will dispose the binding as needed...
*/
if (getModelObservable() instanceof IDisposePendingObservable) {
final IDisposePendingObservable dpov = (IDisposePendingObservable) getModelObservable();
dpov.addDisposePendingListener(new IDisposePendingListener() {
@Override
public void disposePending(DisposePendingEvent event) {
dpov.removeDisposePendingListener(this);
dispose();
}
});
}
}
/**
* The previous type data type for this binding.
*/
private IBindingDataType myPreviousDynamicDataType = null;
/**
* Decorates the binding.
* <p>
* Re-decorates if the {@link #getDynamicDataType() type data type} changes.
*/
protected void decorateIfNeeded() {
final IBindingDataType newDynamicDataType = getDataType();
if (myPreviousDynamicDataType == newDynamicDataType) return;
final ITimedTask task = ITimedTask.Factory.start("decorateIfNeeded ", this);
/*
* Clean up the old decoration as well as the old cached argument
*/
if (getDecorator() != null) {
getDecorator().dispose();
clearCachedArguments();
}
final Class<?> modelValueType = getModelType();
final ModelValueKind modelValueKind = getModelKind();
final Class<?> uiValueType = getUIType();
final String type = getType();
task.subTask("getProvider");
final IDecoratorProvider provider = IManager.Factory.getManager().getProvider(modelValueType, modelValueKind,
uiValueType, type);
myPreviousDynamicDataType = newDynamicDataType;
/*
* Add a note about missing support
*/
if (provider == null) {
setDecorator(null);
addErrorCondition("model: " + modelValueType + "(" + modelValueKind + ") ui: " + uiValueType + " type: " + type //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ " --> no decorator"); //$NON-NLS-1$
return;
}
task.subTask("setProvider");
setDecoratorProvider(provider);
task.subTask("getDecorator");
setDecorator(provider.getDecorator());
if (getDecorator() == null) {
addErrorCondition("model: " + modelValueType + "(" + modelValueKind + ") ui: " + uiValueType + " type: " + type //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ " --> no decorator created"); //$NON-NLS-1$
return;
}
/*
* Initialize the decorator. If anything goes wrong, reset to null..
*/
try {
task.subTask("decorator.init");
getDecorator().init(this);
} catch (final RuntimeException ex) {
// Already reported
setDecorator(null);
return;
} catch (final Exception ex) {
setDecorator(null);
LogUtils.error(this, ex);
return;
}
task.subTask("decorator.decorate");
getDecorator().decorate();
task.subTask("end");
// task.end();
}
@Override
public boolean isChangeable() {
String sb = null;
boolean res = false;
try {
/*
* Can now be called before in state OK - e.g. from the form creator
*/
// assertTrue(getDecorator() != null, "Called before in OK state"); //$NON-NLS-1$
/*
* TODO Not sure about this! In general, the reaction to non-changeable fields stinks!
*/
// if (eIsSet(IUIBindingsPackage.Literals.BINDING__ERROR_CONDITIONS))
// return false;
final IUIAttribute attribute = getUIAttribute();
if (attribute != null) {
if (!attribute.isChangeable()) {
if (Activator.getDefault().TRACE_ISCHANGEABLE) {
sb = attribute + ": not changeable";
}
return false;
}
final Widget widget = attribute.getWidget();
if (widget != null && (!widget.isDisposed() && (widget.getStyle() & SWT.READ_ONLY) == SWT.READ_ONLY)) {
if (Activator.getDefault().TRACE_ISCHANGEABLE) {
sb = widget + " r/o";
}
return false;
}
}
if (!getStaticDataType().isChangeable()) {
if (Activator.getDefault().TRACE_ISCHANGEABLE) {
sb = getStaticDataType() + ": not changeable";
}
return false;
}
if (!getDataType().isChangeable()) {
if (Activator.getDefault().TRACE_ISCHANGEABLE) {
sb = getDataType() + ": not changeable";
}
return false;
}
/*
* If the model observable is a constant IOV, then this binding must be constant.
*/
final IObservableValue ov = getModelObservableValue();
if (ov != null) {
final String ovClassName = ov.getClass().getName();
/*
* These tests are to not depend on any internal classes :-)
*/
if (ovClassName.equals("org.eclipse.core.internal.databinding.observable.ConstantObservableValue")) {
if (Activator.getDefault().TRACE_ISCHANGEABLE) {
sb = ov + ": constant";
}
return false;
}
if (ovClassName.equals("org.eclipse.core.internal.databinding.observable.UnmodifiableObservableValue")) {
if (Activator.getDefault().TRACE_ISCHANGEABLE) {
sb = ov + ": unmodifiable";
}
return false;
}
}
if (getArgument(Constants.ARG_READONLY, Boolean.class, Boolean.FALSE)) {
if (Activator.getDefault().TRACE_ISCHANGEABLE) {
sb = "READONLY";
}
return false;
}
final IUIBindingDecorator d = getDecorator();
if (d != null && !d.isChangeable()) {
if (Activator.getDefault().TRACE_ISCHANGEABLE) {
sb = d + ": not changeable";
}
return false;
}
res = true;
return res;
} finally {
if (Activator.getDefault().TRACE_ISCHANGEABLE) {
LogUtils.debug(this, this + "=" + res + (sb != null ? (": " + sb) : ""));
}
}
}
@Override
public void dispose() {
switch (getState()) {
case DISPOSED:
return;
default:
break;
}
setState(BindingState.DISPOSE_PENDING);
disposeServices();
final Control control = getControl();
if (control != null) {
// TODO: move to separate if(widget...)
final String attribute = getUIAttribute().getAttribute();
if (attribute == null || attribute.length() == 0) {
unregisterWidget(control);
}
}
if (getDecorator() != null) {
getDecorator().dispose();
}
if (dbBindings != null) {
for (final Binding b : dbBindings) {
b.dispose();
}
dbBindings.clear();
}
super.dispose();
final IObservableValue ov = getModelObservableValue();
if (ov != null && myTypeListener != null) {
ov.removeChangeListener(myTypeListener);
myTypeListener = null;
}
if (getModelObservable() != null && myModelObservableDispose) {
IManager.Factory.getManager().stopMonitorObservableDispose(getModelObservable());
getModelObservable().dispose();
}
setModelObservable(null);
if (getUIAttribute() != null) {
getUIAttribute().dispose();
}
}
/**
* The default value of the '{@link #getModelObservable() <em>Model Observable</em>}' attribute.
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @see #getModelObservable()
* @generated
* @ordered
*/
protected static final IObservable MODEL_OBSERVABLE_EDEFAULT = null;
/**
* The cached value of the '{@link #getModelObservable() <em>Model Observable</em>}' attribute.
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @see #getModelObservable()
* @generated
* @ordered
*/
protected IObservable modelObservable = MODEL_OBSERVABLE_EDEFAULT;
/**
* The default value of the '{@link #getModelKind() <em>Model Kind</em>}' attribute. <!--
* begin-user-doc --> <!-- end-user-doc -->
*
* @see #getModelKind()
* @generated
* @ordered
*/
protected static final ModelValueKind MODEL_KIND_EDEFAULT = ModelValueKind.VALUE;
/**
* The default value of the '{@link #getModelObservableValue() <em>Model Observable Value</em>}'
* attribute. <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @see #getModelObservableValue()
* @generated
* @ordered
*/
protected static final IObservableValue MODEL_OBSERVABLE_VALUE_EDEFAULT = null;
/**
* The default value of the '{@link #getModelObservableList() <em>Model Observable List</em>}'
* attribute. <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @see #getModelObservableList()
* @generated
* @ordered
*/
protected static final IObservableList MODEL_OBSERVABLE_LIST_EDEFAULT = null;
/**
* <code>true</code> if {@link #modelObservable} should be disposed by {@link #dispose()}.
*/
private boolean myModelObservableDispose = true;
/**
* The cached value of the '{@link #getDecoratorProvider() <em>Decorator Provider</em>}'
* reference. <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @see #getDecoratorProvider()
* @generated
* @ordered
*/
protected IDecoratorProvider decoratorProvider;
/**
* The cached value of the '{@link #getDecorator() <em>Decorator</em>}' reference. <!--
* begin-user-doc --> <!-- end-user-doc -->
*
* @see #getDecorator()
* @generated
* @ordered
*/
protected IUIBindingDecorator decorator;
/**
* The cached value of the '{@link #getUIAttribute() <em>UI Attribute</em>}' reference. <!--
* begin-user-doc --> <!-- end-user-doc -->
*
* @see #getUIAttribute()
* @generated
* @ordered
*/
protected IUIAttribute uiAttribute;
/**
* The default value of the '{@link #getUIObservable() <em>UI Observable</em>}' attribute. <!--
* begin-user-doc --> <!-- end-user-doc -->
*
* @see #getUIObservable()
* @generated
* @ordered
*/
protected static final IObservableValue UI_OBSERVABLE_EDEFAULT = null;
/**
* The cached value of the '{@link #getCell() <em>Cell</em>}' reference. <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @see #getCell()
* @generated
* @ordered
*/
protected IValueBindingCell cell;
/**
* The default value of the '{@link #getMessagePrefix() <em>Message Prefix</em>}' attribute.
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @see #getMessagePrefix()
* @generated
* @ordered
*/
protected static final String MESSAGE_PREFIX_EDEFAULT = null;
/**
* The default value of the '{@link #isDynamic() <em>Dynamic</em>}' attribute. <!--
* begin-user-doc --> <!-- end-user-doc -->
*
* @see #isDynamic()
* @generated
* @ordered
*/
protected static final boolean DYNAMIC_EDEFAULT = false;
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated
*/
public ValueBindingImpl() {
super();
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated
*/
@Override
protected EClass eStaticClass() {
return IUIBindingsPackage.Literals.VALUE_BINDING;
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated
*/
@Override
public IObservable getModelObservable() {
return modelObservable;
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated
*/
@Override
public void setModelObservable(IObservable newModelObservable) {
final IObservable oldModelObservable = modelObservable;
modelObservable = newModelObservable;
if (eNotificationRequired()) {
eNotify(new ENotificationImpl(this, Notification.SET, IUIBindingsPackage.VALUE_BINDING__MODEL_OBSERVABLE,
oldModelObservable, modelObservable));
}
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated NOT
*/
@Override
public ModelValueKind getModelKind() {
final IObservable o = getModelObservable();
if (o instanceof IObservableValue) return ModelValueKind.VALUE;
if (o instanceof IObservableList) return ModelValueKind.LIST;
return ModelValueKind.VALUE;
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated NOT
*/
@Override
public IObservableValue getModelObservableValue() {
final IObservable observable = getModelObservable();
if (!(observable instanceof IObservableValue)) return null;
return (IObservableValue) observable;
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated NOT
*/
@Override
public IObservableList getModelObservableList() {
final IObservable observable = getModelObservable();
if (!(observable instanceof IObservableList)) return null;
return (IObservableList) observable;
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated NOT
*/
@Override
public EObject getModelObject() {
if (isDisposed()) {
LogUtils.debug(this, "disposed");
return null;
}
if (getModelObservable() instanceof IObserving) {
final Object observed = ((IObserving) getModelObservable()).getObserved();
if (observed instanceof EObject) return (EObject) observed;
}
final IObservableValue ov = getModelObservableValue();
if (ov == null || ov.isDisposed()) return null;
final Object observed = ov.getValue();
if (!(observed instanceof EObject)) return null;
return (EObject) observed;
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated NOT
*/
@Override
public EStructuralFeature getModelFeature() {
final Object valueType = getDataType().getValueType();
if (valueType instanceof EStructuralFeature) return (EStructuralFeature) valueType;
return null;
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated
*/
@Override
public IDecoratorProvider getDecoratorProvider() {
return decoratorProvider;
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated
*/
@Override
public void setDecoratorProvider(IDecoratorProvider newDecoratorProvider) {
final IDecoratorProvider oldDecoratorProvider = decoratorProvider;
decoratorProvider = newDecoratorProvider;
if (eNotificationRequired()) {
eNotify(new ENotificationImpl(this, Notification.SET, IUIBindingsPackage.VALUE_BINDING__DECORATOR_PROVIDER,
oldDecoratorProvider, decoratorProvider));
}
}
/**
* Used to prevent a recursive all to {@link #getArgument(String, Class, Object)}.
* <p>
* Can otherwise happen when {@link IUIBindingDecoratorExtender#isEnabled(IValueBinding)} uses
* {@link #getArgument(String, Class, Object)} to decide whether the extender is enablewd.
*/
private boolean addDecoratorExtenderArgumentsInvoked = false;
@Override
public <ArgumentType> void addDecoratorExtenderArguments(IArgumentContext<ArgumentType> context) {
if (addDecoratorExtenderArgumentsInvoked) return;
try {
addDecoratorExtenderArgumentsInvoked = true;
for (final IUIBindingDecoratorExtenderDescriptor d : IManager.Factory.getManager().getDecoratorExtenders()) {
/*
* Optimalization: not many extenders will have arguments
*/
if (!d.hasArguments()) {
continue;
}
final CEObjectHolder<IUIBindingDecoratorExtender> factory = d.getFactory();
final IUIBindingDecoratorExtender extender = factory.getObject();
if (extender == null) {
LogUtils.error(factory.getConfigurationElement(), "Cannot create extender"); //$NON-NLS-1$
continue;
}
if (!extender.isEnabled(this)) {
continue;
}
IManager.Factory.getManager().addArgumentProviderArguments(d, context);
if (context.isResultFound()) return;
}
} finally {
addDecoratorExtenderArgumentsInvoked = false;
}
}
@Override
public <ArgumentType> void addDecoratorProviderArguments(IArgumentContext<ArgumentType> context) {
IManager.Factory.getManager().addArgumentProviderArguments(getDecoratorProvider(), context);
}
@Override
public IBinding getParentBinding() {
if (getCell() != null && getCell().getColumnBinding() != null) return getCell().getColumnBinding();
return super.getParentBinding();
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated
*/
@Override
public IUIBindingDecorator getDecorator() {
return decorator;
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated
*/
@Override
public void setDecorator(IUIBindingDecorator newDecorator) {
final IUIBindingDecorator oldDecorator = decorator;
decorator = newDecorator;
if (eNotificationRequired()) {
eNotify(new ENotificationImpl(this, Notification.SET, IUIBindingsPackage.VALUE_BINDING__DECORATOR,
oldDecorator, decorator));
}
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated
*/
@Override
public IUIAttribute getUIAttribute() {
return uiAttribute;
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated
*/
@Override
public void setUIAttribute(IUIAttribute newUIAttribute) {
final IUIAttribute oldUIAttribute = uiAttribute;
uiAttribute = newUIAttribute;
if (eNotificationRequired()) {
eNotify(new ENotificationImpl(this, Notification.SET, IUIBindingsPackage.VALUE_BINDING__UI_ATTRIBUTE,
oldUIAttribute, uiAttribute));
}
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated NOT
*/
@Override
public IObservableValue getUIObservable() {
return getUIAttribute().getCurrentValue();
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated
*/
@Override
public IValueBindingCell getCell() {
return cell;
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated
*/
@Override
public void setCell(IValueBindingCell newCell) {
final IValueBindingCell oldCell = cell;
cell = newCell;
if (eNotificationRequired()) {
eNotify(new ENotificationImpl(this, Notification.SET, IUIBindingsPackage.VALUE_BINDING__CELL, oldCell, cell));
}
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated NOT
*/
@Override
public String getMessagePrefix() {
String messagePrefix = null;
/*
* If this binding has a control, then look for the previous label widget and use the text
* of that
*/
final Control c = getControl();
if (c != null) {
final Composite parent = c.getParent();
final Control[] siblings = parent.getChildren();
for (int i = 0; i < siblings.length; i++) {
if (siblings[i] == c) {
// this is us - go backward until you hit a label-like widget
for (int j = i - 1; j >= 0; j--) {
final Control label = siblings[j];
String ltext = null;
if (label instanceof Label) {
ltext = ((Label) label).getText();
} else if (label instanceof Hyperlink) {
ltext = ((Hyperlink) label).getText();
} else if (label instanceof CLabel) {
ltext = ((CLabel) label).getText();
}
if (ltext != null) {
if (!ltext.endsWith(":")) { //$NON-NLS-1$
messagePrefix = ltext + ": "; //$NON-NLS-1$
} else {
messagePrefix = ltext + " "; //$NON-NLS-1$
}
return messagePrefix;
}
}
break;
}
}
}
final IValueBindingCell ci = getCell();
if (ci != null) {
messagePrefix = ci.getMessagePrefix();
if (messagePrefix != null) return messagePrefix;
}
/*
* Fall back on nothing!
*/
messagePrefix = ""; //$NON-NLS-1$
return messagePrefix;
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated
*/
@Override
public Object eGet(int featureID, boolean resolve, boolean coreType) {
switch (featureID) {
case IUIBindingsPackage.VALUE_BINDING__MODEL_OBSERVABLE:
return getModelObservable();
case IUIBindingsPackage.VALUE_BINDING__MODEL_KIND:
return getModelKind();
case IUIBindingsPackage.VALUE_BINDING__MODEL_OBSERVABLE_VALUE:
return getModelObservableValue();
case IUIBindingsPackage.VALUE_BINDING__MODEL_OBSERVABLE_LIST:
return getModelObservableList();
case IUIBindingsPackage.VALUE_BINDING__MODEL_OBJECT:
return getModelObject();
case IUIBindingsPackage.VALUE_BINDING__MODEL_FEATURE:
return getModelFeature();
case IUIBindingsPackage.VALUE_BINDING__DECORATOR_PROVIDER:
return getDecoratorProvider();
case IUIBindingsPackage.VALUE_BINDING__DECORATOR:
return getDecorator();
case IUIBindingsPackage.VALUE_BINDING__UI_ATTRIBUTE:
return getUIAttribute();
case IUIBindingsPackage.VALUE_BINDING__UI_OBSERVABLE:
return getUIObservable();
case IUIBindingsPackage.VALUE_BINDING__CELL:
return getCell();
case IUIBindingsPackage.VALUE_BINDING__MESSAGE_PREFIX:
return getMessagePrefix();
case IUIBindingsPackage.VALUE_BINDING__DYNAMIC:
return isDynamic();
}
return super.eGet(featureID, resolve, coreType);
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated
*/
@Override
public void eSet(int featureID, Object newValue) {
switch (featureID) {
case IUIBindingsPackage.VALUE_BINDING__MODEL_OBSERVABLE:
setModelObservable((IObservable) newValue);
return;
case IUIBindingsPackage.VALUE_BINDING__DECORATOR_PROVIDER:
setDecoratorProvider((IDecoratorProvider) newValue);
return;
case IUIBindingsPackage.VALUE_BINDING__DECORATOR:
setDecorator((IUIBindingDecorator) newValue);
return;
case IUIBindingsPackage.VALUE_BINDING__UI_ATTRIBUTE:
setUIAttribute((IUIAttribute) newValue);
return;
case IUIBindingsPackage.VALUE_BINDING__CELL:
setCell((IValueBindingCell) newValue);
return;
}
super.eSet(featureID, newValue);
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated
*/
@Override
public void eUnset(int featureID) {
switch (featureID) {
case IUIBindingsPackage.VALUE_BINDING__MODEL_OBSERVABLE:
setModelObservable(MODEL_OBSERVABLE_EDEFAULT);
return;
case IUIBindingsPackage.VALUE_BINDING__DECORATOR_PROVIDER:
setDecoratorProvider((IDecoratorProvider) null);
return;
case IUIBindingsPackage.VALUE_BINDING__DECORATOR:
setDecorator((IUIBindingDecorator) null);
return;
case IUIBindingsPackage.VALUE_BINDING__UI_ATTRIBUTE:
setUIAttribute((IUIAttribute) null);
return;
case IUIBindingsPackage.VALUE_BINDING__CELL:
setCell((IValueBindingCell) null);
return;
}
super.eUnset(featureID);
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated
*/
@Override
public boolean eIsSet(int featureID) {
switch (featureID) {
case IUIBindingsPackage.VALUE_BINDING__MODEL_OBSERVABLE:
return MODEL_OBSERVABLE_EDEFAULT == null ? modelObservable != null : !MODEL_OBSERVABLE_EDEFAULT
.equals(modelObservable);
case IUIBindingsPackage.VALUE_BINDING__MODEL_KIND:
return getModelKind() != MODEL_KIND_EDEFAULT;
case IUIBindingsPackage.VALUE_BINDING__MODEL_OBSERVABLE_VALUE:
return MODEL_OBSERVABLE_VALUE_EDEFAULT == null ? getModelObservableValue() != null
: !MODEL_OBSERVABLE_VALUE_EDEFAULT.equals(getModelObservableValue());
case IUIBindingsPackage.VALUE_BINDING__MODEL_OBSERVABLE_LIST:
return MODEL_OBSERVABLE_LIST_EDEFAULT == null ? getModelObservableList() != null
: !MODEL_OBSERVABLE_LIST_EDEFAULT.equals(getModelObservableList());
case IUIBindingsPackage.VALUE_BINDING__MODEL_OBJECT:
return getModelObject() != null;
case IUIBindingsPackage.VALUE_BINDING__MODEL_FEATURE:
return getModelFeature() != null;
case IUIBindingsPackage.VALUE_BINDING__DECORATOR_PROVIDER:
return decoratorProvider != null;
case IUIBindingsPackage.VALUE_BINDING__DECORATOR:
return decorator != null;
case IUIBindingsPackage.VALUE_BINDING__UI_ATTRIBUTE:
return uiAttribute != null;
case IUIBindingsPackage.VALUE_BINDING__UI_OBSERVABLE:
return UI_OBSERVABLE_EDEFAULT == null ? getUIObservable() != null : !UI_OBSERVABLE_EDEFAULT
.equals(getUIObservable());
case IUIBindingsPackage.VALUE_BINDING__CELL:
return cell != null;
case IUIBindingsPackage.VALUE_BINDING__MESSAGE_PREFIX:
return MESSAGE_PREFIX_EDEFAULT == null ? getMessagePrefix() != null : !MESSAGE_PREFIX_EDEFAULT
.equals(getMessagePrefix());
case IUIBindingsPackage.VALUE_BINDING__DYNAMIC:
return isDynamic() != DYNAMIC_EDEFAULT;
}
return super.eIsSet(featureID);
}
/**
* <!-- begin-user-doc --> <!-- end-user-doc -->
*
* @generated NOT
*/
@Override
public String toString() {
return super.toString();
}
@Override
public Widget getWidget() {
final IUIAttribute attribute = getUIAttribute();
if (attribute == null) return null;
return attribute.getWidget();
}
@Override
public Control getControl() {
final Widget widget = getWidget();
if (widget instanceof Control) return (Control) widget;
return null;
}
@Override
protected String getBaseType() {
String baseType = getDataType() != null ? getDataType().getBaseType() : "*UNINIT*";
final Widget w = getWidget();
if (w != null) {
baseType += "<=>" + w;
final String attribute = getUIAttribute().getAttribute();
if (attribute != null && attribute.length() > 0) {
baseType += "(" + attribute + ")";
}
} else if (getUIAttribute() instanceof VirtualUIAttribute) {
baseType += "<=>VIRTUAL";
}
return baseType;
}
@Override
public void updateBinding() {
final IUIBindingDecorator d = getDecorator();
if (d != null) {
d.update();
}
}
@Override
public void updateBinding(Object[] objects) {
if (objects != null) {
boolean found = false;
final EObject modelObject = getModelObject();
for (final Object object : objects) {
if (object == modelObject) {
found = true;
break;
}
}
if (!found) return;
}
updateBinding();
}
@Override
public void updateSourceProviderState(ISourceProviderStateContext context) {
context.putSourceValue(Constants.SOURCES_ACTIVE_BINDING, this);
context.setSelectionProvider(null);
switch (getModelKind()) {
case VALUE:
final IObservableValue v = getModelObservableValue();
final IBindingDataType dataType = getDataType();
context.putSourceValue(Constants.SOURCES_ACTIVE_BINDING_TYPE, getType());
context.putSourceValue(Constants.SOURCES_ACTIVE_BINDING_MODEL_OBJECT, getModelObject());
context.putSourceValue(Constants.SOURCES_ACTIVE_BINDING_FEATURE, getModelFeature());
context.putSourceValue(Constants.SOURCES_ACTIVE_BINDING_RO, !isChangeable()); // TODO
// ??
context.putSourceValue(Constants.SOURCES_ACTIVE_BINDING_UNSETTABLE, dataType.isUnsettable());
context.putSourceValue(Constants.SOURCES_ACTIVE_BINDING_VALUE, v.getValue());
context.putSourceValue(Constants.SOURCES_ACTIVE_BINDING_VALUE_DISPLAY, getUIAttribute().getCurrentValue()
.getValue());
break;
case LIST:
// TODO
break;
default:
LogUtils.error(this, "Unknown kind: " + getModelKind());
}
context.addObservedValue(getUIAttribute().getCurrentValue());
}
@Override
public boolean isEClassFeature(Class<? extends EObject> objClass, EStructuralFeature sf) {
if (sf != getModelFeature()) return false;
if (objClass != null && !objClass.isInstance(getModelObject())) return false;
return true;
}
@Override
public Collection<EObject> getSelection() {
final EObject object = getModelObject();
if (object == null) return Collections.emptyList();
return Collections.singletonList(object);
}
@Override
public List<String> getErrors() {
final IContextMessageProvider messageProvider = getService(IContextMessageProvider.class);
final List<String> errors = new ArrayList<String>();
if (messageProvider != null) {
for (final Object mo : messageProvider.getMessages()) {
final IBindingMessage m = (IBindingMessage) mo;
if (m.getSeverity() != BindingMessageSeverity.ERROR) {
continue;
}
errors.add(m.getMessage());
}
} else {
for (final Binding b : getDBBindings()) {
final IStatus status = (IStatus) b.getValidationStatus().getValue();
if (status.getSeverity() != IStatus.ERROR) {
continue;
}
errors.add(status.getMessage());
}
}
return errors;
}
} // ValueBindingImpl