Bug 577989 - Add ComputedList/Set.create

* Add factory methods for ComputedList/Set that can be called
with a lambda. (ComputedValue already had such a method.)
* Add tests, also for ComputedValue.
* Update documentation and null check on ComputedValue.

Change-Id: I8e042d661a7dd14de5a8a6a7ad7fa74e689ef3ad
Reviewed-on: https://git.eclipse.org/r/c/platform/eclipse.platform.ui/+/189189
Tested-by: Platform Bot <platform-bot@eclipse.org>
Reviewed-by: Jens Lideström <jens@lidestrom.se>
diff --git a/bundles/org.eclipse.core.databinding.observable/META-INF/MANIFEST.MF b/bundles/org.eclipse.core.databinding.observable/META-INF/MANIFEST.MF
index 09b177a..48636a6 100644
--- a/bundles/org.eclipse.core.databinding.observable/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.core.databinding.observable/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: org.eclipse.core.databinding.observable
-Bundle-Version: 1.11.0.qualifier
+Bundle-Version: 1.12.0.qualifier
 Bundle-ClassPath: .
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/ComputedList.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/ComputedList.java
index 9b84169..b3b6f34 100644
--- a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/ComputedList.java
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/list/ComputedList.java
@@ -20,6 +20,8 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
+import java.util.function.Supplier;
 
 import org.eclipse.core.databinding.observable.ChangeEvent;
 import org.eclipse.core.databinding.observable.Diffs;
@@ -89,6 +91,28 @@
 	private IObservable[] dependencies = new IObservable[0];
 
 	/**
+	 * Factory method to create {@link ComputedList} objects in an easy manner.
+	 * <p>
+	 * The created list has a null {@link IObservableList#getElementType}.
+	 *
+	 * @param supplier {@link Supplier}, which is tracked using
+	 *                 {@link ObservableTracker} to find out observables it uses, in
+	 *                 the same manner as {@link #calculate}.
+	 * @return {@link ComputedList} whose elements are computed using the given
+	 *         {@link Supplier}.
+	 * @since 1.12
+	 */
+	public static <E> IObservableList<E> create(Supplier<List<E>> supplier) {
+		Objects.requireNonNull(supplier);
+		return new ComputedList<E>() {
+			@Override
+			protected List<E> calculate() {
+				return supplier.get();
+			}
+		};
+	}
+
+	/**
 	 * Creates a computed list in the default realm and with an unknown (null)
 	 * element type.
 	 */
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/ComputedSet.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/ComputedSet.java
index 8bedb35..71f17d0 100644
--- a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/ComputedSet.java
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/set/ComputedSet.java
@@ -18,7 +18,9 @@
 
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.Objects;
 import java.util.Set;
+import java.util.function.Supplier;
 
 import org.eclipse.core.databinding.observable.ChangeEvent;
 import org.eclipse.core.databinding.observable.Diffs;
@@ -85,6 +87,28 @@
 	private IObservable[] dependencies = new IObservable[0];
 
 	/**
+	 * Factory method to create {@link ComputedSet} objects in an easy manner.
+	 * <p>
+	 * The created list has a null {@link IObservableSet#getElementType}.
+	 *
+	 * @param supplier {@link Supplier}, which is tracked using
+	 *                 {@link ObservableTracker} to find out observables it uses, in
+	 *                 the same manner as {@link #calculate}.
+	 * @return {@link ComputedSet} whose elements are computed using the given
+	 *         {@link Supplier}.
+	 * @since 1.12
+	 */
+	public static <E> IObservableSet<E> create(Supplier<Set<E>> supplier) {
+		Objects.requireNonNull(supplier);
+		return new ComputedSet<E>() {
+			@Override
+			protected Set<E> calculate() {
+				return supplier.get();
+			}
+		};
+	}
+
+	/**
 	 * Creates a computed set in the default realm and with an unknown (null)
 	 * element type.
 	 */
diff --git a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/ComputedValue.java b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/ComputedValue.java
index 43594ad..ca0f8d1 100644
--- a/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/ComputedValue.java
+++ b/bundles/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/value/ComputedValue.java
@@ -17,6 +17,7 @@
  *******************************************************************************/
 package org.eclipse.core.databinding.observable.value;
 
+import java.util.Objects;
 import java.util.function.Supplier;
 
 import org.eclipse.core.databinding.observable.ChangeEvent;
