Bug 493697 - Provide additional 'inject' overload that accepts a static context

Change-Id: Iae0225c354db569c5e7c1f8b78757cc77b93a416
Signed-off-by: drgler <daniel.kruegler@gmail.com>
diff --git a/bundles/org.eclipse.e4.core.contexts/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.core.contexts/META-INF/MANIFEST.MF
index 28d0667..c87e35f 100644
--- a/bundles/org.eclipse.e4.core.contexts/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.e4.core.contexts/META-INF/MANIFEST.MF
@@ -1,7 +1,7 @@
 Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-SymbolicName: org.eclipse.e4.core.contexts
-Bundle-Version: 1.6.100.qualifier
+Bundle-Version: 1.7.0.qualifier
 Bundle-Name: %pluginName
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
@@ -11,7 +11,7 @@
 Import-Package: javax.inject;version="1.0.0",
  org.osgi.framework;version="1.5.0";resolution:=optional,
  org.osgi.service.event;version="1.3.0"
-Export-Package: org.eclipse.e4.core.contexts;version="1.6.0",
+Export-Package: org.eclipse.e4.core.contexts;version="1.7.0",
  org.eclipse.e4.core.internal.contexts;x-friends:="org.eclipse.e4.core.tests",
  org.eclipse.e4.core.internal.contexts.osgi;x-internal:=true
 Automatic-Module-Name: org.eclipse.e4.core.contexts
diff --git a/bundles/org.eclipse.e4.core.contexts/pom.xml b/bundles/org.eclipse.e4.core.contexts/pom.xml
index 9569328..899d457 100644
--- a/bundles/org.eclipse.e4.core.contexts/pom.xml
+++ b/bundles/org.eclipse.e4.core.contexts/pom.xml
@@ -19,6 +19,6 @@
   </parent>
   <groupId>org.eclipse.e4</groupId>
   <artifactId>org.eclipse.e4.core.contexts</artifactId>
-  <version>1.6.100-SNAPSHOT</version>
+  <version>1.7.0-SNAPSHOT</version>
   <packaging>eclipse-plugin</packaging>
 </project>
diff --git a/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/contexts/ContextInjectionFactory.java b/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/contexts/ContextInjectionFactory.java
index 7270e97..3a62d46 100644
--- a/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/contexts/ContextInjectionFactory.java
+++ b/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/contexts/ContextInjectionFactory.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2009, 2013 IBM Corporation and others.
+ * Copyright (c) 2009, 2017 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -7,7 +7,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
- *     Daniel Kruegler <daniel.kruegler@gmail.com> - Bug 527308
+ *     Daniel Kruegler <daniel.kruegler@gmail.com> - Bug 493697, 527308
  *******************************************************************************/
 package org.eclipse.e4.core.contexts;
 
@@ -75,6 +75,34 @@
 	}
 
 	/**
+	 * Injects a context into a domain object. See the class comment for details on
+	 * the injection algorithm that is used.
+	 * <p>
+	 * This method allows extra values that don't need to be tracked to be passed to
+	 * the object using staticContext. If values for the same key present in both
+	 * the context and the static context, the values from the static context are
+	 * injected.
+	 * </p>
+	 *
+	 * @param object
+	 *            The object to perform injection on
+	 * @param context
+	 *            The context to obtain injected values from
+	 * @param staticContext
+	 *            The context containing extra values; not tracked
+	 * @throws InjectionException
+	 *             if an exception occurred while performing this operation
+	 * @see #inject(Object, IEclipseContext)
+	 * @since 1.7
+	 */
+	static public void inject(Object object, IEclipseContext context, IEclipseContext staticContext)
+			throws InjectionException {
+		PrimaryObjectSupplier supplier = ContextObjectSupplier.getObjectSupplier(context, injector);
+		PrimaryObjectSupplier tempSupplier = ContextObjectSupplier.getObjectSupplier(staticContext, injector);
+		injector.inject(object, supplier, tempSupplier);
+	}
+
+	/**
 	 * Call a method, injecting the parameters from the context.
 	 * <p>
 	 * If no matching method is found on the class, an InjectionException will be
diff --git a/bundles/org.eclipse.e4.core.di/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.core.di/META-INF/MANIFEST.MF
index f8d69ab..b7d5304 100644
--- a/bundles/org.eclipse.e4.core.di/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.e4.core.di/META-INF/MANIFEST.MF
@@ -1,7 +1,7 @@
 Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-SymbolicName: org.eclipse.e4.core.di
-Bundle-Version: 1.6.200.qualifier
+Bundle-Version: 1.7.0.qualifier
 Bundle-Name: %pluginName
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
diff --git a/bundles/org.eclipse.e4.core.di/pom.xml b/bundles/org.eclipse.e4.core.di/pom.xml
index a5c65ac..b630e66 100644
--- a/bundles/org.eclipse.e4.core.di/pom.xml
+++ b/bundles/org.eclipse.e4.core.di/pom.xml
@@ -19,6 +19,6 @@
   </parent>
   <groupId>org.eclipse.e4</groupId>
   <artifactId>org.eclipse.e4.core.di</artifactId>
-  <version>1.6.200-SNAPSHOT</version>
+  <version>1.7.0-SNAPSHOT</version>
   <packaging>eclipse-plugin</packaging>
 </project>
diff --git a/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/di/IInjector.java b/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/di/IInjector.java
index 5133b7d..862a9b4 100644
--- a/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/di/IInjector.java
+++ b/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/di/IInjector.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2010, 2012 IBM Corporation and others.
+ * Copyright (c) 2010, 2017 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -7,7 +7,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
- *     Daniel Kruegler <daniel.kruegler@gmail.com> - Bug 527308
+ *     Daniel Kruegler <daniel.kruegler@gmail.com> - Bug 493697, 527308
  *******************************************************************************/
 package org.eclipse.e4.core.di;
 
