495670: initial implementation of Detail widgets validation

Change-Id: I2c5b16abe6f93c1d92d7f336fa54eecd0689352a
Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=495670
Signed-off-by: Francesco Guidieri <francesco.guidieri@gmail.com>
diff --git a/plugins/org.eclipse.emf.parsley.common/META-INF/MANIFEST.MF b/plugins/org.eclipse.emf.parsley.common/META-INF/MANIFEST.MF
index c2686aa..d83aeea 100644
--- a/plugins/org.eclipse.emf.parsley.common/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.emf.parsley.common/META-INF/MANIFEST.MF
@@ -26,6 +26,7 @@
  org.eclipse.emf.parsley.edit.ui.dnd,
  org.eclipse.emf.parsley.edit.ui.provider,
  org.eclipse.emf.parsley.handlers,
+ org.eclipse.emf.parsley.internal.databinding;x-internal:=true,
  org.eclipse.emf.parsley.internal.edit.ui.dnd,
  org.eclipse.emf.parsley.listeners,
  org.eclipse.emf.parsley.menus,
@@ -53,6 +54,7 @@
  org.eclipse.jface.action,
  org.eclipse.jface.bindings,
  org.eclipse.jface.bindings.keys,
+ org.eclipse.jface.databinding.fieldassist,
  org.eclipse.jface.databinding.swt,
  org.eclipse.jface.databinding.viewers,
  org.eclipse.jface.dialogs,
diff --git a/plugins/org.eclipse.emf.parsley.common/src/org/eclipse/emf/parsley/composite/AbstractControlFactory.java b/plugins/org.eclipse.emf.parsley.common/src/org/eclipse/emf/parsley/composite/AbstractControlFactory.java
index 40ce302..e8caeb8 100644
--- a/plugins/org.eclipse.emf.parsley.common/src/org/eclipse/emf/parsley/composite/AbstractControlFactory.java
+++ b/plugins/org.eclipse.emf.parsley.common/src/org/eclipse/emf/parsley/composite/AbstractControlFactory.java
@@ -24,20 +24,24 @@
 import org.eclipse.emf.ecore.EStructuralFeature;
 import org.eclipse.emf.ecore.EcorePackage;
 import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.util.Diagnostician;
 import org.eclipse.emf.edit.command.SetCommand;
 import org.eclipse.emf.edit.domain.EditingDomain;
 import org.eclipse.emf.parsley.EmfParsleyActivator;
 import org.eclipse.emf.parsley.EmfParsleyConstants;
 import org.eclipse.emf.parsley.edit.IEditingStrategy;
 import org.eclipse.emf.parsley.edit.TextUndoRedo;
+import org.eclipse.emf.parsley.internal.databinding.EmfValidationTargetToModelUpdateValueStrategy;
 import org.eclipse.emf.parsley.runtime.util.PolymorphicDispatcherExtensions;
 import org.eclipse.emf.parsley.ui.provider.ComboViewerLabelProvider;
 import org.eclipse.emf.parsley.ui.provider.FeatureLabelCaptionProvider;
 import org.eclipse.emf.parsley.util.DatabindingUtil;
 import org.eclipse.emf.parsley.util.FeatureHelper;
+import org.eclipse.emf.parsley.validation.DiagnosticUtil;
 import org.eclipse.emf.parsley.widgets.IWidgetFactory;
 import org.eclipse.jface.bindings.keys.KeyStroke;
 import org.eclipse.jface.bindings.keys.ParseException;
+import org.eclipse.jface.databinding.fieldassist.ControlDecorationSupport;
 import org.eclipse.jface.databinding.viewers.ViewersObservables;
 import org.eclipse.jface.fieldassist.ContentProposalAdapter;
 import org.eclipse.jface.fieldassist.ControlDecoration;
@@ -95,6 +99,12 @@
 	@Inject
 	private ProposalCreator proposalCreator;
 
+	@Inject
+	private Diagnostician diagnostician;
+
+	@Inject
+	private DiagnosticUtil diagnosticUtil;
+
 	private EObject owner;
 	private Resource resource;
 	private EditingDomain domain;
@@ -332,12 +342,16 @@
 				.getObservableValue();
 
 		if (controlObservable != null) {
-			edbc.bindValue(controlObservable, featureObservable, null, null);
+			EmfValidationTargetToModelUpdateValueStrategy targetToModelUpdateValueStrategy = 
+			new EmfValidationTargetToModelUpdateValueStrategy(owner,feature,diagnostician, diagnosticUtil);
+
+			Binding bindValue = edbc.bindValue(controlObservable, featureObservable, targetToModelUpdateValueStrategy, null);
+			ControlDecorationSupport.create(bindValue, SWT.TOP | SWT.LEFT);
 		}
 
 		return retVal;
 	}
