| /** |
| * Copyright (C) 2011 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.google.inject.internal; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.inject.Binding; |
| import com.google.inject.ProvisionException; |
| import com.google.inject.spi.DependencyAndSource; |
| import com.google.inject.spi.ProvisionListener; |
| |
| import java.util.List; |
| |
| /** |
| * Intercepts provisions with a stack of listeners. |
| * |
| * @author sameb@google.com (Sam Berlin) |
| */ |
| final class ProvisionListenerStackCallback<T> { |
| |
| private static final ProvisionListener EMPTY_LISTENER[] = new ProvisionListener[0]; |
| @SuppressWarnings("rawtypes") |
| private static final ProvisionListenerStackCallback<?> EMPTY_CALLBACK = |
| new ProvisionListenerStackCallback(null /* unused, so ok */, 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 { |
| this.listeners = listeners.toArray(new ProvisionListener[listeners.size()]); |
| } |
| } |
| |
| public boolean hasListeners() { |
| return listeners.length > 0; |
| } |
| |
| public T provision(Errors errors, InternalContext context, ProvisionCallback<T> callable) |
| throws ErrorsException { |
| Provision provision = new Provision(errors, 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 errors |
| .errorInUserCode(caught, "Error notifying ProvisionListener %s of %s.%n" |
| + " Reason: %s", listener, binding.getKey(), caught) |
| .toException(); |
| } else { |
| return provision.result; |
| } |
| } |
| |
| // TODO(sameb): Can this be more InternalFactory-like? |
| public interface ProvisionCallback<T> { |
| public T call() throws ErrorsException; |
| } |
| |
| private class Provision extends ProvisionListener.ProvisionInvocation<T> { |
| |
| final Errors errors; |
| final InternalContext context; |
| final ProvisionCallback<T> callable; |
| int index = -1; |
| T result; |
| ErrorsException exceptionDuringProvision; |
| ProvisionListener erredListener; |
| |
| public Provision(Errors errors, InternalContext context, ProvisionCallback<T> callable) { |
| this.callable = callable; |
| this.context = context; |
| this.errors = errors; |
| } |
| |
| @Override |
| public T provision() { |
| index++; |
| if (index == listeners.length) { |
| try { |
| result = callable.call(); |
| } catch(ErrorsException ee) { |
| exceptionDuringProvision = ee; |
| throw new ProvisionException(errors.merge(ee.getErrors()).getMessages()); |
| } |
| } else if (index < listeners.length) { |
| int currentIdx = index; |
| try { |
| |
| listeners[index].onProvision(this); |
| } catch(RuntimeException re) { |
| erredListener = listeners[currentIdx]; |
| throw re; |
| } |
| if (currentIdx == index) { |
| // Our listener didn't provision -- do it for them. |
| provision(); |
| } |
| } else { |
| throw new IllegalStateException("Already provisioned in this listener."); |
| } |
| return result; |
| } |
| |
| @Override |
| public Binding<T> getBinding() { |
| // TODO(sameb): Because so many places cast directly to BindingImpl & subclasses, |
| // we can't decorate this to prevent calling getProvider().get(), which means |
| // if someone calls that they'll get strange errors. |
| return binding; |
| } |
| |
| @Override |
| public List<DependencyAndSource> getDependencyChain() { |
| return context.getDependencyChain(); |
| } |
| } |
| } |