@@ -66,6 +66,30 @@
 	public void inject(Object object, PrimaryObjectSupplier objectSupplier) throws InjectionException;
 
 	/**
+	 * Injects data from the supplier into a domain object. See the class comment
+	 * for details on the injection algorithm that is used.
+	 * <p>
+	 * If values for the same key present in both the object supplier and the static
+	 * supplier, the values from the static supplier are injected. Injected values
+	 * from the static supplier are not tracked and no links between the static
+	 * supplier and the object are established.
+	 * </p>
+	 *
+	 * @param object
+	 *            the object to perform injection on
+	 * @param objectSupplier
+	 *            primary object supplier for the injection
+	 * @param staticSupplier
+	 *            additional object supplier for the injection, changes in injected
+	 *            values are not tracked
+	 * @throws InjectionException
+	 *             if an exception occurred while performing this operation
+	 * @since 1.7
+	 */
+	public void inject(Object object, PrimaryObjectSupplier objectSupplier, PrimaryObjectSupplier staticSupplier)
+			throws InjectionException;
+
+	/**
 	 * Un-injects the supplier from the object.
 	 * @param object the domain object previously injected with the supplier's data
 	 * @param objectSupplier primary object supplier for the injection
diff --git a/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/InjectorImpl.java b/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/InjectorImpl.java
index 3423c6a..72f764d 100644
--- a/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/InjectorImpl.java
+++ b/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/InjectorImpl.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2009, 2015 IBM Corporation and others.
+ * Copyright (c) 2009, 2017 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -8,6 +8,7 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *     Lars Vogel <Lars.Vogel@gmail.com> - Bug 426754
+ *     Daniel Kruegler <daniel.kruegler@gmail.com> - Bug 493697
  *******************************************************************************/
 package org.eclipse.e4.core.internal.di;
 
@@ -88,13 +89,24 @@
 	@Override
 	public void inject(Object object, PrimaryObjectSupplier objectSupplier) {
 		try {
-			inject(object, objectSupplier, null);
+			internalInject(object, objectSupplier, null);
 		} catch (NoClassDefFoundError | NoSuchMethodError e) {
 			throw new InjectionException(e);
 		}
 	}
 
