Bug 519607 - Add support for Multi edit

* fixes application of multi editing context behavior only if
multi-selection occurs
* add support for add/remove notifications
* enable multi editing on view model editor
* add test case

Change-Id: I1e664caeac2fd56fff0beb80794df994981f95c9
Signed-off-by: Eugen Neufeld <eneufeld@eclipsesource.com>
diff --git a/bundles/org.eclipse.emf.ecp.ide.editor.view/src/org/eclipse/emf/ecp/ide/editor/view/ViewEditorPart.java b/bundles/org.eclipse.emf.ecp.ide.editor.view/src/org/eclipse/emf/ecp/ide/editor/view/ViewEditorPart.java
index 2839b8b..cd59331 100644
--- a/bundles/org.eclipse.emf.ecp.ide.editor.view/src/org/eclipse/emf/ecp/ide/editor/view/ViewEditorPart.java
+++ b/bundles/org.eclipse.emf.ecp.ide.editor.view/src/org/eclipse/emf/ecp/ide/editor/view/ViewEditorPart.java
@@ -58,6 +58,7 @@
 import org.eclipse.emf.ecp.view.migrator.ViewModelMigratorUtil;
 import org.eclipse.emf.ecp.view.migrator.ViewModelWorkspaceMigrator;
 import org.eclipse.emf.ecp.view.model.common.edit.provider.CustomReflectiveItemProviderAdapterFactory;
+import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
 import org.eclipse.emf.ecp.view.spi.context.ViewModelContextFactory;
 import org.eclipse.emf.ecp.view.spi.model.VView;
 import org.eclipse.emf.ecp.view.spi.model.reporting.StatusReport;
