Bug 552127 - ValidationService can only validate a collection of Objects

Add API for validating any generic iterable of EObjects.

Change-Id: I6d997ea7c174b7ad43c777698b9f90caccff3bf9
Signed-off-by: Christian W. Damus <give.a.damus@gmail.com>
diff --git a/bundles/org.eclipse.emf.ecp.view.validation/src/org/eclipse/emf/ecp/view/internal/validation/ValidationServiceImpl.java b/bundles/org.eclipse.emf.ecp.view.validation/src/org/eclipse/emf/ecp/view/internal/validation/ValidationServiceImpl.java
index b92dd7b..e747b00 100644
--- a/bundles/org.eclipse.emf.ecp.view.validation/src/org/eclipse/emf/ecp/view/internal/validation/ValidationServiceImpl.java
+++ b/bundles/org.eclipse.emf.ecp.view.validation/src/org/eclipse/emf/ecp/view/internal/validation/ValidationServiceImpl.java
@@ -10,7 +10,7 @@
  *
  * Contributors:
  * Eugen - initial API and implementation
- * Christian W. Damus - bugs 533522, 543160, 545686, 527686, 548761
+ * Christian W. Damus - bugs 533522, 543160, 545686, 527686, 548761, 552127
  ******************************************************************************/
 package org.eclipse.emf.ecp.view.internal.validation;
 
@@ -543,7 +543,14 @@
 	}
 
 	@Override
+	public void validate(Iterable<? extends EObject> objects) {
+		objects.forEach(validationQueue::add);
+		processValidationQueue();
+	}
+
+	@Override
 	public void validate(Collection<EObject> eObjects) {
+		// Delegate the opposite direction to how the default interface method does
 		validationQueue.addAll(eObjects);
 		processValidationQueue();
 	}
diff --git a/bundles/org.eclipse.emf.ecp.view.validation/src/org/eclipse/emf/ecp/view/spi/validation/ValidationService.java b/bundles/org.eclipse.emf.ecp.view.validation/src/org/eclipse/emf/ecp/view/spi/validation/ValidationService.java
index 86a3087..8beedc5 100644
--- a/bundles/org.eclipse.emf.ecp.view.validation/src/org/eclipse/emf/ecp/view/spi/validation/ValidationService.java
+++ b/bundles/org.eclipse.emf.ecp.view.validation/src/org/eclipse/emf/ecp/view/spi/validation/ValidationService.java
@@ -10,11 +10,13 @@
  *
  * Contributors:
  * Eugen - initial API and implementation
- * Christian W. Damus - bug 548761
+ * Christian W. Damus - bugs 548761, 552127
  ******************************************************************************/
 package org.eclipse.emf.ecp.view.spi.validation;
 
 import java.util.Collection;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 import org.eclipse.emf.common.notify.AdapterFactory;
 import org.eclipse.emf.ecore.EObject;
@@ -85,11 +87,34 @@
 	void deregisterValidationListener(ViewValidationListener listener);
 
 	/**
+	 * Validate the {@code objects} provided by an iterable.
+	 *
+	 * @param objects the iterable from which to obtain objects to validate
+	 *
+	 * @since 1.23
+	 */
+	default void validate(Iterable<? extends EObject> objects) {
+		if (objects instanceof Collection<?>) {
+			// This should be safe because a correct implementation must never modify the collection
+			@SuppressWarnings("unchecked")
+			final Collection<EObject> collection = (Collection<EObject>) objects;
+			validate(collection);
+		} else {
+			// Create a new collection
+			final Collection<EObject> collection = StreamSupport.stream(objects.spliterator(), false)
+				.collect(Collectors.toList());
+			validate(collection);
+		}
+	}
+
+	/**
 	 * Validates all given eObjects.
 	 *
 	 * @param eObjects the eObjects to validate
+	 *
+	 * @see {@link #validate(Iterable)} for an alternative that is more convenient when validating
+	 *      content trees covered by EMF {@code TreeIterator}s
 	 */
