Bug 304586 - Injected methods only react to changes to method arguments
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/ContextChangeEvent.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/ContextChangeEvent.java
index db2fe9b..c1489e1 100644
--- a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/ContextChangeEvent.java
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/ContextChangeEvent.java
@@ -10,6 +10,8 @@
*******************************************************************************/
package org.eclipse.e4.core.services.context;
+import org.eclipse.e4.core.services.injector.IObjectProvider;
+
/**
* An event describing a change to an {@link IEclipseContext}. The following types of events are
* currently defined:
@@ -59,7 +61,7 @@
public static final int UNINJECTED = 4;
private Object[] args;
- private IEclipseContext context;
+ private IObjectProvider context;
private int eventType;
private String key;
@@ -73,7 +75,7 @@
* @param args
* @param name
*/
- ContextChangeEvent(IEclipseContext context, int eventType, Object[] args, String name,
+ ContextChangeEvent(IObjectProvider context, int eventType, Object[] args, String name,
Object oldValue) {
this.context = context;
this.key = name;
@@ -97,7 +99,7 @@
*
* @return the context where the change occurred
*/
- public IEclipseContext getContext() {
+ public IObjectProvider getContext() {
return context;
}
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/EclipseContextFactory.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/EclipseContextFactory.java
index 29b219b..7f1c0f8 100644
--- a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/EclipseContextFactory.java
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/EclipseContextFactory.java
@@ -15,7 +15,9 @@
import org.eclipse.e4.core.services.context.spi.IContextConstants;
import org.eclipse.e4.core.services.context.spi.IEclipseContextStrategy;
import org.eclipse.e4.core.services.context.spi.ILookupStrategy;
+import org.eclipse.e4.core.services.injector.IObjectProvider;
import org.eclipse.e4.core.services.internal.context.EclipseContext;
+import org.eclipse.e4.core.services.internal.context.ObjectProviderContext;
import org.eclipse.e4.internal.core.services.osgi.OSGiContextStrategy;
import org.osgi.framework.BundleContext;
@@ -94,6 +96,12 @@
*/
public static ContextChangeEvent createContextEvent(IEclipseContext context, int eventType,
Object[] args, String name, Object oldValue) {
- return new ContextChangeEvent(context, eventType, args, name, oldValue);
+ return new ContextChangeEvent(new ObjectProviderContext(context), eventType, args, name,
+ oldValue);
+ }
+
+ public static ContextChangeEvent createContextEvent(IObjectProvider provider, int eventType,
+ Object[] args, String name, Object oldValue) {
+ return new ContextChangeEvent(provider, eventType, args, name, oldValue);
}
}
\ No newline at end of file
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/spi/ContextInjectionFactory.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/spi/ContextInjectionFactory.java
index 983f036..9b1a551 100644
--- a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/spi/ContextInjectionFactory.java
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/context/spi/ContextInjectionFactory.java
@@ -16,6 +16,7 @@
import org.eclipse.e4.core.services.IDisposable;
import org.eclipse.e4.core.services.context.IEclipseContext;
import org.eclipse.e4.core.services.context.IEclipseContextAware;
+import org.eclipse.e4.core.services.internal.context.EclipseContext;
import org.eclipse.e4.core.services.internal.context.ObjectProviderContext;
/**
@@ -89,8 +90,8 @@
static public Object inject(Object object, IEclipseContext context) {
// TBD IEclipseContext might have a delayed runAndTrack. In this case
// the object might be returned before the actual injection took place.
- context.runAndTrack(ObjectProviderContext.getObjectProvider(context),
- new Object[] { object });
+ ObjectProviderContext provider = ObjectProviderContext.getObjectProvider(context);
+ provider.init(object);
return object;
}
@@ -145,8 +146,7 @@
* the context previously injected into the object
*/
static public void uninject(Object object, IEclipseContext context) {
- ObjectProviderContext objectProvider = ObjectProviderContext.getObjectProvider(context);
- objectProvider.getInjector().uninject(object);
+ ((EclipseContext) context).removeListenersTo(object);
}
/**
@@ -169,7 +169,7 @@
ObjectProviderContext objectProvider = ObjectProviderContext.getObjectProvider(context);
Object result = objectProvider.getInjector().make(clazz);
if (result != null)
- context.runAndTrack(objectProvider, new Object[] { result });
+ inject(result, context);
return result;
}
}
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/injector/IObjectProvider.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/injector/IObjectProvider.java
index 5a4d39c..1e0909c 100644
--- a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/injector/IObjectProvider.java
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/injector/IObjectProvider.java
@@ -10,6 +10,8 @@
*******************************************************************************/
package org.eclipse.e4.core.services.injector;
+import org.eclipse.e4.core.services.context.IRunAndTrack;
+
/**
* This interface describes an "object provider" - something that knows how to instantiate objects
* corresponding to the key. NOTE: This is a preliminary form; this API will change.
@@ -26,4 +28,5 @@
public String getKey(IObjectDescriptor key);
+ public void runAndTrack(final IRunAndTrack runnable, Object[] args);
}
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/injector/Injector.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/injector/Injector.java
index d328d2e..11d2681 100644
--- a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/injector/Injector.java
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/injector/Injector.java
@@ -12,7 +12,6 @@
import java.lang.reflect.InvocationTargetException;
import org.eclipse.core.runtime.CoreException;
-import org.eclipse.e4.core.services.IDisposable;
import org.eclipse.e4.core.services.internal.context.ContextInjector;
/**
@@ -20,7 +19,7 @@
*
* @noextend This class is not intended to be subclassed by clients.
*/
-final public class Injector implements IDisposable {
+final public class Injector {
final private ContextInjector eInjector;
@@ -32,22 +31,6 @@
return eInjector.inject(object);
}
- public void reinject() {
- eInjector.reinject();
- }
-
- public boolean uninject(Object object) {
- return eInjector.uninject(object);
- }
-
- public void added(IObjectDescriptor descriptor) {
- eInjector.added(descriptor);
- }
-
- public void removed(IObjectDescriptor descriptor) {
- eInjector.removed(descriptor);
- }
-
public Object make(Class clazz) throws InvocationTargetException, InstantiationException {
return eInjector.make(clazz);
}
@@ -66,12 +49,4 @@
return eInjector.injectStatic(clazz);
}
- public void reparent(IObjectProvider oldParent) {
- eInjector.reparent(oldParent);
- }
-
- public void dispose() {
- eInjector.dispose();
- }
-
}
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/Computation.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/Computation.java
index c200b38..2116716 100644
--- a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/Computation.java
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/Computation.java
@@ -18,6 +18,7 @@
import java.util.Set;
import org.eclipse.e4.core.services.context.ContextChangeEvent;
import org.eclipse.e4.core.services.context.IEclipseContext;
+import org.eclipse.e4.core.services.injector.IObjectProvider;
abstract class Computation {
Map dependencies = new HashMap();
@@ -48,7 +49,9 @@
public abstract boolean equals(Object arg0);
final void handleInvalid(ContextChangeEvent event, List scheduled) {
- IEclipseContext context = event.getContext();
+ IObjectProvider provider = event.getContext();
+ IEclipseContext context = ((ObjectProviderContext) provider).getContext();
+
String name = event.getName();
Set names = (Set) dependencies.get(context);
if (name == null && event.getEventType() == ContextChangeEvent.DISPOSE) {
@@ -151,4 +154,8 @@
}
}
+ public Set dependsOnNames(IEclipseContext context) {
+ return (Set) dependencies.get(context);
+ }
+
}
\ No newline at end of file
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ContextInjector.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ContextInjector.java
index 4c4c618..de4f43b 100644
--- a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ContextInjector.java
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ContextInjector.java
@@ -19,16 +19,11 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
-import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.e4.core.internal.services.ServicesActivator;
-import org.eclipse.e4.core.services.IDisposable;
-import org.eclipse.e4.core.services.context.IEclipseContext;
-import org.eclipse.e4.core.services.context.spi.ContextInjectionFactory;
import org.eclipse.e4.core.services.context.spi.IContextConstants;
-import org.eclipse.e4.core.services.injector.IObjectDescriptor;
import org.eclipse.e4.core.services.injector.IObjectProvider;
import org.eclipse.e4.core.services.internal.annotations.AnnotationsSupport;
@@ -38,190 +33,6 @@
*/
public class ContextInjector {
- private class Processor {
-
- final private IObjectDescriptor descriptor;
- protected boolean addition;
-
- protected boolean shouldProcessPostConstruct = false;
- protected boolean isInDispose = false;
- protected Object userObject;
-
- protected boolean injectWithNulls = false;
-
- protected boolean processStatic = false;
-
- private List postConstructMethods;
-
- public ArrayList classHierarchy = new ArrayList(5);
-
- public Processor(IObjectDescriptor descriptor, boolean addition, boolean isInDispose) {
- this.descriptor = descriptor;
- this.addition = addition;
- this.isInDispose = isInDispose;
- }
-
- public void setObject(Object userObject) {
- this.userObject = userObject;
- // this operation also resets state variables
- classHierarchy.clear();
- }
-
- public void setInjectNulls(boolean injectWithNulls) {
- this.injectWithNulls = injectWithNulls;
- }
-
- public void setProcessStatic(boolean processStatic) {
- this.processStatic = processStatic;
- }
-
- /**
- * The method assumes injection is needed for this field.
- */
- public boolean processField(final Field field, InjectionProperties properties) {
- if (Modifier.isStatic(field.getModifiers()) != processStatic)
- return true;
- if (descriptor != null) { // filter if descriptor is specified
- String descriptorsKey = context.getKey(descriptor);
- if (!descriptorsKey.equals(context.getKey(properties)))
- return true;
- }
- Object value = null;
- if (addition) {
- Object provider = properties.getProvider();
- if (provider != null)
- value = provider;
- else if (context.containsKey(properties))
- value = context.get(properties);
- else {
- if (!properties.isOptional()) {
- if (shouldTrace)
- System.out.println("Could not set " + field.getName()
- + " because of the missing: " + context.getKey(properties));
- return false;
- }
- return true;
- }
- }
- return setField(userObject, field, value);
- }
-
- public boolean processMethod(final Method method, boolean optional)
- throws InvocationTargetException {
- if (Modifier.isStatic(method.getModifiers()) != processStatic)
- return true;
- // we only get here if we are injecting
- InjectionProperties[] properties = annotationSupport.getInjectParamProperties(method);
- if (descriptor != null) {
- // is it one of the arguments of this method?
- boolean found = false;
- String descriptorsKey = context.getKey(descriptor);
- for (int i = 0; i < properties.length; i++) {
- if (descriptorsKey.equals(context.getKey(properties[i]))) {
- found = true;
- break;
- }
- }
- if (!found)
- return true;
- }
-
- Object[] actualParams = processParams(properties, method.getParameterTypes(),
- !addition, injectWithNulls);
- if (actualParams != null)
- callMethod(userObject, method, actualParams);
- else if (!optional) {
- if (shouldTrace)
- System.out.println("Could not invoke " + method.getName()
- + ": no matching context elements");
- return false;
- }
- return true;
- }
-
- public void addPostConstructMethod(Method method) {
- if (postConstructMethods == null)
- postConstructMethods = new ArrayList(1);
- postConstructMethods.add(method);
- }
-
- public void processPostConstructMethod() throws InvocationTargetException {
- if (!shouldProcessPostConstruct)
- return;
- if (postConstructMethods == null)
- return;
- for (Iterator it = postConstructMethods.iterator(); it.hasNext();) {
- Method method = (Method) it.next();
- InjectionProperties[] properties = annotationSupport
- .getInjectParamProperties(method);
- Object[] actualParams = processParams(properties, method.getParameterTypes(),
- !addition, injectWithNulls);
- if (actualParams == null)
- logError(userObject, new IllegalArgumentException());
- else
- callMethod(userObject, method, actualParams);
- }
- postConstructMethods.clear();
- }
-
- }
-
- // TBD investigate if this approach to reparenting works with calculated values and providers
- private class ReparentProcessor extends Processor {
-
- private IObjectProvider oldParent;
-
- public ReparentProcessor(IObjectProvider oldParent) {
- super(null, true /* set */, false);
- this.oldParent = oldParent;
- }
-
- /**
- * Returns whether the value associated with the given key is affected by the parent change.
- */
- private boolean hasChanged(InjectionProperties key) {
- // if value is local then parent change has no effect
- // XXX this is incorrect
- // if (context.getLocal(key) != null)
- // return false;
- // XXX this is incorrect: different parents, same grandparent
- // Object oldValue = oldParent == null ? null : oldParent.internalGet(
- // (EclipseContext) context, key, null, false);
- // Object newValue = context == null ? null : ((EclipseContext) context).internalGet(
- // (EclipseContext) context, key, null, false);
- // return oldValue != newValue;
-
- // XXX for now, check if values are different
- Object oldValue = oldParent.get(key);
- Object newValue = context.get(key);
- return (oldValue != newValue); // use pointer comparison, not #equals()
- }
-
- public boolean processField(final Field field, InjectionProperties properties) {
- if (hasChanged(properties))
- return super.processField(field, properties);
- return true;
- }
-
- public boolean processMethod(final Method method, boolean optional)
- throws InvocationTargetException {
- // any argument changed?
- InjectionProperties[] properties = annotationSupport.getInjectParamProperties(method);
-
- boolean changed = false;
- for (int i = 0; i < properties.length; i++) {
- if (hasChanged(properties[i])) {
- changed = true;
- break;
- }
- }
- if (changed)
- return super.processMethod(method, optional);
- return true;
- }
-
- }
-
final static private String DEBUG_INJECTOR = "org.eclipse.e4.core.services/debug/injector"; //$NON-NLS-1$
final static private boolean shouldTrace = ServicesActivator.getDefault()
.getBooleanDebugOption(DEBUG_INJECTOR, false);
@@ -231,60 +42,28 @@
final protected IObjectProvider context;
final private AnnotationsSupport annotationSupport;
- protected WeakRefList userObjects = new WeakRefList(3); // start small
-
public ContextInjector(IObjectProvider context) {
this.context = context;
// plug-in class that gets replaced in Java 1.5+
- annotationSupport = new AnnotationsSupport(context);
- }
-
- public void added(IObjectDescriptor descriptor) {
- Object[] objectsCopy = userObjects.getSafeCopy();
- Processor processor = new Processor(descriptor, true, false);
- for (int i = 0; i < objectsCopy.length; i++) {
- try {
- processClassHierarchy(objectsCopy[i], processor);
- } catch (InvocationTargetException e) {
- logExternalError("Exception occured while processing addition on", objectsCopy[i],
- e);
- }
- }
+ annotationSupport = new AnnotationsSupport(context); // XXX remove
}
public boolean inject(Object userObject) {
- Processor processor = new Processor(null, true, false);
- processor.shouldProcessPostConstruct = true;
boolean result = false;
try {
- result = processClassHierarchy(userObject, processor);
+ result = processClassHierarchy(userObject, false /* process static */);
} catch (InvocationTargetException e) {
logExternalError("Exception occured while processing injecting", userObject, e);
}
- userObjects.add(userObject);
+ context.runAndTrack(new InjectionClass(userObject, context), null);
return result;
}
- public void reinject() {
- Processor processor = new Processor(null, true, false);
- Object[] objectsCopy = userObjects.getSafeCopy();
- for (int i = 0; i < objectsCopy.length; i++) {
- try {
- processClassHierarchy(objectsCopy[i], processor);
- } catch (InvocationTargetException e) {
- logExternalError("Exception occured while processing removal on", objectsCopy[i], e);
- }
- }
- }
-
// TBD use null object to inject statics
public boolean injectStatic(Class clazz) {
- Processor processor = new Processor(null, true, false);
- processor.shouldProcessPostConstruct = true;
- processor.setProcessStatic(true);
try {
Object object = make(clazz);
- return processClassHierarchy(object, processor);
+ return processClassHierarchy(object, true /* process static */);
} catch (InvocationTargetException e) {
// try-catch won't be necessary once we stop creating an object
e.printStackTrace();
@@ -295,126 +74,54 @@
return false;
}
- public void removed(IObjectDescriptor descriptor) {
- Processor processor = new Processor(descriptor, false, false);
- Object[] objectsCopy = userObjects.getSafeCopy();
- for (int i = 0; i < objectsCopy.length; i++) {
- try {
- processClassHierarchy(objectsCopy[i], processor);
- } catch (InvocationTargetException e) {
- logExternalError("Exception occured while processing removal on", objectsCopy[i], e);
- }
- }
- }
-
- public boolean uninject(Object releasedObject) {
- if (!userObjects.remove(releasedObject))
- return false;
- Processor processor = new Processor(null, false, false);
- processor.setInjectNulls(true);
- try {
- return processClassHierarchy(releasedObject, processor);
- } catch (InvocationTargetException e) {
- logExternalError("Exception occured while uninjecting", releasedObject, e);
- }
- return false;
- }
-
- public void dispose() {
- Object[] objectsCopy = userObjects.getSafeCopy();
- Processor processor = new Processor(null, false, true);
- processor.setInjectNulls(true);
- for (int i = 0; i < objectsCopy.length; i++) {
- if (objectsCopy[i] instanceof IDisposable)
- ((IDisposable) objectsCopy[i]).dispose();
- try {
- processClassHierarchy(objectsCopy[i], processor);
- } catch (InvocationTargetException e) {
- logExternalError("Exception occured while disposing", objectsCopy[i], e);
- }
- }
- }
-
- public void reparent(IObjectProvider oldParent) {
- if (oldParent == context)
- return;
- Object[] objectsCopy = userObjects.getSafeCopy();
- Processor processor = new ReparentProcessor(oldParent);
- for (int i = 0; i < objectsCopy.length; i++) {
- try {
- processClassHierarchy(objectsCopy[i], processor);
- } catch (InvocationTargetException e) {
- logExternalError("Exception occured while reparenting", objectsCopy[i], e);
- }
- }
- }
-
/**
* Make the processor visit all declared members on the given class and all superclasses
*
* @throws InvocationTargetException
*/
- private boolean processClass(Class objectsClass, Processor processor)
- throws InvocationTargetException {
- if (processor.addition) {
- // order: superclass, fields, methods
- if (objectsClass != null) {
- Class superClass = objectsClass.getSuperclass();
- if (!superClass.getName().equals(JAVA_OBJECT)) {
- processor.classHierarchy.add(objectsClass);
- if (!processClass(superClass, processor))
- return false;
- processor.classHierarchy.remove(objectsClass);
- }
- }
- if (!processFields(objectsClass, processor))
- return false;
- if (!processMethods(objectsClass, processor))
- return false;
- } else {
- // order: methods, fields, superclass
- if (!processMethods(objectsClass, processor))
- return false;
- if (!processFields(objectsClass, processor))
- return false;
- if (objectsClass != null) {
- Class superClass = objectsClass.getSuperclass();
- if (!superClass.getName().equals(JAVA_OBJECT)) {
- processor.classHierarchy.add(objectsClass);
- if (!processClass(superClass, processor))
- return false;
- processor.classHierarchy.remove(objectsClass);
- }
+ private boolean processClass(Object userObject, Class objectsClass, ArrayList classHierarchy,
+ boolean processStatic) throws InvocationTargetException {
+ // order: superclass, fields, methods
+ if (objectsClass != null) {
+ Class superClass = objectsClass.getSuperclass();
+ if (!superClass.getName().equals(JAVA_OBJECT)) {
+ classHierarchy.add(objectsClass);
+ if (!processClass(userObject, superClass, classHierarchy, processStatic))
+ return false;
+ classHierarchy.remove(objectsClass);
}
}
+ if (!processFields(userObject, objectsClass, processStatic))
+ return false;
+ if (!processMethods(userObject, objectsClass, classHierarchy, processStatic))
+ return false;
return true;
}
- private boolean processClassHierarchy(Object userObject, Processor processor)
+ private boolean processClassHierarchy(Object userObject, boolean processStatic)
throws InvocationTargetException {
- processor.setObject(userObject);
- if (!processClass((userObject == null) ? null : userObject.getClass(), processor))
+ if (!processClass(userObject, (userObject == null) ? null : userObject.getClass(),
+ new ArrayList(5), processStatic))
return false;
- processor.processPostConstructMethod();
return true;
}
/**
* Make the processor visit all declared fields on the given class.
*/
- private boolean processFields(Class objectsClass, Processor processor) {
+ private boolean processFields(Object userObject, Class objectsClass, boolean processStatic) {
Field[] fields = objectsClass.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
-
+ if (Modifier.isStatic(field.getModifiers()) != processStatic)
+ continue;
InjectionProperties properties = annotationSupport.getInjectProperties(field);
if (field.getName().startsWith(IContextConstants.INJECTION_PREFIX))
properties.setInject(true);
if (!properties.shouldInject())
continue;
- if (!processor.processField(field, properties))
- return false;
+ context.runAndTrack(new InjectionField(userObject, context, field), null);
}
return true;
}
@@ -424,66 +131,32 @@
*
* @throws InvocationTargetException
*/
- private boolean processMethods(Class objectsClass, Processor processor)
- throws InvocationTargetException {
+ private boolean processMethods(Object userObject, Class objectsClass, ArrayList classHierarchy,
+ boolean processStatic) throws InvocationTargetException {
Method[] methods = objectsClass.getDeclaredMethods();
- if (processor.isInDispose) {
- for (int i = 0; i < methods.length; i++) {
- Method method = methods[i];
- if (method.getParameterTypes().length > 0) // TBD why?
- continue;
- if (!annotationSupport.isPreDestory(method))
- continue;
- if (!isOverridden(method, processor))
- callMethod(processor.userObject, method, null);
- }
- }
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
- if (isOverridden(method, processor))
+ if (isOverridden(method, classHierarchy))
continue; // process in the subclass
- if (processor.shouldProcessPostConstruct) {
- if (isPostConstruct(method)) {
- processor.addPostConstructMethod(method);
- continue;
- }
- }
-
+ if (Modifier.isStatic(method.getModifiers()) != processStatic)
+ continue;
InjectionProperties properties = annotationSupport.getInjectProperties(method);
if (method.getName().startsWith(IContextConstants.INJECTION_PREFIX))
properties.setInject(true);
if (!properties.shouldInject())
continue;
- if (!processor.processMethod(method, properties.isOptional()))
- return false;
+
+ context.runAndTrack(new InjectionMethod(userObject, context, method), null);
}
return true;
}
- // TBD simplify this: only one non-annotation and one "implements IInitializable"?
- /**
- * Returns whether the given method is a post-construction process method, as defined by the
- * class comment of {@link ContextInjectionFactory}.
- */
- private boolean isPostConstruct(Method method) {
- boolean isPostConstruct = annotationSupport.isPostConstruct(method);
- if (isPostConstruct)
- return true;
- if (!method.getName().equals(IContextConstants.INJECTION_SET_CONTEXT_METHOD))
- return false;
- Class[] parms = method.getParameterTypes();
- if (parms.length == 0)
- return true;
- if (parms.length == 1 && parms[0].equals(IEclipseContext.class))
- return true;
- return false;
- }
-
+ // TBD this is the same method as in the class injector
/**
* Checks if a given method is overridden with an injectable method.
*/
- private boolean isOverridden(Method method, Processor processor) {
+ private boolean isOverridden(Method method, ArrayList classHierarchy) {
int modifiers = method.getModifiers();
if (Modifier.isPrivate(modifiers))
return false;
@@ -492,7 +165,7 @@
// method is not private if we reached this line, check not(public OR protected)
boolean isDefault = !(Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers));
- for (Iterator i = processor.classHierarchy.iterator(); i.hasNext();) {
+ for (Iterator i = classHierarchy.iterator(); i.hasNext();) {
Class subClass = (Class) i.next();
Method override = null;
try {
@@ -528,11 +201,14 @@
if (!method.getName().equals(methodName))
continue;
- InjectionProperties[] properties = annotationSupport.getInjectParamProperties(method);
- Object[] actualParams = processParams(properties, method.getParameterTypes(), false,
- false);
- if (actualParams != null)
- return callMethod(userObject, method, actualParams);
+ InjectionMethod injectMethod = new InjectionMethod(userObject, context, method);
+ try {
+ return injectMethod.invoke(false, false);
+ } catch (InjectionException e) {
+ IStatus status = new Status(IStatus.ERROR, "org.eclipse.e4.core.services",
+ "Unable to invoke method");
+ throw new CoreException(status);
+ }
}
IStatus status = new Status(IStatus.ERROR, "org.eclipse.e4.core.services",
"Unable to find matching method to invoke");
@@ -552,11 +228,12 @@
if (!method.getName().equals(methodName))
continue;
- InjectionProperties[] properties = annotationSupport.getInjectParamProperties(method);
- Object[] actualParams = processParams(properties, method.getParameterTypes(), false,
- false);
- if (actualParams != null)
- return callMethod(userObject, method, actualParams);
+ InjectionMethod injectMethod = new InjectionMethod(userObject, context, method);
+ try {
+ return injectMethod.invoke(false, false);
+ } catch (InjectionException e) {
+ // TBD?
+ }
}
Class superClass = currentClass.getSuperclass();
if (superClass == null) {
@@ -593,13 +270,9 @@
if (!cProps.shouldInject() && constructor.getParameterTypes().length != 0)
continue;
- InjectionProperties[] properties = annotationSupport
- .getInjectParamsProperties(constructor);
- Object[] actualParams = processParams(properties, constructor.getParameterTypes(),
- false, false);
- if (actualParams == null)
- continue;
- Object newInstance = callConstructor(constructor, actualParams);
+ InjectionConstructor injectedConstructor = new InjectionConstructor(null, context,
+ constructor);
+ Object newInstance = injectedConstructor.make();
if (newInstance != null)
return newInstance;
}
@@ -610,129 +283,6 @@
return null;
}
- private Object[] processParams(InjectionProperties[] properties, Class[] parameterTypes,
- boolean ignoreMissing, boolean injectWithNulls) {
- Object[] actualParams = new Object[properties.length];
- for (int i = 0; i < actualParams.length; i++) {
- // 1) if we have a provider, use it
- Object provider = properties[i].getProvider();
- if (provider != null) {
- actualParams[i] = provider;
- continue;
- }
- // 2) if we have the key in the context
- if (context.containsKey(properties[i])) {
- if (injectWithNulls) {
- actualParams[i] = null;
- continue;
- } else {
- Object candidate = context.get(properties[i]);
- if (candidate != null
- && parameterTypes[i].isAssignableFrom(candidate.getClass())) {
- actualParams[i] = candidate;
- continue;
- }
- }
- }
- // 3) can we ignore this argument?
- if (ignoreMissing || properties[i].isOptional()) {
- actualParams[i] = null;
- continue;
- }
- return null;
- }
- return actualParams;
- }
-
- private boolean setField(Object userObject, Field field, Object value) {
- if ((value != null) && !field.getType().isAssignableFrom(value.getClass())) {
- // TBD add debug option
- return false;
- }
-
- boolean wasAccessible = true;
- if (!field.isAccessible()) {
- field.setAccessible(true);
- wasAccessible = false;
- }
- try {
- field.set(userObject, value);
- } catch (IllegalArgumentException e) {
- logError(field, e);
- return false;
- } catch (IllegalAccessException e) {
- logError(field, e);
- return false;
- } finally {
- if (!wasAccessible)
- field.setAccessible(false);
- }
- return true;
- }
-
- private Object callMethod(Object userObject, Method method, Object[] args)
- throws InvocationTargetException {
- Object result = null;
- boolean wasAccessible = true;
- if (!method.isAccessible()) {
- method.setAccessible(true);
- wasAccessible = false;
- }
- try {
- result = method.invoke(userObject, args);
- } catch (IllegalArgumentException e) {
- // should not happen, is checked during formation of the array of actual arguments
- logError(method, e);
- return null;
- } catch (IllegalAccessException e) {
- // should not happen, is checked at the start of this method
- logError(method, e);
- return null;
- } finally {
- if (!wasAccessible)
- method.setAccessible(false);
- }
- return result;
- }
-
- private Object callConstructor(Constructor constructor, Object[] args)
- throws InvocationTargetException, InstantiationException {
- if (args != null) { // make sure args are assignable
- Class[] parameterTypes = constructor.getParameterTypes();
- if (parameterTypes.length != args.length) {
- // internal error, log it
- logError(constructor, new IllegalArgumentException());
- return null;
- }
- for (int i = 0; i < args.length; i++) {
- if ((args[i] != null) && !parameterTypes[i].isAssignableFrom(args[i].getClass()))
- return null;
- }
- }
-
- Object result = null;
- boolean wasAccessible = true;
- if (!constructor.isAccessible()) {
- constructor.setAccessible(true);
- wasAccessible = false;
- }
- try {
- result = constructor.newInstance(args);
- } catch (IllegalArgumentException e) {
- // should not happen, is checked at the start of this method
- logError(constructor, e);
- return null;
- } catch (IllegalAccessException e) {
- // should not happen as we set constructor to be accessible
- logError(constructor, e);
- return null;
- } finally {
- if (!wasAccessible)
- constructor.setAccessible(false);
- }
- return result;
- }
-
private void logExternalError(String msg, Object destination, Exception e) {
System.out.println(msg + " " + destination.toString()); //$NON-NLS-1$
if (e != null)
@@ -743,14 +293,4 @@
// IRuntimeConstants.PI_COMMON, 0, msg, e));
}
- private void logError(Object destination, Exception e) {
- System.out.println("Injection failed " + destination.toString()); //$NON-NLS-1$
- if (e != null)
- e.printStackTrace();
- // TBD convert this into real logging
- // String msg = NLS.bind("Injection failed", destination.toString());
- // RuntimeLog.log(new Status(IStatus.WARNING,
- // IRuntimeConstants.PI_COMMON, 0, msg, e));
- }
-
}
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/EclipseContext.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/EclipseContext.java
index 4ffe78a..50df977 100644
--- a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/EclipseContext.java
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/EclipseContext.java
@@ -122,6 +122,12 @@
this.runnable = runnable;
}
+ public Object getObject() {
+ if (runnable instanceof IRunAndTrackObject)
+ return ((IRunAndTrackObject) runnable).getObject();
+ return null;
+ }
+
final protected void doHandleInvalid(ContextChangeEvent event, List scheduledList) {
int eventType = event.getEventType();
if (eventType == ContextChangeEvent.INITIAL || eventType == ContextChangeEvent.DISPOSE) {
@@ -189,7 +195,7 @@
};
static class DebugSnap {
- Set listeners = new HashSet();
+ List listeners = new ArrayList();
Map localValueComputations = Collections.synchronizedMap(new HashMap());
Map localValues = Collections.synchronizedMap(new HashMap());
}
@@ -205,7 +211,7 @@
private static final Object[] NO_ARGUMENTS = new Object[0];
- final Set listeners = new HashSet();
+ final List listeners = new ArrayList();
final Map localValueComputations = Collections.synchronizedMap(new HashMap());
final Map localValues = Collections.synchronizedMap(new HashMap());
@@ -243,7 +249,7 @@
*/
public void debugSnap() {
snapshot = new DebugSnap();
- snapshot.listeners = new HashSet(listeners);
+ snapshot.listeners = new ArrayList(listeners);
snapshot.localValueComputations = new HashMap(localValueComputations);
snapshot.localValues = new HashMap(localValues);
}
@@ -254,9 +260,9 @@
public void debugDiff() {
if (snapshot == null)
return;
- Set listenerDiff = new HashSet(listeners);
+ List listenerDiff = new ArrayList(listeners);
listenerDiff.removeAll(snapshot.listeners);
- listenerDiff = new HashSet(listenerDiff);// shrink the set
+ listenerDiff = new ArrayList(listenerDiff);// shrink the set
System.out.println("Listener diff: ");
for (Iterator it = listenerDiff.iterator(); it.hasNext();) {
System.out.println("\t" + it.next());
@@ -286,11 +292,14 @@
Computation[] ls = (Computation[]) listeners.toArray(new Computation[listeners.size()]);
ContextChangeEvent event = EclipseContextFactory.createContextEvent(this,
ContextChangeEvent.DISPOSE, null, null, null);
- for (int i = 0; i < ls.length; i++) {
+ // reverse order of listeners
+ for (int i = ls.length - 1; i >= 0; i--) {
List scheduled = new ArrayList();
ls[i].handleInvalid(event, scheduled);
processScheduled(scheduled);
}
+
+ // TBD used by OSGI Context startegy - is this needed? Looks like @PreDestroy
if (strategy instanceof IDisposable)
((IDisposable) strategy).dispose();
}
@@ -426,6 +435,14 @@
}
public void set(String name, Object value) {
+ if (IContextConstants.PARENT.equals(name)) {
+ // TBD make setting parent a separate operation
+ List scheduled = new ArrayList();
+ handleReparent((EclipseContext) value, scheduled);
+ localValues.put(IContextConstants.PARENT, value);
+ processScheduled(scheduled);
+ return;
+ }
boolean containsKey = localValues.containsKey(name);
Object oldValue = localValues.put(name, value);
if (!containsKey || value != oldValue) {
@@ -511,4 +528,44 @@
return false;
}
+ public void removeListenersTo(Object object) {
+ if (object == null)
+ return;
+ synchronized (listeners) {
+ ContextChangeEvent event = EclipseContextFactory.createContextEvent(this,
+ ContextChangeEvent.UNINJECTED, null, null, null);
+ for (Iterator i = listeners.iterator(); i.hasNext();) {
+ Computation computation = (Computation) i.next();
+ if (computation instanceof TrackableComputationExt) {
+ if (object == ((TrackableComputationExt) computation).getObject()) {
+ ((IRunAndTrack) computation).notify(event);
+ i.remove();
+ }
+ }
+ }
+ }
+ }
+
+ private void handleReparent(EclipseContext newParent, List scheduled) {
+ // 1) everybody who depends on me: I need to collect combined list of names injected
+ Computation[] ls = (Computation[]) listeners.toArray(new Computation[listeners.size()]);
+ Set usedNames = new HashSet();
+ for (int i = 0; i < ls.length; i++) {
+ Set listenerNames = ls[i].dependsOnNames(this);
+ if (listenerNames == null)
+ continue; // should not happen?
+ usedNames.addAll(listenerNames); // also removes duplicates
+ }
+ // 2) for each used name:
+ for (Iterator i = usedNames.iterator(); i.hasNext();) {
+ String name = (String) i.next();
+ if (localValues.containsKey(name))
+ continue; // it is a local value
+ Object oldValue = get(name);
+ Object newValue = (newParent != null) ? newParent.get(name) : null;
+ if (oldValue != newValue)
+ invalidate(name, ContextChangeEvent.ADDED, oldValue, scheduled);
+ }
+ localValueComputations.clear();
+ }
}
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/IRunAndTrackObject.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/IRunAndTrackObject.java
new file mode 100644
index 0000000..6206f7b
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/IRunAndTrackObject.java
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 2010 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.e4.core.services.internal.context;
+
+import org.eclipse.e4.core.services.context.IRunAndTrack;
+
+public interface IRunAndTrackObject extends IRunAndTrack {
+
+ public Object getObject();
+
+}
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionAbstract.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionAbstract.java
new file mode 100644
index 0000000..9c5d741
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionAbstract.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2010 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.e4.core.services.internal.context;
+
+import java.lang.ref.WeakReference;
+import org.eclipse.e4.core.services.context.ContextChangeEvent;
+import org.eclipse.e4.core.services.injector.IObjectProvider;
+import org.eclipse.e4.core.services.internal.annotations.AnnotationsSupport;
+
+abstract public class InjectionAbstract implements IRunAndTrackObject {
+
+ final static protected Object NOT_A_VALUE = new Object();
+
+ final protected WeakReference userObjectRef;
+ final protected AnnotationsSupport annotationSupport;
+ final protected IObjectProvider primarySupplier;
+
+ protected boolean optional = false;
+
+ abstract public boolean notify(ContextChangeEvent event);
+
+ public InjectionAbstract(Object userObject, IObjectProvider primarySupplier) {
+ userObjectRef = new WeakReference(userObject);
+ // plug-in class that gets replaced in Java 1.5+
+ annotationSupport = new AnnotationsSupport(primarySupplier);
+ this.primarySupplier = primarySupplier;
+ }
+
+ public Object getObject() {
+ if (userObjectRef == null)
+ return null;
+ return userObjectRef.get();
+ }
+
+ protected boolean injectNulls(int eventType, IObjectProvider changed) {
+ if ((eventType != ContextChangeEvent.UNINJECTED)
+ && (eventType != ContextChangeEvent.DISPOSE))
+ return false;
+ return (changed.equals(primarySupplier));
+ }
+
+ protected boolean ignoreMissing(int eventType, IObjectProvider changed) {
+ if (eventType == ContextChangeEvent.REMOVED)
+ return true;
+ if ((eventType != ContextChangeEvent.UNINJECTED)
+ && (eventType != ContextChangeEvent.DISPOSE))
+ return false;
+ return (changed.equals(primarySupplier));
+ }
+
+ protected Object getValue(InjectionProperties properties, Class parameterType,
+ boolean ignoreMissing, boolean injectWithNulls) {
+ // 1) if we have a provider, use it
+ Object provider = properties.getProvider();
+ if (provider != null)
+ return provider;
+
+ // 2) if we have the key in the context
+ if (primarySupplier.containsKey(properties)) {
+ if (injectWithNulls)
+ return null;
+ Object value = primarySupplier.get(properties);
+ if (value == null)
+ return value;
+ if (parameterType.isAssignableFrom(value.getClass()))
+ return value;
+ }
+
+ // 3) can we ignore this argument?
+ if (ignoreMissing || properties.isOptional())
+ return null;
+
+ if (!optional) {
+ String msg = "Unable to find value for \"" + primarySupplier.getKey(properties) + "\"";
+ throw new IllegalArgumentException(msg);
+ }
+ return NOT_A_VALUE;
+ }
+
+ protected void logError(Object destination, Exception e) {
+ String msg = "Injection failed " + destination.toString();
+ logError(msg, e);
+ }
+
+ protected void logError(String msg, Exception e) {
+ System.out.println(msg); //$NON-NLS-1$
+ if (e != null)
+ e.printStackTrace();
+ // TBD convert this into real logging
+ // String msg = NLS.bind("Injection failed", destination.toString());
+ // RuntimeLog.log(new Status(IStatus.WARNING,
+ // IRuntimeConstants.PI_COMMON, 0, msg, e));
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionClass.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionClass.java
new file mode 100644
index 0000000..7a5da93
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionClass.java
@@ -0,0 +1,163 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.e4.core.services.internal.context;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Iterator;
+import org.eclipse.e4.core.services.IDisposable;
+import org.eclipse.e4.core.services.context.ContextChangeEvent;
+import org.eclipse.e4.core.services.context.IEclipseContext;
+import org.eclipse.e4.core.services.context.spi.ContextInjectionFactory;
+import org.eclipse.e4.core.services.context.spi.IContextConstants;
+import org.eclipse.e4.core.services.injector.IObjectProvider;
+
+public class InjectionClass extends InjectionAbstract {
+
+ final static private String JAVA_OBJECT = "java.lang.Object"; //$NON-NLS-1$
+
+ public InjectionClass(Object userObject, IObjectProvider primarySupplier) {
+ super(userObject, primarySupplier);
+ }
+
+ public boolean notify(ContextChangeEvent event) {
+ Object object = getObject();
+ if (object == null)
+ return false;
+ int eventType = event.getEventType();
+ if (eventType == ContextChangeEvent.DISPOSE) {
+ if (object instanceof IDisposable)
+ ((IDisposable) object).dispose();
+ processPreDestory(object, object.getClass(), new ArrayList(5));
+ return false;
+ }
+ if (eventType == ContextChangeEvent.INITIAL) {
+ processPostConstruct(object, object.getClass(), new ArrayList(5));
+ }
+ IObjectProvider context = event.getContext();
+ context.get(new InjectionProperties(true, "e4_valid_context", true, null)); // pseudo-dependency to create a link
+ return true;
+ }
+
+ private void processPostConstruct(Object userObject, Class objectClass, ArrayList classHierarchy) {
+ Class superClass = objectClass.getSuperclass();
+ if (superClass != null && !superClass.getName().equals(JAVA_OBJECT)) {
+ classHierarchy.add(objectClass);
+ processPostConstruct(userObject, superClass, classHierarchy);
+ classHierarchy.remove(objectClass);
+ }
+ Method[] methods = objectClass.getDeclaredMethods();
+ for (int i = 0; i < methods.length; i++) {
+ Method method = methods[i];
+ if (!isPostConstruct(method))
+ continue;
+ if (!isOverridden(method, classHierarchy)) {
+ InjectionMethod methodInvoke = new InjectionMethod(getObject(), primarySupplier,
+ method);
+ try {
+ methodInvoke.invoke(false, false);
+ } catch (InjectionException e) {
+ // TBD log
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ // TBD simplify this: only one non-annotation and one "implements IInitializable"?
+ /**
+ * Returns whether the given method is a post-construction process method, as defined by the
+ * class comment of {@link ContextInjectionFactory}.
+ */
+ private boolean isPostConstruct(Method method) {
+ boolean isPostConstruct = annotationSupport.isPostConstruct(method);
+ if (isPostConstruct)
+ return true;
+ if (!method.getName().equals(IContextConstants.INJECTION_SET_CONTEXT_METHOD))
+ return false;
+ Class[] parms = method.getParameterTypes();
+ if (parms.length == 0)
+ return true;
+ if (parms.length == 1 && parms[0].equals(IEclipseContext.class))
+ return true;
+ return false;
+ }
+
+ private void processPreDestory(Object userObject, Class objectClass, ArrayList classHierarchy) {
+ Class superClass = objectClass.getSuperclass();
+ if (superClass != null && !superClass.getName().equals(JAVA_OBJECT)) {
+ classHierarchy.add(objectClass);
+ processPreDestory(userObject, superClass, classHierarchy);
+ classHierarchy.remove(objectClass);
+ }
+ Method[] methods = objectClass.getDeclaredMethods();
+ for (int i = 0; i < methods.length; i++) {
+ Method method = methods[i];
+ if (method.getParameterTypes().length > 0) // TBD why?
+ continue;
+ if (!annotationSupport.isPreDestory(method))
+ continue;
+ if (!isOverridden(method, classHierarchy)) {
+ InjectionMethod methodInvoke = new InjectionMethod(getObject(), primarySupplier,
+ method);
+ try {
+ methodInvoke.invoke(false, false);
+ } catch (InjectionException e) {
+ // TBD log
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ // TBD move into the base class? For @PostConstruct?
+ /**
+ * Checks if a given method is overridden with an injectable method.
+ */
+ private boolean isOverridden(Method method, ArrayList classHierarchy) {
+ int modifiers = method.getModifiers();
+ if (Modifier.isPrivate(modifiers))
+ return false;
+ if (Modifier.isStatic(modifiers))
+ return false;
+ // method is not private if we reached this line, check not(public OR protected)
+ boolean isDefault = !(Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers));
+
+ for (Iterator i = classHierarchy.iterator(); i.hasNext();) {
+ Class subClass = (Class) i.next();
+ Method override = null;
+ try {
+ override = subClass.getDeclaredMethod(method.getName(), method.getParameterTypes());
+ } catch (SecurityException e) {
+ continue;
+ } catch (NoSuchMethodException e) {
+ continue; // this is the desired outcome
+ }
+ if (override != null) {
+ if (isDefault) { // must be in the same package to override
+ Package originalPackage = method.getDeclaringClass().getPackage();
+ Package overridePackage = subClass.getPackage();
+
+ if (originalPackage == null && overridePackage == null)
+ return true;
+ if (originalPackage == null || overridePackage == null)
+ return false;
+ if (originalPackage.equals(overridePackage))
+ return true;
+ } else
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionConstructor.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionConstructor.java
new file mode 100644
index 0000000..45780e0
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionConstructor.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.e4.core.services.internal.context;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import org.eclipse.e4.core.services.context.ContextChangeEvent;
+import org.eclipse.e4.core.services.injector.IObjectProvider;
+
+/**
+ * Collection of static methods that deal with reflection-based injection at a low level.
+ */
+public class InjectionConstructor extends InjectionAbstract {
+
+ private final Constructor constructor;
+
+ public InjectionConstructor(Object userObject, IObjectProvider primarySupplier,
+ Constructor constructor) {
+ super(userObject, primarySupplier);
+ this.constructor = constructor;
+ }
+
+ public boolean notify(ContextChangeEvent event) {
+ if (event.getEventType() == ContextChangeEvent.INITIAL)
+ make();
+ return false; // constructor injection is static by nature
+ }
+
+ public Object make() {
+ Object[] actualParams = processParams();
+ if (actualParams == null)
+ return null;
+ try {
+ return callConstructor(constructor, actualParams);
+ } catch (InvocationTargetException e) {
+ String msg = "Could not invoke " + constructor.getName();
+ logError(msg, e);
+ return null;
+ } catch (InstantiationException e) {
+ String msg = "Could not instantiate " + constructor.getName();
+ logError(msg, e);
+ return null;
+ }
+ }
+
+ private Object[] processParams() {
+ Class[] parameterTypes = constructor.getParameterTypes();
+ InjectionProperties[] properties = annotationSupport.getInjectParamsProperties(constructor);
+ Object[] actualParams = new Object[properties.length];
+ for (int i = 0; i < actualParams.length; i++) {
+ try {
+ actualParams[i] = getValue(properties[i], parameterTypes[i], false, false);
+ } catch (IllegalArgumentException e) {
+ String msg = "Unable to find matching arguments for " + constructor.getName();
+ logError(msg, e);
+ return null;
+ }
+ }
+ return actualParams;
+ }
+
+ private Object callConstructor(Constructor constructor, Object[] args)
+ throws InvocationTargetException, InstantiationException {
+ if (args != null) { // make sure args are assignable
+ Class[] parameterTypes = constructor.getParameterTypes();
+ if (parameterTypes.length != args.length) {
+ // internal error, log it
+ logError(constructor, new IllegalArgumentException());
+ return null;
+ }
+ for (int i = 0; i < args.length; i++) {
+ if ((args[i] != null) && !parameterTypes[i].isAssignableFrom(args[i].getClass()))
+ return null;
+ }
+ }
+
+ Object result = null;
+ boolean wasAccessible = true;
+ if (!constructor.isAccessible()) {
+ constructor.setAccessible(true);
+ wasAccessible = false;
+ }
+ try {
+ result = constructor.newInstance(args);
+ } catch (IllegalArgumentException e) {
+ // should not happen, is checked at the start of this method
+ logError(constructor, e);
+ return null;
+ } catch (IllegalAccessException e) {
+ // should not happen as we set constructor to be accessible
+ logError(constructor, e);
+ return null;
+ } finally {
+ if (!wasAccessible)
+ constructor.setAccessible(false);
+ }
+ return result;
+ }
+}
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionException.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionException.java
new file mode 100644
index 0000000..cb30fef
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionException.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.e4.core.services.internal.context;
+
+public class InjectionException extends Exception {
+
+ private static final long serialVersionUID = 3098545573510654907L;
+
+ public InjectionException() {
+ super();
+ }
+
+ public InjectionException(Throwable e) {
+ super(e);
+ }
+
+ public InjectionException(String msg) {
+ super(msg);
+ }
+
+ public InjectionException(String msg, Throwable e) {
+ super(msg, e);
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionField.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionField.java
new file mode 100644
index 0000000..a2339fa
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionField.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.e4.core.services.internal.context;
+
+import java.lang.reflect.Field;
+import org.eclipse.e4.core.services.context.ContextChangeEvent;
+import org.eclipse.e4.core.services.injector.IObjectProvider;
+
+/**
+ * Collection of static methods that deal with reflection-based injection at a low level.
+ */
+public class InjectionField extends InjectionAbstract {
+
+ private final Field field;
+
+ public InjectionField(Object userObject, IObjectProvider primarySupplier, Field field) {
+ super(userObject, primarySupplier);
+ this.field = field;
+ InjectionProperties fieldProps = annotationSupport.getInjectProperties(field);
+ optional = fieldProps.isOptional();
+ }
+
+ public boolean notify(ContextChangeEvent event) {
+ Object userObject = getObject();
+ if (userObject == null)
+ return false;
+ // set variables based on the event type
+ int eventType = event.getEventType();
+ IObjectProvider changed = event.getContext();
+
+ boolean ignoreMissing = ignoreMissing(eventType, changed);
+ boolean injectWithNulls = injectNulls(eventType, changed);
+
+ InjectionProperties properties = annotationSupport.getInjectProperties(field);
+ Object value;
+ try {
+ value = getValue(properties, field.getType(), ignoreMissing, injectWithNulls);
+ } catch (IllegalArgumentException e) {
+ String msg = "Could not set " + field.getName();
+ logError(msg, e);
+ return true; // keep trying?
+ }
+ setField(value);
+ return true;
+ }
+
+ private boolean setField(Object value) {
+ Object userObject = getObject();
+ boolean wasAccessible = true;
+ if (!field.isAccessible()) {
+ field.setAccessible(true);
+ wasAccessible = false;
+ }
+ try {
+ field.set(userObject, value);
+ } catch (IllegalArgumentException e) {
+ logError(field, e);
+ return false;
+ } catch (IllegalAccessException e) {
+ logError(field, e);
+ return false;
+ } finally {
+ if (!wasAccessible)
+ field.setAccessible(false);
+ }
+ return true;
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionMethod.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionMethod.java
new file mode 100644
index 0000000..138b5ee
--- /dev/null
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/InjectionMethod.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.e4.core.services.internal.context;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import org.eclipse.e4.core.services.context.ContextChangeEvent;
+import org.eclipse.e4.core.services.injector.IObjectProvider;
+
+/**
+ * Collection of static methods that deal with reflection-based injection at a low level.
+ */
+public class InjectionMethod extends InjectionAbstract {
+
+ private final Method method;
+
+ public InjectionMethod(Object userObject, IObjectProvider primarySupplier, Method method) {
+ super(userObject, primarySupplier);
+ this.method = method;
+ InjectionProperties methodProps = annotationSupport.getInjectProperties(method);
+ optional = methodProps.isOptional();
+ }
+
+ public boolean notify(ContextChangeEvent event) {
+ Object userObject = getObject();
+ if (userObject == null)
+ return false;
+ int eventType = event.getEventType();
+ IObjectProvider changed = event.getContext();
+ boolean ignoreMissing = ignoreMissing(eventType, changed);
+ boolean injectWithNulls = injectNulls(eventType, changed);
+ try {
+ invoke(ignoreMissing, injectWithNulls);
+ } catch (InjectionException e) {
+ logError(method, e);
+ return false;
+ }
+ return true;
+ }
+
+ public Object invoke(boolean ignoreMissing, boolean injectWithNulls) throws InjectionException {
+ Object[] actualParams = processParams(ignoreMissing, injectWithNulls);
+ if (actualParams == null) {
+ if (!optional) {
+ String msg = "Unable to find matching argument to call method \""
+ + method.getName() + "\"";
+ throw new InjectionException(msg);
+ }
+ return null;
+ }
+ try {
+ return callMethod(actualParams);
+ } catch (InvocationTargetException e) {
+ String msg = "Unexpected error invoking method \"" + method.getName() + "\"";
+ throw new InjectionException(msg, e);
+ }
+ }
+
+ private Object[] processParams(boolean ignoreMissing, boolean injectWithNulls) {
+ Class[] parameterTypes = method.getParameterTypes();
+ InjectionProperties[] properties = annotationSupport.getInjectParamProperties(method);
+ Object[] actualParams = new Object[properties.length];
+ for (int i = 0; i < actualParams.length; i++) {
+ try {
+ Object actualValue = getValue(properties[i], parameterTypes[i], ignoreMissing,
+ injectWithNulls);
+ if (actualValue == NOT_A_VALUE)
+ return null;
+ actualParams[i] = actualValue;
+ } catch (IllegalArgumentException e) {
+ String msg = "Could not invoke " + method.getName();
+ logError(msg, e);
+ return null;
+ }
+ }
+ return actualParams;
+ }
+
+ private Object callMethod(Object[] args) throws InvocationTargetException {
+ Object userObject = getObject();
+ Object result = null;
+ boolean wasAccessible = true;
+ if (!method.isAccessible()) {
+ method.setAccessible(true);
+ wasAccessible = false;
+ }
+ try {
+ result = method.invoke(userObject, args);
+ } catch (IllegalArgumentException e) {
+ // should not happen, is checked during formation of the array of actual arguments
+ logError(method, e);
+ return null;
+ } catch (IllegalAccessException e) {
+ // should not happen, is checked at the start of this method
+ logError(method, e);
+ return null;
+ } finally {
+ if (!wasAccessible)
+ method.setAccessible(false);
+ }
+ return result;
+ }
+}
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ObjectProviderContext.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ObjectProviderContext.java
index 0bc6164..6f94a91 100644
--- a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ObjectProviderContext.java
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ObjectProviderContext.java
@@ -10,15 +10,13 @@
*******************************************************************************/
package org.eclipse.e4.core.services.internal.context;
-import org.eclipse.e4.core.services.context.ContextChangeEvent;
import org.eclipse.e4.core.services.context.IEclipseContext;
import org.eclipse.e4.core.services.context.IRunAndTrack;
-import org.eclipse.e4.core.services.context.spi.IContextConstants;
import org.eclipse.e4.core.services.injector.IObjectDescriptor;
import org.eclipse.e4.core.services.injector.IObjectProvider;
import org.eclipse.e4.core.services.injector.Injector;
-public class ObjectProviderContext implements IObjectProvider, IRunAndTrack {
+public class ObjectProviderContext implements IObjectProvider {
final static private String ECLIPSE_CONTEXT_NAME = IEclipseContext.class.getName();
@@ -69,52 +67,11 @@
return "ContextToInjectorLink(" + context + ')'; //$NON-NLS-1$
}
- // /////////////////////////////////////////////////////////////////////////////////
- // Context events
-
- public boolean notify(ContextChangeEvent event) {
- switch (event.getEventType()) {
- case ContextChangeEvent.INITIAL:
- injector.inject(event.getArguments()[0]);
- break;
- case ContextChangeEvent.UNINJECTED:
- injector.uninject(event.getArguments()[0]);
- break;
- case ContextChangeEvent.DISPOSE:
- if (event.getContext() == context)
- injector.dispose();
- else
- injector.reinject();
- break;
- case ContextChangeEvent.ADDED: {
- String name = event.getName();
- if (IContextConstants.PARENT.equals(name))
- handleParentChange(event);
- else
- injector.added(new InjectionProperties(true, name, false, null));
- break;
- }
- case ContextChangeEvent.REMOVED: {
- String name = event.getName();
- if (IContextConstants.PARENT.equals(name))
- handleParentChange(event);
- else
- injector.removed(new InjectionProperties(true, name, false, null));
- break;
- }
- }
- return true; // only dispose of the injector when the context has been disposed
+ public IEclipseContext getContext() {
+ return context;
}
- private void handleParentChange(final ContextChangeEvent event) {
- IEclipseContext eventContext = (IEclipseContext) event.getContext();
- IEclipseContext oldParent = (IEclipseContext) event.getOldValue();
- IEclipseContext newParent = (IEclipseContext) eventContext.get(IContextConstants.PARENT);
- if (oldParent == newParent)
- return;
- injector.reparent(getObjectProvider(oldParent));
- }
-
+ // TBD remove?
static public ObjectProviderContext getObjectProvider(IEclipseContext context) {
String key = ObjectProviderContext.class.getName();
if (context.containsKey(key, true))
@@ -126,4 +83,25 @@
return objectProvider;
}
+ public void init(Object userObject) {
+ injector.inject(userObject);
+ }
+
+ public void runAndTrack(final IRunAndTrack runnable, Object[] args) {
+ context.runAndTrack(runnable, args);
+ }
+
+ // TBD remove?
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+ if (!(obj instanceof ObjectProviderContext))
+ return false;
+ return context.equals(((ObjectProviderContext) obj).context);
+ }
+
+ // TBD remove?
+ public int hashCode() {
+ return context.hashCode();
+ }
}
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/TestHelper.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/TestHelper.java
index 9896bb5..cd0aa3c 100644
--- a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/TestHelper.java
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/TestHelper.java
@@ -10,7 +10,7 @@
*******************************************************************************/
package org.eclipse.e4.core.services.internal.context;
-import java.util.Set;
+import java.util.List;
import org.eclipse.e4.core.services.context.IEclipseContext;
/**
@@ -24,7 +24,7 @@
// don't allow instantiation
}
- public static Set getListeners(IEclipseContext context) {
+ public static List getListeners(IEclipseContext context) {
return ((EclipseContext) context).listeners;
}
}
diff --git a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ValueComputation.java b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ValueComputation.java
index cbb4f8f..634fbe5 100644
--- a/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ValueComputation.java
+++ b/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/services/internal/context/ValueComputation.java
@@ -14,6 +14,7 @@
import org.eclipse.e4.core.services.context.ContextChangeEvent;
import org.eclipse.e4.core.services.context.IContextFunction;
import org.eclipse.e4.core.services.context.IEclipseContext;
+import org.eclipse.e4.core.services.injector.IObjectProvider;
public class ValueComputation extends Computation {
Object cachedValue;
@@ -110,10 +111,13 @@
final protected void doHandleInvalid(ContextChangeEvent event, List scheduled) {
int eventType = event.getEventType();
// if the originating context is being disposed, remove this value computation completely
- if (eventType == ContextChangeEvent.DISPOSE
- && originatingContext.equals(event.getContext())) {
- removeAll();
- return;
+ if (eventType == ContextChangeEvent.DISPOSE) {
+ IObjectProvider provider = event.getContext();
+ IEclipseContext eventsContext = ((ObjectProviderContext) provider).getContext();
+ if (originatingContext.equals(eventsContext)) {
+ removeAll();
+ return;
+ }
}
this.originatingContext.invalidate(this.name,
eventType == ContextChangeEvent.DISPOSE ? ContextChangeEvent.REMOVED : eventType,
diff --git a/tests/org.eclipse.e4.core.tests.services/src/org/eclipse/e4/core/services/internal/context/RunAndTrackTest.java b/tests/org.eclipse.e4.core.tests.services/src/org/eclipse/e4/core/services/internal/context/RunAndTrackTest.java
index 721e5d5..d923a2f 100644
--- a/tests/org.eclipse.e4.core.tests.services/src/org/eclipse/e4/core/services/internal/context/RunAndTrackTest.java
+++ b/tests/org.eclipse.e4.core.tests.services/src/org/eclipse/e4/core/services/internal/context/RunAndTrackTest.java
@@ -22,6 +22,7 @@
import org.eclipse.e4.core.services.context.IRunAndTrack;
import org.eclipse.e4.core.services.context.spi.ContextFunction;
import org.eclipse.e4.core.services.context.spi.IContextConstants;
+import org.eclipse.e4.core.services.injector.IObjectProvider;
import org.eclipse.e4.core.tests.services.TestActivator;
/**
@@ -131,7 +132,9 @@
public boolean notify(ContextChangeEvent event) {
oldValue = event.getOldValue();
- newValue = event.getContext().get(NAME);
+ IObjectProvider provider = event.getContext();
+ IEclipseContext eventsContext = ((ObjectProviderContext) provider).getContext();
+ newValue = eventsContext.get(NAME);
return true;
}
}
@@ -176,7 +179,9 @@
public boolean notify(ContextChangeEvent event) {
// must get a value so we aren't removed
eventType = event.getEventType();
- event.getContext().get(NAME);
+ IObjectProvider provider = event.getContext();
+ IEclipseContext eventsContext = ((ObjectProviderContext) provider).getContext();
+ eventsContext.get(NAME);
return true;
}
}