Bug 548333 - ReadOnlyConfiguration should only allow to link column DMRs

* Adapt the ColumnConfigurationDMRRendererReferenceService to work for
any VTableColumnConfiguration
* Add custom renderer for VReadOnlyConfigurations that uses the
reference service

Change-Id: I425f1ba742b38960267a1015f43e1a95ad6a4a0d
Signed-off-by: Lucas Koehler <lkoehler@eclipsesource.com>
diff --git a/bundles/org.eclipse.emf.ecp.ui.view.editor.controls/META-INF/MANIFEST.MF b/bundles/org.eclipse.emf.ecp.ui.view.editor.controls/META-INF/MANIFEST.MF
index 5b7eeeb..d00a9b7 100644
--- a/bundles/org.eclipse.emf.ecp.ui.view.editor.controls/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.emf.ecp.ui.view.editor.controls/META-INF/MANIFEST.MF
@@ -44,7 +44,8 @@
  org.eclipse.emfforms.view.annotation.model;bundle-version="[1.22.0,1.23.0)",
  org.eclipse.emf.ecp.view.rule;bundle-version="[1.22.0,1.23.0)",
  org.eclipse.emfforms.view.multisegment.model;bundle-version="[1.22.0,1.23.0)",
- org.eclipse.emfforms.ide.view.segments;bundle-version="[1.22.0,1.23.0)"
+ org.eclipse.emfforms.ide.view.segments;bundle-version="[1.22.0,1.23.0)",
+ org.eclipse.emf.ecp.view.control.multireference;bundle-version="[1.22.0,1.23.0)"
 Import-Package: org.eclipse.core.resources;version="0.0.0",
  org.eclipse.emfforms.spi.common.report;version="[1.22.0,1.23.0)",
  org.eclipse.emfforms.spi.core.services.databinding.emf;version="[1.22.0,1.23.0)",
@@ -79,6 +80,7 @@
  OSGI-INF/IterateConditionDmrOpenInNewContextStrategyProvider.xml,
  OSGI-INF/RuleConditionDmrOpenInNewContextStrategyProvider.xml,
  OSGI-INF/IterateConditionDmrNewModelElementStrategyProvider.xml,
- OSGI-INF/RuleConditionDmrControlSWTRendererService.xml
+ OSGI-INF/RuleConditionDmrControlSWTRendererService.xml,
+ OSGI-INF/ReadOnlyConfigurationSWTRendererService.xml
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
diff --git a/bundles/org.eclipse.emf.ecp.ui.view.editor.controls/OSGI-INF/ReadOnlyConfigurationSWTRendererService.xml b/bundles/org.eclipse.emf.ecp.ui.view.editor.controls/OSGI-INF/ReadOnlyConfigurationSWTRendererService.xml
new file mode 100644
index 0000000..8dc3114
--- /dev/null
+++ b/bundles/org.eclipse.emf.ecp.ui.view.editor.controls/OSGI-INF/ReadOnlyConfigurationSWTRendererService.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="ReadOnlyConfigurationSWTRendererService">
+   <service>
+      <provide interface="org.eclipse.emfforms.spi.swt.core.di.EMFFormsDIRendererService"/>
+   </service>
+   <reference bind="setEMFFormsDatabindingEMF" cardinality="1..1" interface="org.eclipse.emfforms.spi.core.services.databinding.emf.EMFFormsDatabindingEMF" name="EMFFormsDatabindingEMF"/>
+   <reference bind="setreportService" cardinality="1..1" interface="org.eclipse.emfforms.spi.common.report.ReportService" name="reportService"/>
+   <implementation class="org.eclipse.emf.ecp.view.internal.editor.controls.ReadOnlyConfigurationSWTRendererService"/>
+</scr:component>
\ No newline at end of file
diff --git a/bundles/org.eclipse.emf.ecp.ui.view.editor.controls/src/org/eclipse/emf/ecp/view/internal/editor/controls/ColumnConfigurationDMRRendererReferenceService.java b/bundles/org.eclipse.emf.ecp.ui.view.editor.controls/src/org/eclipse/emf/ecp/view/internal/editor/controls/ColumnConfigurationDMRRendererReferenceService.java
index 04c6297..cfe76db 100644
--- a/bundles/org.eclipse.emf.ecp.ui.view.editor.controls/src/org/eclipse/emf/ecp/view/internal/editor/controls/ColumnConfigurationDMRRendererReferenceService.java
+++ b/bundles/org.eclipse.emf.ecp.ui.view.editor.controls/src/org/eclipse/emf/ecp/view/internal/editor/controls/ColumnConfigurationDMRRendererReferenceService.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2017 EclipseSource Muenchen GmbH and others.
+ * Copyright (c) 2017-2019 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
@@ -14,17 +14,18 @@
  ******************************************************************************/
 package org.eclipse.emf.ecp.view.internal.editor.controls;
 