@@ -611,10 +612,11 @@
 		}
 
 		try {
-
-			render = ECPSWTViewRenderer.INSTANCE.render(parent, ViewModelContextFactory.INSTANCE
+			final ViewModelContext viewModelContext = ViewModelContextFactory.INSTANCE
 				.createViewModelContext(ViewProviderHelper.getView(view, null), view, new DefaultReferenceService(),
-					new EMFDeleteServiceImpl()));
+					new EMFDeleteServiceImpl());
+			viewModelContext.putContextValue("enableMultiEdit", Boolean.TRUE);
+			render = ECPSWTViewRenderer.INSTANCE.render(parent, viewModelContext);
 		} catch (final ECPRendererException ex) {
 			Activator.getDefault().getReportService().report(
 				new StatusReport(new Status(IStatus.ERROR, Activator.PLUGIN_ID, ex.getMessage(), ex)));
diff --git a/bundles/org.eclipse.emf.ecp.view.treemasterdetail.ui.swt/src/org/eclipse/emf/ecp/view/spi/treemasterdetail/ui/swt/TreeMasterDetailSWTRenderer.java b/bundles/org.eclipse.emf.ecp.view.treemasterdetail.ui.swt/src/org/eclipse/emf/ecp/view/spi/treemasterdetail/ui/swt/TreeMasterDetailSWTRenderer.java
index f784be1..63b260b 100644
--- a/bundles/org.eclipse.emf.ecp.view.treemasterdetail.ui.swt/src/org/eclipse/emf/ecp/view/spi/treemasterdetail/ui/swt/TreeMasterDetailSWTRenderer.java
+++ b/bundles/org.eclipse.emf.ecp.view.treemasterdetail.ui.swt/src/org/eclipse/emf/ecp/view/spi/treemasterdetail/ui/swt/TreeMasterDetailSWTRenderer.java
@@ -69,6 +69,7 @@
 import org.eclipse.emf.ecp.view.treemasterdetail.ui.swt.internal.TreeMasterDetailSelectionManipulatorHelper;
 import org.eclipse.emf.edit.command.AddCommand;
 import org.eclipse.emf.edit.command.CommandParameter;
+import org.eclipse.emf.edit.command.DeleteCommand;
 import org.eclipse.emf.edit.command.SetCommand;
 import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
 import org.eclipse.emf.edit.domain.EditingDomain;
@@ -852,6 +853,57 @@
 	 */
 	private class TreeMasterViewSelectionListener implements ISelectionChangedListener {
 
+		/**
+		 * Adapter which listens to changes and delegates the notification to other EObjects.
+		 *
+		 * @author Eugen Neufeld
+		 *
+		 */
+		private final class MultiEditAdapter extends AdapterImpl {
+			private final EObject dummy;
+			private final Set<EObject> selectedEObjects;
+
+			private MultiEditAdapter(EObject dummy, Set<EObject> selectedEObjects) {
+				this.dummy = dummy;
+				this.selectedEObjects = selectedEObjects;
+			}
+
+			@Override
+			public void notifyChanged(Notification notification) {
+				final EditingDomain editingDomain = AdapterFactoryEditingDomain
+					.getEditingDomainFor(getViewModelContext().getDomainModel());
+				if (dummy.eClass().getEAllAttributes().contains(notification.getFeature())) {
+					final CompoundCommand cc = new CompoundCommand();
+					for (final EObject selected : selectedEObjects) {
+						Command command = null;
+						switch (notification.getEventType()) {
+						case Notification.SET:
+							command = SetCommand.create(editingDomain, selected,
+								notification.getFeature(), notification.getNewValue());
+							break;
+						case Notification.UNSET:
+							command = SetCommand.create(editingDomain, selected,
+								notification.getFeature(), SetCommand.UNSET_VALUE);
+							break;
+						case Notification.ADD:
+						case Notification.ADD_MANY:
+							command = AddCommand.create(editingDomain, selected,
+								notification.getFeature(), notification.getNewValue());
+							break;
+						case Notification.REMOVE:
+						case Notification.REMOVE_MANY:
+							command = DeleteCommand.create(editingDomain, notification.getOldValue());
+							break;
+						default:
+							continue;
+						}
+						cc.append(command);
+					}
+					editingDomain.getCommandStack().execute(cc);
+				}
+			}
+		}
+
 		private Composite childComposite;
 		private boolean currentDetailViewOriginalReadonly;
 
@@ -894,7 +946,10 @@
 					final ReferenceService referenceService = getViewModelContext().getService(
 						ReferenceService.class);
 					ViewModelContext childContext;
-					if (getViewModelContext().getContextValue(ENABLE_MULTI_EDIT) == Boolean.TRUE) {
+					// we have a multi selection, the multi edit is enabled and the multi selection is valid
+					if (getViewModelContext().getContextValue(ENABLE_MULTI_EDIT) == Boolean.TRUE
+						&& selection.size() > 1
+						&& selected != getSelection(new StructuredSelection(selection.getFirstElement()))) {
 						childContext = ViewModelContextFactory.INSTANCE.createViewModelContext(view, (EObject) selected,
 							new TreeMasterDetailReferenceService(referenceService));
 					} else {
@@ -950,35 +1005,7 @@
 				}
 				if (allOfSameType) {
 					treeSelected = dummy;
-					dummy.eAdapters().add(new AdapterImpl() {
-
-						@Override
-						public void notifyChanged(Notification notification) {
-							final EditingDomain editingDomain = AdapterFactoryEditingDomain
-								.getEditingDomainFor(getViewModelContext().getDomainModel());
-							if (dummy.eClass().getEAllAttributes().contains(notification.getFeature())) {
-								final CompoundCommand cc = new CompoundCommand();
-								for (final EObject selected : selectedEObjects) {
-									Command command = null;
-									switch (notification.getEventType()) {
-									case Notification.SET:
-										command = SetCommand.create(editingDomain, selected,
-											notification.getFeature(), notification.getNewValue());
-										break;
-									case Notification.UNSET:
-										command = SetCommand.create(editingDomain, selected,
-											notification.getFeature(), SetCommand.UNSET_VALUE);
-										break;
-									default:
-										continue;
-									}
-									cc.append(command);
-								}
-								editingDomain.getCommandStack().execute(cc);
-							}
-						}
-
-					});
+					dummy.eAdapters().add(new MultiEditAdapter(dummy, selectedEObjects));
 				}
 			}
 			return treeSelected;
diff --git a/bundles/org.eclipse.emfforms.swt.treemasterdetail/src/org/eclipse/emfforms/spi/swt/treemasterdetail/TreeMasterDetailComposite.java b/bundles/org.eclipse.emfforms.swt.treemasterdetail/src/org/eclipse/emfforms/spi/swt/treemasterdetail/TreeMasterDetailComposite.java
index a0fe12d..2f24e07 100644
--- a/bundles/org.eclipse.emfforms.swt.treemasterdetail/src/org/eclipse/emfforms/spi/swt/treemasterdetail/TreeMasterDetailComposite.java
+++ b/bundles/org.eclipse.emfforms.swt.treemasterdetail/src/org/eclipse/emfforms/spi/swt/treemasterdetail/TreeMasterDetailComposite.java
@@ -39,6 +39,8 @@
 import org.eclipse.emf.ecp.view.spi.model.VViewModelProperties;
 import org.eclipse.emf.ecp.view.spi.provider.ViewProviderHelper;
 import org.eclipse.emf.ecp.view.treemasterdetail.model.VTreeMasterDetail;
+import org.eclipse.emf.edit.command.AddCommand;
+import org.eclipse.emf.edit.command.DeleteCommand;
 import org.eclipse.emf.edit.command.SetCommand;
 import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
 import org.eclipse.emf.edit.domain.EditingDomain;
@@ -418,32 +420,7 @@
 			}
 			if (allOfSameType) {
 				selectedObject = dummy;
-				dummy.eAdapters().add(new AdapterImpl() {
-
-					@Override
-					public void notifyChanged(Notification notification) {
-						if (dummy.eClass().getEAllAttributes().contains(notification.getFeature())) {
-							final CompoundCommand cc = new CompoundCommand();
-							for (final EObject selected : selectedEObjects) {
-								Command command = null;
-								switch (notification.getEventType()) {
-								case Notification.SET:
-									command = SetCommand.create(editingDomain, selected,
-										notification.getFeature(), notification.getNewValue());
-									break;
-								case Notification.UNSET:
-									command = SetCommand.create(editingDomain, selected,
-										notification.getFeature(), SetCommand.UNSET_VALUE);
-									break;
-								default:
-									continue;
-								}
-								cc.append(command);
-							}
-							editingDomain.getCommandStack().execute(cc);
-						}
-					}
-				});
+				dummy.eAdapters().add(new MultiEditAdapter(selectedEObjects, dummy));
 			}
 		}
 		return selectedObject;
