package com.google.inject.internal;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.google.inject.Binding;
import com.google.inject.spi.ProvisionListener;
import java.util.List;
import java.util.Set;
final class ProvisionListenerStackCallback<T> {
private static final ProvisionListener[] EMPTY_LISTENER = new ProvisionListener[0];
@SuppressWarnings({"rawtypes", "unchecked"})
private static final ProvisionListenerStackCallback<?> EMPTY_CALLBACK =
new ProvisionListenerStackCallback(null , ImmutableList.of());
private final ProvisionListener[] listeners;
private final Binding<T> binding;
@SuppressWarnings("unchecked")
public static <T> ProvisionListenerStackCallback<T> emptyListener() {
return (ProvisionListenerStackCallback<T>) EMPTY_CALLBACK;
}
public ProvisionListenerStackCallback(Binding<T> binding, List<ProvisionListener> listeners) {
this.binding = binding;
if (listeners.isEmpty()) {
this.listeners = EMPTY_LISTENER;
} else {
Set<ProvisionListener> deDuplicated = Sets.newLinkedHashSet(listeners);
this.listeners = deDuplicated.toArray(new ProvisionListener[deDuplicated.size()]);
}
}
public boolean hasListeners() {
return listeners.length > 0;
}
public T provision(InternalContext context, ProvisionCallback<T> callable)
throws InternalProvisionException {
Provision provision = new Provision(context, callable);
RuntimeException caught = null;
try {
provision.provision();
} catch (RuntimeException t) {
caught = t;
}
if (provision.exceptionDuringProvision != null) {
throw provision.exceptionDuringProvision;
} else if (caught != null) {
Object listener =
provision.erredListener != null ? provision.erredListener.getClass() : "(unknown)";
throw InternalProvisionException.errorInUserCode(
caught,
"Error notifying ProvisionListener %s of %s.%n Reason: %s",
listener,
binding.getKey(),
caught);
} else {
return provision.result;
}
}
public interface ProvisionCallback<T> {
public T call() throws InternalProvisionException;
}
private class Provision extends ProvisionListener.ProvisionInvocation<T> {
final InternalContext context;
final ProvisionCallback<T> callable;
int index = -1;
T result;
InternalProvisionException exceptionDuringProvision;
ProvisionListener erredListener;
public Provision(InternalContext context, ProvisionCallback<T> callable) {
this.callable = callable;
this.context = context;
}
@Override
public T provision() {
index++;
if (index == listeners.length) {
try {
result = callable.call();
} catch (InternalProvisionException ipe) {
exceptionDuringProvision = ipe;
throw ipe.toProvisionException();
}
} else if (index < listeners.length) {
int currentIdx = index;
try {
listeners[index].onProvision(this);
} catch (RuntimeException re) {
erredListener = listeners[currentIdx];
throw re;
}
if (currentIdx == index) {
provision();
}
} else {
throw new IllegalStateException("Already provisioned in this listener.");
}
return result;
}
@Override
public Binding<T> getBinding() {
return binding;
}
@Deprecated
@Override
public List<com.google.inject.spi.DependencyAndSource> getDependencyChain() {
return context.getDependencyChain();
}
}
}