+import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.Optional;
 import java.util.Set;
 
+import org.eclipse.emf.common.util.EList;
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.ecore.EReference;
 import org.eclipse.emf.ecp.edit.spi.ReferenceService;
 import org.eclipse.emf.ecp.spi.common.ui.SelectModelElementWizardFactory;
 import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
 import org.eclipse.emf.ecp.view.spi.model.VDomainModelReference;
-import org.eclipse.emf.ecp.view.spi.table.model.VSingleColumnConfiguration;
 import org.eclipse.emf.ecp.view.spi.table.model.VTableColumnConfiguration;
 import org.eclipse.emf.ecp.view.spi.table.model.VTableControl;
 import org.eclipse.emf.ecp.view.spi.table.model.VTableDomainModelReference;
@@ -33,22 +34,23 @@
 import org.eclipse.emfforms.view.spi.multisegment.model.VMultiDomainModelReferenceSegment;
 
 /**
- * Special {@link ReferenceService} allowing stream lined DMR selection for the width configuration.
+ * Special {@link ReferenceService} allowing stream lined DMR selection for {@link VTableColumnConfiguration
+ * VTableColumnConfigurations}.
  *
  * @author Johannes Faltermeier
  *
  */
 public class ColumnConfigurationDMRRendererReferenceService implements ReferenceService {
 
-	private final Class<? extends VSingleColumnConfiguration> columnConfigClass;
+	private final Class<? extends VTableColumnConfiguration> columnConfigClass;
 
 	/**
 	 * Constructor.
 	 *
-	 * @param columnConfigClass the {@link VSingleColumnConfiguration} based class to be filtered
+	 * @param columnConfigClass the {@link VTableColumnConfiguration} based class to be filtered
 	 */
 	public ColumnConfigurationDMRRendererReferenceService(
-		Class<? extends VSingleColumnConfiguration> columnConfigClass) {
+		Class<? extends VTableColumnConfiguration> columnConfigClass) {
 		this.columnConfigClass = columnConfigClass;
 	}
 
@@ -93,13 +95,8 @@
 		if (!unconfiguredColumns.isPresent()) {
 			return;
 		}
-		for (final VTableColumnConfiguration columnConfiguration : tableControl.getColumnConfigurations()) {
-			if (!columnConfigClass.isInstance(columnConfiguration)) {
-				continue;
-			}
-			unconfiguredColumns.get()
-				.remove(columnConfigClass.cast(columnConfiguration).getColumnDomainReference());
-		}
+
+		unconfiguredColumns.get().removeAll(getConfiguredColumns(tableControl, eReference));
 
 		final Set<EObject> selectedColumns = SelectModelElementWizardFactory
 			.openModelElementSelectionDialog(
@@ -113,6 +110,12 @@
 			AdapterFactoryEditingDomain.getEditingDomainFor(eObject));
 	}
 