@@ -591,6 +568,55 @@
 	}
 
 	/**
+	 * Adapter which listens to changes and delegates the notification to other EObjects.
+	 * 
+	 * @author Eugen Neufeld
+	 *
+	 */
+	private final class MultiEditAdapter extends AdapterImpl {
+		private final Set<EObject> selectedEObjects;
+		private final EObject dummy;
+
+		private MultiEditAdapter(Set<EObject> selectedEObjects, EObject dummy) {
+			this.selectedEObjects = selectedEObjects;
+			this.dummy = dummy;
+		}
+
+		@Override
+		public void notifyChanged(Notification notification) {
+			if (dummy.eClass().getEAllAttributes().contains(notification.getFeature())) {
+				final CompoundCommand cc = new CompoundCommand();
+				for (final EObject selected : selectedEObjects) {
+					Command command = null;
+					switch (notification.getEventType()) {
+					case Notification.SET:
+						command = SetCommand.create(editingDomain, selected,
+							notification.getFeature(), notification.getNewValue());
+						break;
+					case Notification.UNSET:
+						command = SetCommand.create(editingDomain, selected,
+							notification.getFeature(), SetCommand.UNSET_VALUE);
+						break;
+					case Notification.ADD:
+					case Notification.ADD_MANY:
+						command = AddCommand.create(editingDomain, selected,
+							notification.getFeature(), notification.getNewValue());
+						break;
+					case Notification.REMOVE:
+					case Notification.REMOVE_MANY:
+						command = DeleteCommand.create(editingDomain, notification.getOldValue());
+						break;
+					default:
+						continue;
+					}
+					cc.append(command);
+				}
+				editingDomain.getCommandStack().execute(cc);
+			}
+		}
+	}
+
+	/**
 	 * Runnable which updates the detail panel.
 	 */
 	private final class UpdateDetailRunnable implements Runnable {
diff --git a/tests/ECPQ7Tests/EPPTests/update-site/project/ViewEditorMultiEdit.test b/tests/ECPQ7Tests/EPPTests/update-site/project/ViewEditorMultiEdit.test
new file mode 100644
index 0000000..e04eae8
--- /dev/null
+++ b/tests/ECPQ7Tests/EPPTests/update-site/project/ViewEditorMultiEdit.test
@@ -0,0 +1,57 @@
+--- RCPTT testcase ---
+Format-Version: 1.0
+Contexts: _D4Pj4C3lEeSwhO5Nwx0hPg,_Iu0EsS36EeSYRYqCbC6LMQ
+Element-Name: ViewEditorMultiEdit
+Element-Type: testcase
+Element-Version: 3.0
+External-Reference: 
+Id: _1aNe4KerEeijWtYvZtcong
+Runtime-Version: 2.2.0.201706152316
+Save-Time: 8/24/18 5:10 PM
+Testcase-Type: ecl
+
+------=_.content-0a7243a0-75d3-3d5f-9791-539de0e5b7ac
+Content-Type: text/ecl
+Entry-Name: .content
+
+get-view "Project Explorer" | get-tree 
+    | select "org.eclipse.emf.ecp.makeithappen.model.viewmodel/viewmodels/UserGroup.view" 
+    | select "org.eclipse.emf.ecp.makeithappen.model.viewmodel/viewmodels/UserGroup.view" | get-menu 
+    -path "Open With/View Model Editor" | click
+with [get-editor "UserGroup.view"] {
+    with [get-tree] {
+        select View | get-menu -path Control | click
+        select View | get-menu -path Control | click
+        select [get-item -path View | get-item -path Control -index 1] "View/Control"
+    }
+    with [get-editbox -after [get-label Name]] {
+        set-text aaa
+        key-type "TRAVERSE_TAB_NEXT"
+    }
+}
+with [get-editor "UserGroup.view" | get-tree] {
+    get-item -path "View/aaa" | get-property caption | equals aaa | verify-true
+    get-item -path View | get-item -path aaa -index 1 | get-property caption | equals aaa | verify-true
+}
+with [get-editor "UserGroup.view"] {
+    get-tree | select "View/aaa"
+    get-editbox -after [get-label Name] | set-text x
+    get-tree | select "View/aaa" "View/x"
+}
+get-editor "UserGroup.view" | get-editbox -after [get-label Name] | get-property text | equals "" | verify-true
+with [get-editor "UserGroup.view"] {
+    get-tree | select "View/x"
+    get-editbox -after [get-label Name] | set-text aaa
+    get-tree | select [get-item -path View | get-item -path aaa -index 1] "View/aaa"
+}
+get-editor "UserGroup.view" | get-editbox -after [get-label Name] | get-property text | equals aaa | verify-true
+with [get-editor "UserGroup.view"] {
+    get-combo -after [get-label "Label Alignment*"] | select Top
+    get-tree | select "View/aaa"
+}
+get-editor "UserGroup.view" | get-combo -after [get-label "Label Alignment*"] | get-property selection | equals Top 
+    | verify-true
+get-editor "UserGroup.view" | get-tree | select [get-item -path View | get-item -path aaa -index 1]
+get-editor "UserGroup.view" | get-combo -after [get-label "Label Alignment*"] | get-property selection | equals Top 
+    | verify-true
+------=_.content-0a7243a0-75d3-3d5f-9791-539de0e5b7ac--