-
+	
 	protected ControlObservablePair createControlAndObservableValue(
 			EStructuralFeature feature, boolean withPolymorphicDispatch) {
 		if (withPolymorphicDispatch) {
@@ -471,7 +485,9 @@
 			// set default layout data if not already set by a custom
 			// polymorphic implementation or from the DSL
 			if (c.getLayoutData()==null) {
-				c.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+				GridData deafultLayout = new GridData(GridData.FILL_HORIZONTAL);
+				deafultLayout.horizontalIndent=10;
+				c.setLayoutData(deafultLayout);
 			}
 		}
 	}
diff --git a/plugins/org.eclipse.emf.parsley.common/src/org/eclipse/emf/parsley/internal/databinding/EmfValidationTargetToModelUpdateValueStrategy.java b/plugins/org.eclipse.emf.parsley.common/src/org/eclipse/emf/parsley/internal/databinding/EmfValidationTargetToModelUpdateValueStrategy.java
new file mode 100644
index 0000000..83c4216
--- /dev/null
+++ b/plugins/org.eclipse.emf.parsley.common/src/org/eclipse/emf/parsley/internal/databinding/EmfValidationTargetToModelUpdateValueStrategy.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2016 RCP Vision (http://www.rcp-vision.com) 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:
+ * Francesco Guidieri - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.emf.parsley.internal.databinding;
+
+import static com.google.common.collect.Iterables.filter;
+
+import java.util.List;
+
+import org.eclipse.core.databinding.UpdateValueStrategy;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.emf.common.util.Diagnostic;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.util.Diagnostician;
+import org.eclipse.emf.parsley.validation.DiagnosticUtil;
+
+import com.google.common.base.Predicate;
+
+/**
+ * @author Francesco Guidieri - initial API and implementation
+ *
+ */
+public class EmfValidationTargetToModelUpdateValueStrategy extends UpdateValueStrategy {
+
+	private EStructuralFeature feature;
+	private Diagnostician diagnostician;
+	private DiagnosticUtil diagnosticUtil;
+	private EObject owner;
+	private boolean firstValidateBeforeSet = true;
+
+	public EmfValidationTargetToModelUpdateValueStrategy(EObject owner, EStructuralFeature feature,
+			Diagnostician diagnostician, DiagnosticUtil diagnosticUtil) {
+		this.owner = owner;
+		this.feature = feature;
+		this.diagnostician = diagnostician;
+		this.diagnosticUtil = diagnosticUtil;
+	}
+
+	@SuppressWarnings("rawtypes")
+	@Override
+	protected IStatus doSet(IObservableValue observableValue, Object value) {
+		IStatus afterSetStatus = super.doSet(observableValue, (!"".equals(value) ? value : null));
+		if (afterSetStatus.isOK()) {
+			return validationStatus(feature);
+		}
+		return afterSetStatus;
+	};
+
+	@Override
+	public IStatus validateBeforeSet(Object value) {
+		if (firstValidateBeforeSet) {
+			IStatus validationStatus = validationStatus(feature);
+			firstValidateBeforeSet = false;
+			if (!validationStatus.isOK()) {
+				return validationStatus;
+			}
+			return super.validateBeforeSet(value);
+		}
+		return Status.OK_STATUS;
+	}
+
+	private IStatus validationStatus(final EStructuralFeature feature) {
+		Diagnostic diagnostic = diagnostician.validate(owner);
+		List<Diagnostic> diagnostics = diagnosticUtil.flatten(diagnostic);
+		Iterable<Diagnostic> filtered = filter(diagnostics, new Predicate<Diagnostic>() {
+			@Override
+			public boolean apply(Diagnostic d) {
+				return filter(filter(d.getData(), EStructuralFeature.class), new Predicate<EStructuralFeature>() {
+					@Override
+					public boolean apply(EStructuralFeature input) {
+						return input.equals(feature);
+					}
+				}).iterator().hasNext();
+			}
+		});
+		for (Diagnostic d : filtered) {
+			int severity = d.getSeverity();
+			if (severity == Diagnostic.ERROR) {
+				return ValidationStatus.error(d.getMessage());
+			}
+		}
+		return ValidationStatus.ok();
+	}
+}