-	public void inject(Object object, PrimaryObjectSupplier objectSupplier, PrimaryObjectSupplier tempSupplier) {
+	@Override
+	public void inject(Object object, PrimaryObjectSupplier objectSupplier, PrimaryObjectSupplier staticSupplier)
+			throws InjectionException {
+		try {
+			internalInject(object, objectSupplier, staticSupplier);
+		} catch (NoClassDefFoundError | NoSuchMethodError e) {
+			throw new InjectionException(e);
+		}
+	}
+
+	private void internalInject(Object object, PrimaryObjectSupplier objectSupplier,
+			PrimaryObjectSupplier tempSupplier) {
 		// Two stages: first, go and collect {requestor, descriptor[] }
 		ArrayList<Requestor<?>> requestors = new ArrayList<>();
 		processClassHierarchy(object, objectSupplier, tempSupplier, true /* track */, true /* normal order */, requestors);
@@ -396,7 +408,7 @@
 
 				Object newInstance = requestor.execute();
 				if (newInstance != null) {
-					inject(newInstance, objectSupplier, tempSupplier);
+					internalInject(newInstance, objectSupplier, tempSupplier);
 					if (isSingleton) {
 						synchronized (singletonCache) { // TBD this is not quite right, synch the method
 							singletonCache.put(clazz, newInstance);
diff --git a/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/InjectStaticContextTest.java b/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/InjectStaticContextTest.java
index eafba1d..1e7f37d 100644
--- a/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/InjectStaticContextTest.java
+++ b/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/contexts/inject/InjectStaticContextTest.java
@@ -8,6 +8,7 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *     Lars Vogel <Lars.Vogel@vogella.com> - Bug 474274
+ *     Daniel Kruegler <daniel.kruegler@gmail.com> - Bug 493697
  *******************************************************************************/
 package org.eclipse.e4.core.internal.tests.contexts.inject;
 
@@ -96,19 +97,19 @@
 
 	@Test
 	public void testStaticMake() {
-		IEclipseContext parentContext = EclipseContextFactory.create();
-		parentContext.set("a", "abc");
-		parentContext.set("aConstructor", "abcConstructor");
-		parentContext.set("b", "bbc");
+		IEclipseContext trackedContext = EclipseContextFactory.create();
+		trackedContext.set("a", "abc");
+		trackedContext.set("aConstructor", "abcConstructor");
+		trackedContext.set("b", "bbc");
 
-		IEclipseContext localContext = EclipseContextFactory.create();
-		localContext.set("b", "123"); // local values override
-		localContext.set("bConstructor", "123Constructor");
-		localContext.set("c", "xyz");
+		IEclipseContext staticContext = EclipseContextFactory.create();
+		staticContext.set("b", "123"); // local values override
+		staticContext.set("bConstructor", "123Constructor");
+		staticContext.set("c", "xyz");
 
-		TestClass testObject = ContextInjectionFactory.make(TestClass.class, parentContext, localContext);
+		TestClass testObject = ContextInjectionFactory.make(TestClass.class, trackedContext, staticContext);
 
-		assertEquals(parentContext, testObject.injectedContext);
+		assertEquals(trackedContext, testObject.injectedContext);
 		assertEquals("abcConstructor", testObject.aConstructorString);
 		assertEquals("123Constructor", testObject.bConstructorString);
 		assertEquals("abc", testObject.aString);
@@ -118,9 +119,9 @@
 		assertEquals(0, testObject.preDestroyCalled);
 
 		// modify local context -> should have no effect
-		localContext.set("b", "_123_");
-		localContext.set("bConstructor", "_123Constructor_");
-		localContext.set("c", "_xyz_");
+		staticContext.set("b", "_123_");
+		staticContext.set("bConstructor", "_123Constructor_");
+		staticContext.set("c", "_xyz_");
 
 		assertEquals("abcConstructor", testObject.aConstructorString);
 		assertEquals("123Constructor", testObject.bConstructorString);
@@ -131,7 +132,7 @@
 		assertEquals(0, testObject.preDestroyCalled);
 
 		// dispose local context -> should have no effect
-		localContext.dispose();
+		staticContext.dispose();
 
 		assertEquals("abcConstructor", testObject.aConstructorString);
 		assertEquals("123Constructor", testObject.bConstructorString);
@@ -142,14 +143,14 @@
 		assertEquals(0, testObject.preDestroyCalled);
 
 		// modify parent context -> should propagate
-		parentContext.set("a", "_abc_");
-		parentContext.set("b", "_bbc_");
+		trackedContext.set("a", "_abc_");
+		trackedContext.set("b", "_bbc_");
 
 		assertEquals("_abc_", testObject.aString);
 		assertEquals("123", testObject.bString);
 
 		// uninject from the parent context
-		ContextInjectionFactory.uninject(testObject, parentContext);
+		ContextInjectionFactory.uninject(testObject, trackedContext);
 
 		assertNull(testObject.injectedContext);
 		assertNull(testObject.aString);
@@ -158,33 +159,116 @@
 		assertEquals(1, testObject.preDestroyCalled);
 
 		// further changes should have no effect
-		parentContext.set("a", "+abc+");
+		trackedContext.set("a", "+abc+");
 		assertNull(testObject.aString);
 
-		parentContext.dispose();
+		trackedContext.dispose();
+		assertEquals(1, testObject.postConstructCalled);
+		assertEquals(1, testObject.preDestroyCalled);
+	}
+
+	@Test
+	public void testStaticInject() {
+		IEclipseContext trackedContext = EclipseContextFactory.create();
+		trackedContext.set("a", "abc");
+		trackedContext.set("aConstructor", "abcConstructor");
+		trackedContext.set("b", "bbc");
+
+		IEclipseContext staticContext = EclipseContextFactory.create();
+		staticContext.set("b", "123"); // local values override
+		staticContext.set("bConstructor", "123Constructor");
+		staticContext.set("c", "xyz");
+
+		TestClass testObject = new TestClass();
+
+		assertNull(testObject.injectedContext);
+		assertNull(testObject.aConstructorString);
+		assertNull(testObject.bConstructorString);
+		assertNull(testObject.aString);
+		assertNull(testObject.bString);
+		assertNull(testObject.cString);
+		assertEquals(0, testObject.postConstructCalled);
+		assertEquals(0, testObject.preDestroyCalled);
+
+		ContextInjectionFactory.inject(testObject, trackedContext, staticContext);
+
+		assertEquals(trackedContext, testObject.injectedContext);
+		assertEquals("abcConstructor", testObject.aConstructorString);
+		assertEquals("123Constructor", testObject.bConstructorString);
+		assertEquals("abc", testObject.aString);
+		assertEquals("123", testObject.bString);
+		assertEquals("xyz", testObject.cString);
+		assertEquals(1, testObject.postConstructCalled);
+		assertEquals(0, testObject.preDestroyCalled);
+
+		// modify local context -> should have no effect
+		staticContext.set("b", "_123_");
+		staticContext.set("bConstructor", "_123Constructor_");
+		staticContext.set("c", "_xyz_");
+
+		assertEquals("abcConstructor", testObject.aConstructorString);
+		assertEquals("123Constructor", testObject.bConstructorString);
+		assertEquals("abc", testObject.aString);
+		assertEquals("123", testObject.bString);
+		assertEquals("xyz", testObject.cString);
+		assertEquals(1, testObject.postConstructCalled);
+		assertEquals(0, testObject.preDestroyCalled);
+
+		// dispose local context -> should have no effect
+		staticContext.dispose();
+
+		assertEquals("abcConstructor", testObject.aConstructorString);
+		assertEquals("123Constructor", testObject.bConstructorString);
+		assertEquals("abc", testObject.aString);
+		assertEquals("123", testObject.bString);
+		assertEquals("xyz", testObject.cString);
+		assertEquals(1, testObject.postConstructCalled);
+		assertEquals(0, testObject.preDestroyCalled);
+
+		// modify parent context -> should propagate
+		trackedContext.set("a", "_abc_");
+		trackedContext.set("b", "_bbc_");
+
+		assertEquals("_abc_", testObject.aString);
+		assertEquals("123", testObject.bString);
+
+		// uninject from the parent context
+		ContextInjectionFactory.uninject(testObject, trackedContext);
+
+		assertNull(testObject.injectedContext);
+		assertNull(testObject.aString);
+
+		assertEquals(1, testObject.postConstructCalled);
+		assertEquals(1, testObject.preDestroyCalled);
+
+		// further changes should have no effect
+		trackedContext.set("a", "+abc+");
+		assertNull(testObject.aString);
+
+		trackedContext.dispose();
 		assertEquals(1, testObject.postConstructCalled);
 		assertEquals(1, testObject.preDestroyCalled);
 	}
 
 	@Test
 	public void testStaticInvoke() {
-		IEclipseContext parentContext = EclipseContextFactory.create("main");
-		parentContext.set("a", "abc");
+		IEclipseContext trackedContext = EclipseContextFactory.create("main");
+		trackedContext.set("a", "abc");
 
-		IEclipseContext localContext = EclipseContextFactory.create("local");
-		localContext.set("b", "123");
+		IEclipseContext staticContext = EclipseContextFactory.create("static");
+		staticContext.set("b", "123");
 
 		TestInvokeClass testObject = new TestInvokeClass();
 		assertNull(testObject.aString);
 		assertNull(testObject.bString);
 
-		Object result = ContextInjectionFactory.invoke(testObject, Execute.class, parentContext, localContext, null);
+		Object result = ContextInjectionFactory.invoke(testObject, Execute.class, trackedContext, staticContext, null);
 
 		assertEquals("abc123", result);
 
 		assertEquals("abc", testObject.aString);
 		assertEquals("123", testObject.bString);
 
-		assertEquals(parentContext, testObject.context);
+		assertEquals(trackedContext, testObject.context);
 	}
 }