+	/**
+	 * @param dmr The DMR defining the table
+	 * @return The set of all column dmrs which are not configured by a configuration of this service's
+	 *         <code>columnConfigClass</code>. Returns nothing if the list of column dmrs cannot be retrieved from the
+	 *         given dmr.
+	 */
 	private Optional<Set<EObject>> getUnconfiguredColumns(VDomainModelReference dmr) {
 		Set<EObject> result = null;
 		if (!dmr.getSegments().isEmpty()) {
@@ -127,6 +130,26 @@
 		return Optional.ofNullable(result);
 	}
 
+	/**
+	 * @return The set of all column dmrs which are already configured by a configuration of this service's
+	 *         <code>columnConfigClass</code>. May return an empty set but never <code>null</code>.
+	 */
+	@SuppressWarnings("unchecked")
+	private Set<EObject> getConfiguredColumns(VTableControl tableControl, EReference eReference) {
+		final Set<EObject> result = new HashSet<>();
+		for (final VTableColumnConfiguration columnConfiguration : tableControl.getColumnConfigurations()) {
+			if (!columnConfigClass.isInstance(columnConfiguration)) {
+				continue;
+			}
+			if (eReference.isMany()) {
+				result.addAll((EList<EObject>) columnConfiguration.eGet(eReference));
+			} else {
+				result.add((EObject) columnConfiguration.eGet(eReference));
+			}
+		}
+		return result;
+	}
+
 	@Override
 	public void openInNewContext(EObject eObject) {
 		/* no-op */
diff --git a/bundles/org.eclipse.emf.ecp.ui.view.editor.controls/src/org/eclipse/emf/ecp/view/internal/editor/controls/ReadOnlyConfigurationSWTRenderer.java b/bundles/org.eclipse.emf.ecp.ui.view.editor.controls/src/org/eclipse/emf/ecp/view/internal/editor/controls/ReadOnlyConfigurationSWTRenderer.java
new file mode 100644
index 0000000..bc83ce5
--- /dev/null
+++ b/bundles/org.eclipse.emf.ecp.ui.view.editor.controls/src/org/eclipse/emf/ecp/view/internal/editor/controls/ReadOnlyConfigurationSWTRenderer.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2019 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:
+ * Lucas Koehler - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.emf.ecp.view.internal.editor.controls;
+
+import javax.inject.Inject;
+
+import org.eclipse.emf.ecp.edit.spi.ReferenceService;
+import org.eclipse.emf.ecp.view.internal.control.multireference.LinkOnlyMultiReferenceRenderer;
+import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
+import org.eclipse.emf.ecp.view.spi.model.VControl;
+import org.eclipse.emf.ecp.view.spi.table.model.VReadOnlyColumnConfiguration;
+import org.eclipse.emf.ecp.view.spi.util.swt.ImageRegistryService;
+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.EMFFormsDatabinding;
+import org.eclipse.emfforms.spi.core.services.label.EMFFormsLabelProvider;
+
+/**
+ * Renderer to configure the linked column DMRs for {@link VReadOnlyColumnConfiguration ReadOnlyColumnConfigurations}.
+ * Uses a custom reference service to only show suitable column dmrs for linking.
+ *
+ * @author Lucas Koehler
+ *
+ */
+public class ReadOnlyConfigurationSWTRenderer extends LinkOnlyMultiReferenceRenderer {
+
+	private ReferenceService referenceService;
+
+	/**
+	 * Default constructor.
+	 *
+	 * @param vElement the view model element to be rendered
+	 * @param viewContext the view context
+	 * @param emfFormsDatabinding The {@link EMFFormsDatabinding}
+	 * @param emfFormsLabelProvider The {@link EMFFormsLabelProvider}
+	 * @param reportService The {@link ReportService}
+	 * @param vtViewTemplateProvider The {@link VTViewTemplateProvider}
+	 * @param imageRegistryService The {@link ImageRegistryService}
+	 */
+	@Inject
+	public ReadOnlyConfigurationSWTRenderer(VControl vElement, ViewModelContext viewContext,
+		ReportService reportService, EMFFormsDatabinding emfFormsDatabinding,
+		EMFFormsLabelProvider emfFormsLabelProvider, VTViewTemplateProvider vtViewTemplateProvider,
+		ImageRegistryService imageRegistryService) {
+		super(vElement, viewContext, reportService, emfFormsDatabinding, emfFormsLabelProvider, vtViewTemplateProvider,
+			imageRegistryService);
+	}
+
+	@Override
+	protected ReferenceService getReferenceService() {
+		if (referenceService == null) {
+			referenceService = new ColumnConfigurationDMRRendererReferenceService(VReadOnlyColumnConfiguration.class);
+		}
+		return referenceService;
+	}
+}
diff --git a/bundles/org.eclipse.emf.ecp.ui.view.editor.controls/src/org/eclipse/emf/ecp/view/internal/editor/controls/ReadOnlyConfigurationSWTRendererService.java b/bundles/org.eclipse.emf.ecp.ui.view.editor.controls/src/org/eclipse/emf/ecp/view/internal/editor/controls/ReadOnlyConfigurationSWTRendererService.java
new file mode 100644
index 0000000..a23e298
--- /dev/null
+++ b/bundles/org.eclipse.emf.ecp.ui.view.editor.controls/src/org/eclipse/emf/ecp/view/internal/editor/controls/ReadOnlyConfigurationSWTRendererService.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2011-2019 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:
+ * Lucas Koehler - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.emf.ecp.view.internal.editor.controls;
+
+import org.eclipse.emf.databinding.IEMFValueProperty;
+import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
+import org.eclipse.emf.ecp.view.spi.model.VControl;
+import org.eclipse.emf.ecp.view.spi.model.VDomainModelReference;
+import org.eclipse.emf.ecp.view.spi.model.VElement;
+import org.eclipse.emf.ecp.view.spi.table.model.VReadOnlyColumnConfiguration;
+import org.eclipse.emf.ecp.view.spi.table.model.VTablePackage;
+import org.eclipse.emfforms.spi.common.report.AbstractReport;
+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.emf.EMFFormsDatabindingEMF;
+import org.eclipse.emfforms.spi.swt.core.AbstractSWTRenderer;
+import org.eclipse.emfforms.spi.swt.core.di.EMFFormsDIRendererService;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+
+/**
+ * Renderer service for {@link ReadOnlyConfigurationSWTRenderer}.
+ *
+ * @author Lucas Koehler
+ *
+ */
+@Component(name = "ReadOnlyConfigurationSWTRendererService")
+public class ReadOnlyConfigurationSWTRendererService implements EMFFormsDIRendererService<VControl> {
+
+	private EMFFormsDatabindingEMF databinding;
+	private ReportService reportService;
+
+	/**
+	 * @param databinding {@link EMFFormsDatabindingEMF}
+	 */
+	@Reference(cardinality = ReferenceCardinality.MANDATORY, unbind = "-")
+	public void setEMFFormsDatabindingEMF(EMFFormsDatabindingEMF databinding) {
+		this.databinding = databinding;
+	}
+
+	/**
+	 * @param reportService {@link ReportService}
+	 */
+	@Reference(cardinality = ReferenceCardinality.MANDATORY, unbind = "-")
+	public void setreportService(ReportService reportService) {
+		this.reportService = reportService;
+	}
+
+	@Override
+	public double isApplicable(VElement vElement, ViewModelContext viewModelContext) {
+		if (!VControl.class.isInstance(vElement)) {
+			return NOT_APPLICABLE;
+		}
+		final VDomainModelReference domainModelReference = VControl.class.cast(vElement).getDomainModelReference();
+		if (domainModelReference == null) {
+			return NOT_APPLICABLE;
+		}
+		try {
+			final IEMFValueProperty valueProperty = databinding.getValueProperty(
+				domainModelReference, viewModelContext.getDomainModel());
+			if (viewModelContext.getDomainModel() instanceof VReadOnlyColumnConfiguration
+				&& valueProperty
+					.getStructuralFeature() == VTablePackage.Literals.READ_ONLY_COLUMN_CONFIGURATION__COLUMN_DOMAIN_REFERENCES) {
+				return 10d;
+			}
+		} catch (final DatabindingFailedException ex) {
+			reportService.report(new AbstractReport(ex));
+		}
+
+		return NOT_APPLICABLE;
+	}
+
+	@Override
+	public Class<? extends AbstractSWTRenderer<VControl>> getRendererClass() {
+		return ReadOnlyConfigurationSWTRenderer.class;
+	}
+
+}
diff --git a/bundles/org.eclipse.emf.ecp.view.control.multireference/META-INF/MANIFEST.MF b/bundles/org.eclipse.emf.ecp.view.control.multireference/META-INF/MANIFEST.MF
index 6eabcfc..32b797e 100644
--- a/bundles/org.eclipse.emf.ecp.view.control.multireference/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.emf.ecp.view.control.multireference/META-INF/MANIFEST.MF
@@ -4,7 +4,7 @@
 Bundle-SymbolicName: org.eclipse.emf.ecp.view.control.multireference;singleton:=true
 Bundle-Version: 1.22.0.qualifier
 Bundle-Vendor: Eclipse Modeling Project
-Export-Package: org.eclipse.emf.ecp.view.internal.control.multireference;version="1.22.0";x-friends:="org.eclipse.emfforms.ide.view.multisegment"
+Export-Package: org.eclipse.emf.ecp.view.internal.control.multireference;version="1.22.0";x-friends:="org.eclipse.emfforms.ide.view.multisegment,org.eclipse.emf.ecp.ui.view.editor.controls"
 Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.7.0,4.0.0)",
  org.eclipse.core.databinding;bundle-version="[1.4.1,2.0.0)",
  org.eclipse.emf.edit;bundle-version="[2.8.0,3.0.0)",
diff --git a/bundles/org.eclipse.emf.ecp.view.control.multireference/src/org/eclipse/emf/ecp/view/internal/control/multireference/MultiReferenceSWTRenderer.java b/bundles/org.eclipse.emf.ecp.view.control.multireference/src/org/eclipse/emf/ecp/view/internal/control/multireference/MultiReferenceSWTRenderer.java
index 7db9f62..935c570 100644
--- a/bundles/org.eclipse.emf.ecp.view.control.multireference/src/org/eclipse/emf/ecp/view/internal/control/multireference/MultiReferenceSWTRenderer.java
+++ b/bundles/org.eclipse.emf.ecp.view.control.multireference/src/org/eclipse/emf/ecp/view/internal/control/multireference/MultiReferenceSWTRenderer.java
@@ -1034,7 +1034,7 @@
 	 * @param selectedObject the selected {@link EObject}
 	 */
 	protected void handleDoubleClick(EObject selectedObject) {
-		final ReferenceService referenceService = getViewModelContext().getService(ReferenceService.class);
+		final ReferenceService referenceService = getReferenceService();
 		referenceService.openInNewContext(selectedObject);
 	}
 
@@ -1046,7 +1046,7 @@
 	 * @param structuralFeature The corresponding {@link EStructuralFeature}
 	 */
 	protected void handleAddExisting(TableViewer tableViewer, EObject eObject, EStructuralFeature structuralFeature) {
-		final ReferenceService referenceService = getViewModelContext().getService(ReferenceService.class);
+		final ReferenceService referenceService = getReferenceService();
 		referenceService.addExistingModelElements(eObject, (EReference) structuralFeature);
 	}
 
@@ -1058,11 +1058,20 @@
 	 * @param structuralFeature The corresponding {@link EStructuralFeature}
 	 */
 	protected void handleAddNew(TableViewer tableViewer, EObject eObject, EStructuralFeature structuralFeature) {
-		final ReferenceService referenceService = getViewModelContext().getService(ReferenceService.class);
+		final ReferenceService referenceService = getReferenceService();
 		referenceService.addNewModelElements(eObject, (EReference) structuralFeature, true);
 	}
 
 	/**
+	 * Override to customize linking and creation of EObjects in this renderer's EReference.
+	 * 
+	 * @return The {@link ReferenceService} used to link and create new EObjects in this renderer's reference.
+	 */
+	protected ReferenceService getReferenceService() {
+		return getViewModelContext().getService(ReferenceService.class);
+	}
+
+	/**
 	 * Method for deleting elements.
 	 *
 	 * @param tableViewer the {@link TableViewer}