package tc.oc.pgm.module; import java.util.Optional; import javax.annotation.Nullable; import com.google.common.reflect.TypeParameter; import com.google.inject.Key; import com.google.inject.TypeLiteral; import tc.oc.commons.core.reflect.ResolvableType; /** * Contains the result of provisioning a module, and the module instance * itself, if it was provided. * * This exists in order to distinguish between absent modules and modules * that failed due to a user error. Knowing the difference allows us to * avoid chain reactions that can spew tons of superfluous errors. * * @see UpstreamProvisionFailure */ public class ProvisionWrapper<T> { static <T> Key<ProvisionWrapper<T>> keyOf(TypeLiteral<T> type) { return Key.get(new ResolvableType<ProvisionWrapper<T>>(){}.where(new TypeParameter<T>(){}, type)); } final TypeLiteral<T> type; final ProvisionResult result; final @Nullable T instance; public ProvisionWrapper(TypeLiteral<T> type, ProvisionResult result) { this(type, result, null); } public ProvisionWrapper(TypeLiteral<T> type, ProvisionResult result, @Nullable T instance) { this.type = type; this.result = result; this.instance = instance; } public T require(@Nullable TypeLiteral<?> dependee) { switch(result) { case PRESENT: return instance; case FAILED: throw new UpstreamProvisionFailure(); } // Don't throw a ProvisionException because it doesn't give you a stack trace if(dependee == null) { throw new IllegalStateException("Missing required module " + type); } else { throw new IllegalStateException("Missing module " + type + " which is required by " + dependee); } } public Optional<T> optional() { switch(result) { case PRESENT: return Optional.of(instance); case FAILED: throw new UpstreamProvisionFailure(); } return Optional.empty(); } }