-	// TODO this should be refactored to use an iterator rather than a collection
 	void validate(Collection<EObject> eObjects);
 
 	/**
diff --git a/tests/org.eclipse.emf.ecp.view.validation.test/src/org/eclipse/emf/ecp/view/validation/test/ValidationService_PTest.java b/tests/org.eclipse.emf.ecp.view.validation.test/src/org/eclipse/emf/ecp/view/validation/test/ValidationService_PTest.java
index 214c7d9..1501c3a 100644
--- a/tests/org.eclipse.emf.ecp.view.validation.test/src/org/eclipse/emf/ecp/view/validation/test/ValidationService_PTest.java
+++ b/tests/org.eclipse.emf.ecp.view.validation.test/src/org/eclipse/emf/ecp/view/validation/test/ValidationService_PTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2011-2018 EclipseSource Muenchen GmbH and others.
+ * 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
@@ -10,17 +10,24 @@
  *
  * Contributors:
  * Stefan Dirix - initial API and implementation
- * Christian W. Damus - bug 533522
+ * Christian W. Damus - bugs 533522, 552127
  ******************************************************************************/
 package org.eclipse.emf.ecp.view.validation.test;
 
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
 
 import org.eclipse.emf.common.util.Diagnostic;
 import org.eclipse.emf.ecore.EObject;
@@ -35,6 +42,7 @@
 import org.eclipse.emf.ecp.view.spi.model.VViewFactory;
 import org.eclipse.emf.ecp.view.spi.validation.ValidationProvider;
 import org.eclipse.emf.ecp.view.spi.validation.ValidationService;
+import org.eclipse.emf.ecp.view.spi.validation.ViewValidationListener;
 import org.eclipse.emf.ecp.view.validation.test.model.CrossReferenceContainer;
 import org.eclipse.emf.ecp.view.validation.test.model.CrossReferenceContent;
 import org.eclipse.emf.ecp.view.validation.test.model.TableContentWithInnerChild;
@@ -389,4 +397,82 @@
 		/* assert no NPE */
 	}
 
+	@Test
+	public void testValidate_Iterable() {
+		final AtomicReference<Collection<EObject>> validated = new AtomicReference<>();
+
+		// CHECKSTYLE.OFF: AnonInnerLength - it's a test
+		final ValidationService validationService = new ValidationService() {
+
+			@Override
+			public void validate(Collection<EObject> eObjects) {
+				validated.set(eObjects);
+			}
+
+			@Override
+			public void instantiate(ViewModelContext context) {
+				// Not interesting
+			}
+
+			@Override
+			public int getPriority() {
+				// Not interesting
+				return 0;
+			}
+
+			@Override
+			public void dispose() {
+				// Not interesting
+			}
+
+			@Override
+			public void childViewModelContextAdded(ViewModelContext childContext) {
+				// Not interesting
+			}
+
+			@Override
+			public void removeValidationProvider(ValidationProvider validationProvider, boolean revalidate) {
+				// Not interesting
+			}
+
+			@Override
+			public void removeValidationProvider(ValidationProvider validationProvider) {
+				// Not interesting
+			}
+
+			@Override
+			public void registerValidationListener(ViewValidationListener listener) {
+				// TODO Auto-generated method stub
+			}
+
+			@Override
+			public void deregisterValidationListener(ViewValidationListener listener) {
+				// Not interesting
+			}
+
+			@Override
+			public void addValidationProvider(ValidationProvider validationProvider, boolean revalidate) {
+				// Not interesting
+			}
+
+			@Override
+			public void addValidationProvider(ValidationProvider validationProvider) {
+				// Not interesting
+			}
+		};
+		// CHECKSTYLE.ON: AnonInnerLength
+
+		final List<EObject> eObjects = Arrays.asList(
+			mock(EObject.class, "a"),
+			mock(EObject.class, "b"),
+			mock(EObject.class, "c"));
+
+		final Iterable<EObject> iterable = eObjects::iterator;
+		validationService.validate(iterable);
+
+		assertThat("No collection passed to validation service", validated.get(), notNullValue());
+		assertThat("Collection has wrong size", validated.get().size(), is(eObjects.size()));
+		assertThat("Collection has wrong elements", new ArrayList<>(validated.get()), equalTo(eObjects));
+	}
+
 }