@@ -87,23 +88,24 @@
 
 	/**
 	 * Factory method to create {@link ComputedValue} objects in an easy manner.
-	 * <br>
-	 * <br>
+	 * <p>
+	 * The created list has a null {@link IObservableValue#getValueType}.
+	 * <p>
 	 * Example observing the size of an {@link IObservableList}:
 	 *
 	 * <pre>
 	 * IObservableValue&lt;Integer&gt; listSizeObservable = ComputedValue.create(() -&gt; observableList.size());
 	 * </pre>
 	 *
-	 * @param supplier {@link Supplier}, whose {@link Supplier#get()} method is a
-	 *                 TrackedGetter. See
-	 *                 {@link ObservableTracker#getterCalled(IObservable)} for
-	 *                 details.
+	 * @param supplier {@link Supplier}, which is tracked using
+	 *                 {@link ObservableTracker} to find out observables it uses, in
+	 *                 the same manner as {@link #calculate}.
 	 * @return {@link ComputedValue} whose value is computed using the given
 	 *         {@link Supplier}.
 	 * @since 1.6
 	 */
 	public static <T> IObservableValue<T> create(Supplier<T> supplier) {
+		Objects.requireNonNull(supplier);
 		return new ComputedValue<T>() {
 			@Override
 			protected T calculate() {
diff --git a/tests/org.eclipse.jface.tests.databinding/META-INF/MANIFEST.MF b/tests/org.eclipse.jface.tests.databinding/META-INF/MANIFEST.MF
index fc98213..59e0b6e 100644
--- a/tests/org.eclipse.jface.tests.databinding/META-INF/MANIFEST.MF
+++ b/tests/org.eclipse.jface.tests.databinding/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: org.eclipse.jface.tests.databinding
-Bundle-Version: 1.10.0.qualifier
+Bundle-Version: 1.11.0.qualifier
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
 Require-Bundle: org.eclipse.core.databinding;bundle-version="[1.3.0,2.0.0)",
diff --git a/tests/org.eclipse.jface.tests.databinding/pom.xml b/tests/org.eclipse.jface.tests.databinding/pom.xml
index d94e684..e82e504 100644
--- a/tests/org.eclipse.jface.tests.databinding/pom.xml
+++ b/tests/org.eclipse.jface.tests.databinding/pom.xml
@@ -19,7 +19,7 @@
   </parent>
   <groupId>org.eclipse.jface</groupId>
   <artifactId>org.eclipse.jface.tests.databinding</artifactId>
-  <version>1.10.0-SNAPSHOT</version>
+  <version>1.11.0-SNAPSHOT</version>
   <packaging>eclipse-test-plugin</packaging>
 
   <properties>
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/ComputedListTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/ComputedListTest.java
index b9ddd9c..41296e9 100644
--- a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/ComputedListTest.java
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/list/ComputedListTest.java
@@ -29,6 +29,8 @@
 import org.eclipse.core.databinding.observable.ObservableTracker;
 import org.eclipse.core.databinding.observable.Realm;
 import org.eclipse.core.databinding.observable.list.ComputedList;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.WritableList;
 import org.eclipse.jface.databinding.conformance.ObservableListContractTest;
 import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
 import org.eclipse.jface.databinding.conformance.util.ListChangeEventTracker;
@@ -98,6 +100,16 @@
 				2, tracker.count);
 	}
 
+	@Test
+	public void testCreate() throws Exception {
+		WritableList<Integer> writeList = new WritableList<>();
+		writeList.add(44);
+		IObservableList<Integer> compList = ComputedList.create(() -> new ArrayList<>(writeList));
+		assertEquals(writeList, compList);
+		writeList.add(55);
+		assertEquals(writeList, compList);
+	}
+
 	static class ComputedListStub<E> extends ComputedList<E> {
 		List<E> nextComputation = new ArrayList<E>();
 		ObservableStub dependency;
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/set/ComputedSetTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/set/ComputedSetTest.java
index c40755b..9ecaae2 100644
--- a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/set/ComputedSetTest.java
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/set/ComputedSetTest.java
@@ -29,6 +29,8 @@
 import org.eclipse.core.databinding.observable.ObservableTracker;
 import org.eclipse.core.databinding.observable.Realm;
 import org.eclipse.core.databinding.observable.set.ComputedSet;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.WritableSet;
 import org.eclipse.jface.databinding.conformance.ObservableCollectionContractTest;
 import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
 import org.eclipse.jface.databinding.conformance.util.SetChangeEventTracker;
@@ -96,6 +98,16 @@
 				2, tracker.count);
 	}
 
+	@Test
+	public void testCreate() throws Exception {
+		WritableSet<Integer> writeSet = new WritableSet<>();
+		writeSet.add(44);
+		IObservableSet<Integer> compSet = ComputedSet.create(() -> new HashSet<>(writeSet));
+		assertEquals(writeSet, compSet);
+		writeSet.add(55);
+		assertEquals(writeSet, compSet);
+	}
+
 	static class ComputedSetStub extends ComputedSet<Object> {
 		Set<Object> nextComputation = new HashSet<>();
 		ObservableStub dependency;
diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/ComputedValueTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/ComputedValueTest.java
index a66a6a4..b330e77 100755
--- a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/ComputedValueTest.java
+++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/core/tests/databinding/observable/value/ComputedValueTest.java
@@ -27,6 +27,7 @@
 import java.util.List;
 
 import org.eclipse.core.databinding.observable.value.ComputedValue;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
 import org.eclipse.core.databinding.observable.value.WritableValue;
 import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
 import org.junit.Test;
@@ -86,6 +87,15 @@
 		assertEquals("calculated value should have been that of the writable value", value.getValue(), cv.getValue());
 	}
 
+	@Test
+	public void testCreate() throws Exception {
+		WritableValue<Integer> value = new WritableValue<>(42, null);
+		IObservableValue<Integer> cv = ComputedValue.create(value::getValue);
+		assertEquals(value.getValue(), cv.getValue());
+		value.setValue(44);
+		assertEquals(value.getValue(), cv.getValue());
+	}
+
 	private static class WritableValueExt<E> extends WritableValue<E> {
 		public WritableValueExt(Object valueType, E initialValue) {
 			super(initialValue, valueType);