Bug 551066 - [Reveal Service] Revealing a control does not work within a category

Defer finding the renderer of the control because in the case of a
control within a category view it will not be rendered until the
category itself is first revealed by a prior reveal step.

Change-Id: Id0a7c876a08d7dbc706f2ee41fecc039897d7561
Signed-off-by: Christian W. Damus <give.a.damus@gmail.com>
diff --git a/bundles/org.eclipse.emfforms.swt.core/src/org/eclipse/emfforms/internal/swt/core/ControlRevealProvider.java b/bundles/org.eclipse.emfforms.swt.core/src/org/eclipse/emfforms/internal/swt/core/ControlRevealProvider.java
index 6fe0c20..b67a936 100644
--- a/bundles/org.eclipse.emfforms.swt.core/src/org/eclipse/emfforms/internal/swt/core/ControlRevealProvider.java
+++ b/bundles/org.eclipse.emfforms.swt.core/src/org/eclipse/emfforms/internal/swt/core/ControlRevealProvider.java
@@ -17,8 +17,9 @@
 
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.ecore.EStructuralFeature;
-import org.eclipse.emf.ecp.view.model.common.di.annotations.Renderer;
+import org.eclipse.emf.ecp.view.model.common.AbstractRenderer;
 import org.eclipse.emf.ecp.view.model.common.di.annotations.ViewService;
+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.VViewPackage;
 import org.eclipse.emfforms.bazaar.Bid;
@@ -62,20 +63,36 @@
 	}
 
 	/**
-	 * Create a terminal reveal step to focus into the specific setting control in a
-	 * container {@code element}.
+	 * Create a terminal reveal step to focus into the specific setting {@code control}
+	 * in a that presents a {@code feature} or an {@code object}.
 	 *
-	 * @param renderer the renderer of the control to reveal
+	 * @param control the control to reveal
 	 * @param object the object to reveal
 	 * @param feature the feature of the domain {@code object} to reveal
+	 * @param context the view-model context in which the {@code control} is rendered
 	 * @return the specific control reveal step
 	 */
 	@Create
-	public RevealStep reveal(@Renderer AbstractSWTRenderer<?> renderer, EObject object,
-		EStructuralFeature feature) {
+	public RevealStep reveal(VControl control, EObject object,
+		EStructuralFeature feature, ViewModelContext context) {
 
-		return RevealStep.reveal(renderer.getVElement(), object, feature,
-			() -> Display.getDefault().asyncExec(renderer::scrollToReveal));
+		return RevealStep.reveal(control, object, feature,
+			// Scroll to reveal the control in the future, when it has been
+			// rendered by the previous reveal step (bug 551066)
+			() -> Display.getDefault().asyncExec(() -> scrollToReveal(control, context)));
+	}
+
+	/**
+	 * Scroll to reveal the rendered {@code control}.
+	 *
+	 * @param control a control rendered in some {@code context}
+	 * @param context the context in which the {@code control} is rendered
+	 */
+	private void scrollToReveal(VControl control, ViewModelContext context) {
+		final AbstractRenderer<?> renderer = AbstractRenderer.getRenderer(control, context);
+		if (renderer instanceof AbstractSWTRenderer<?>) {
+			((AbstractSWTRenderer<?>) renderer).scrollToReveal();
+		}
 	}
 
 }
diff --git a/tests/org.eclipse.emf.ecp.view.categorization.swt.test/src/org/eclipse/emf/ecp/view/spi/categorization/swt/CategorizationRevealProvider_PTest.java b/tests/org.eclipse.emf.ecp.view.categorization.swt.test/src/org/eclipse/emf/ecp/view/spi/categorization/swt/CategorizationRevealProvider_PTest.java
index 1170953..1ed1106 100644
--- a/tests/org.eclipse.emf.ecp.view.categorization.swt.test/src/org/eclipse/emf/ecp/view/spi/categorization/swt/CategorizationRevealProvider_PTest.java
+++ b/tests/org.eclipse.emf.ecp.view.categorization.swt.test/src/org/eclipse/emf/ecp/view/spi/categorization/swt/CategorizationRevealProvider_PTest.java
@@ -13,16 +13,21 @@
  ******************************************************************************/
 package org.eclipse.emf.ecp.view.spi.categorization.swt;
 
