blob: bd4b0e9e8d0403e5080a97ff01bd8a861773444f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2015 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.e4.core.internal.contexts;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Objects;
import java.util.Stack;
import javax.inject.Named;
import org.eclipse.e4.core.contexts.Active;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.contexts.RunAndTrack;
import org.eclipse.e4.core.di.IInjector;
import org.eclipse.e4.core.di.suppliers.IObjectDescriptor;
import org.eclipse.e4.core.di.suppliers.IRequestor;
import org.eclipse.e4.core.di.suppliers.PrimaryObjectSupplier;
import org.eclipse.e4.core.internal.di.Requestor;
public class ContextObjectSupplier extends PrimaryObjectSupplier {
final static protected String ECLIPSE_CONTEXT_NAME = IEclipseContext.class.getName();
public static class ContextInjectionListener extends RunAndTrackExt {
final private Object[] result;
final private String[] keys;
final private boolean[] active;
final private IRequestor requestor;
final private IEclipseContext context;
public ContextInjectionListener(IEclipseContext context, Object[] result, String[] keys, boolean[] active, IRequestor requestor, boolean group) {
super(group);
this.result = result;
this.keys = keys;
this.active = active;
this.requestor = requestor;
this.context = context;
}
@Override
public Reference<Object> getReference() {
if (requestor instanceof Requestor)
return ((Requestor<?>) requestor).getReference();
return super.getReference();
}
@Override
public boolean update(IEclipseContext eventsContext, int eventType, Object[] extraArguments) {
if (eventType == ContextChangeEvent.INITIAL) {
// needs to be done inside runnable to establish dependencies
for (int i = 0; i < keys.length; i++) {
if (keys[i] == null)
continue;
IEclipseContext targetContext = (active[i]) ? context.getActiveLeaf() : context;
if (ECLIPSE_CONTEXT_NAME.equals(keys[i])) {
result[i] = targetContext;
IEclipseContext parent = targetContext.getParent(); // creates pseudo-link
if (parent == null)
targetContext.get(ECLIPSE_CONTEXT_NAME); // pseudo-link in case there is no parent
} else if (targetContext.containsKey(keys[i]))
result[i] = targetContext.get(keys[i]);
}
return true;
}
switch (eventType) {
case ContextChangeEvent.DISPOSE:
if (eventsContext == context) {
ContextObjectSupplier originatingSupplier = eventsContext.getLocal(ContextObjectSupplier.class);
requestor.disposed(originatingSupplier);
return false;
} break;
case ContextChangeEvent.UNINJECTED:
if (eventsContext == context) {
ContextObjectSupplier originatingSupplier = eventsContext.getLocal(ContextObjectSupplier.class);
return requestor.uninject(extraArguments[0], originatingSupplier);
} break;
default:
if (!requestor.isValid())
return false; // remove this listener
requestor.resolveArguments(false);
requestor.execute();
break;
}
return true;
}
@Override
public boolean changed(IEclipseContext eventsContext) {
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int hashRresult = 1;
hashRresult = prime * hashRresult + Objects.hashCode(context);
return prime * hashRresult + Objects.hashCode(requestor);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ContextInjectionListener other = (ContextInjectionListener) obj;
return Objects.equals(this.context, other.context) && Objects.equals(this.requestor, other.requestor);
}
@Override
public String toString() {
return requestor.toString();
}
}
final private IEclipseContext context;
public ContextObjectSupplier(IEclipseContext context, IInjector injector) {
this.context = context;
}
public IEclipseContext getContext() {
return context;
}
@Override
public void get(IObjectDescriptor[] descriptors, Object[] actualArgs, final IRequestor requestor, boolean initial, boolean track, boolean group) {
final String[] keys = new String[descriptors.length];
final boolean[] active = new boolean[descriptors.length];
for (int i = 0; i < descriptors.length; i++) {
String key = getKey(descriptors[i]);
if ((actualArgs[i] == IInjector.NOT_A_VALUE))
keys[i] = key;
else if (ECLIPSE_CONTEXT_NAME.equals(key)) // allow provider to override IEclipseContext
keys[i] = ECLIPSE_CONTEXT_NAME;
else
keys[i] = null;
if (descriptors[i] == null)
active[i] = false;
else
active[i] = (descriptors[i].hasQualifier(Active.class));
}
if (requestor != null && track) { // only track if requested
if (initial) {
RunAndTrack trackable = new ContextInjectionListener(context, actualArgs, keys, active, requestor, group);
context.runAndTrack(trackable);
} else { // we do track if this is done inside a computation, but don't create another runnable
fillArgs(actualArgs, keys, active);
}
} else if (descriptors.length > 0) {
pauseRecording();
try {
fillArgs(actualArgs, keys, active);
} finally {
resumeRecording();
}
}
}
private void fillArgs(Object[] actualArgs, String[] keys, boolean[] active) {
for (int i = 0; i < keys.length; i++) {
if (keys[i] == null)
continue;
IEclipseContext targetContext = (active[i]) ? context.getActiveLeaf() : context;
if (ECLIPSE_CONTEXT_NAME.equals(keys[i]))
actualArgs[i] = targetContext;
else if (targetContext.containsKey(keys[i]))
actualArgs[i] = targetContext.get(keys[i]);
}
}
private String getKey(IObjectDescriptor descriptor) {
if (descriptor.hasQualifier(Named.class)) {
Named namedAnnotation = descriptor.getQualifier(Named.class);
return namedAnnotation.value();
}
Type elementType = descriptor.getDesiredType();
return typeToString(elementType);
}
private String typeToString(Type type) {
if (type == null)
return null;
if (type instanceof Class<?>)
return ((Class<?>) type).getName();
if (type instanceof ParameterizedType) {
Type rawType = ((ParameterizedType) type).getRawType();
return typeToString(rawType);
}
return null;
}
@Override
synchronized public void pauseRecording() {
Stack<Computation> current = EclipseContext.getCalculatedComputations();
current.push(null);
}
@Override
synchronized public void resumeRecording() {
Stack<Computation> current = EclipseContext.getCalculatedComputations();
Computation plug = current.pop();
if (plug != null)
throw new IllegalArgumentException("Internal error in nested computation processing"); //$NON-NLS-1$
}
static public ContextObjectSupplier getObjectSupplier(IEclipseContext context, IInjector injector) {
if (context == null)
return null;
// don't track this dependency if we are called in RaT
ContextObjectSupplier supplier = (ContextObjectSupplier) ((EclipseContext) context).internalGet((EclipseContext) context, ContextObjectSupplier.class.getName(), true);
if (supplier != null)
return supplier;
ContextObjectSupplier objectSupplier = new ContextObjectSupplier(context, injector);
context.set(ContextObjectSupplier.class, objectSupplier);
return objectSupplier;
}
@Override
public WeakReference<Object> makeReference(Object object) {
if (context instanceof EclipseContext) {
return ((EclipseContext) context).trackedWeakReference(object);
}
return super.makeReference(object);
}
}