+import static org.eclipse.emf.ecp.common.spi.UniqueSetting.createSetting;
 import static org.eclipse.emf.ecp.view.test.common.spi.EMFMocking.eMock;
 import static org.eclipse.emf.ecp.view.test.common.spi.EMFMocking.withESettings;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import java.util.ArrayList;
 
 import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.EcoreFactory;
+import org.eclipse.emf.ecore.EcorePackage;
 import org.eclipse.emf.ecp.test.common.DefaultRealm;
 import org.eclipse.emf.ecp.view.internal.categorization.swt.CategorizationRevealProvider;
 import org.eclipse.emf.ecp.view.spi.categorization.model.VCategorization;
@@ -32,7 +37,11 @@
 import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
 import org.eclipse.emf.ecp.view.spi.model.VContainedContainer;
 import org.eclipse.emf.ecp.view.spi.model.VContainer;
+import org.eclipse.emf.ecp.view.spi.model.VControl;
 import org.eclipse.emf.ecp.view.spi.model.VElement;
+import org.eclipse.emf.ecp.view.spi.model.VViewFactory;
+import org.eclipse.emf.ecp.view.spi.vertical.model.VVerticalFactory;
+import org.eclipse.emf.ecp.view.spi.vertical.model.VVerticalLayout;
 import org.eclipse.emf.ecp.view.test.common.spi.EMFFormsRevealServiceFixture;
 import org.eclipse.emf.ecp.view.test.common.spi.EMFFormsViewContextFixture.DomainModel;
 import org.eclipse.emf.ecp.view.test.common.spi.EMFFormsViewContextFixture.ViewModel;
@@ -41,6 +50,7 @@
 import org.eclipse.emf.ecp.view.test.common.swt.spi.SWTViewTestHelper;
 import org.eclipse.emfforms.bazaar.Bid;
 import org.eclipse.emfforms.bazaar.Create;
+import org.eclipse.emfforms.spi.core.services.controlmapper.EMFFormsSettingToControlMapper;
 import org.eclipse.emfforms.spi.core.services.reveal.EMFFormsRevealProvider;
 import org.eclipse.emfforms.spi.core.services.reveal.RevealStep;
 import org.eclipse.swt.widgets.Shell;
@@ -61,9 +71,8 @@
 	private final VCategorizationElement categorizations = VCategorizationFactory.eINSTANCE
 		.createCategorizationElement();
 
-	@EMock
 	@DomainModel
-	private EObject rootObject;
+	private final EObject rootObject = EcoreFactory.eINSTANCE.createEClass();
 
 	@EMock
 	private EObject obj1;
@@ -192,6 +201,40 @@
 		verify(reveal).run();
 	}
 
+	/**
+	 * Integration test for revealing a specific control (by feature) within a tree categorization,
+	 * which is the scenario that didn't work as reported in <a href="http://eclip.se/551066">bug 551066</a>.
+	 * The test relies on the fact that the non-default category containing the control to be revealed
+	 * would not end up being selected if the control within did not provide a successful reveal step.
+	 *
+	 * @see <a href="http://eclip.se/551066">bug 551066</a>
+	 */
+	@Test
+	public void revealSpecificControlInTree() {
+		final VVerticalLayout vertical = VVerticalFactory.eINSTANCE.createVerticalLayout();
+		cat3.setComposite(vertical);
+
+		final VControl control = VViewFactory.eINSTANCE.createControl();
+		final EStructuralFeature feature = EcorePackage.Literals.ENAMED_ELEMENT__NAME;
+		control.setDomainModelReference(feature);
+		vertical.getChildren().add(control);
+
+		// Revealing a control requires the settings mapper
+		final EMFFormsSettingToControlMapper mapper = mock(EMFFormsSettingToControlMapper.class);
+		when(mapper.hasControlsFor(rootObject)).thenReturn(true);
+		when(mapper.hasMapping(createSetting(rootObject, feature), control)).thenReturn(true);
+		fixture.putService(EMFFormsSettingToControlMapper.class, mapper);
+
+		render();
+
+		fixture.reveal(rootObject, feature);
+
+		// The category would only be selected if the control within it was revealed.
+		// In the case of bug 551066, context injection of the ControlRevealProvider failed
+		// to create the provider because the name control renderer was not yet available
+		assertThat("The category was not selected", categorizations.getCurrentSelection(), is(cat3));
+	}
+
 	//
 	// Test framework
 	//