001package io.avaje.inject.spi;
002
003import org.mockito.Mockito;
004
005import javax.inject.Named;
006
007import java.lang.reflect.Type;
008import java.util.function.Consumer;
009
010/**
011 * Holds beans supplied to the dependency injection.
012 * <p>
013 * These can be externally supplied dependencies or test doubles for testing purposes.
014 */
015public abstract class SuppliedBean<B> {
016
017  protected final String name;
018  protected final Type type;
019  protected B bean;
020
021  /**
022   * Create with a class type and bean instance.
023   */
024  @SuppressWarnings({"rawtypes", "unchecked"})
025  public static SuppliedBean of(Class<?> type, Object bean) {
026    return new SuppliedBean.ForClass(null, type, bean, null);
027  }
028
029  /**
030   * Create for a class type with a consumer that runs once when the bean is obtained.
031   */
032  public static <B> SuppliedBean<B> of(String name, Class<B> type, B bean, Consumer<B> consumer) {
033    return new SuppliedBean.ForClass<>(name, type, bean, consumer);
034  }
035
036  /**
037   * Create for a class type with name.
038   */
039  public static <B> SuppliedBean<B> of(String name, Class<B> type, B bean) {
040    return new SuppliedBean.ForClass<>(name, type, bean, null);
041  }
042
043  /**
044   * Create a supplied bean for a generic type.
045   */
046  public static <B> SuppliedBean<B> ofType(String name, Type type, B bean) {
047    return new SuppliedBean.ForType<>(name, type, bean);
048  }
049
050  private SuppliedBean(String name, Type type, B bean) {
051    this.name = name;
052    this.type = type;
053    this.bean = bean;
054  }
055
056  private SuppliedBean(String name, Class<B> type, B bean) {
057    this.name = initName(name, type);
058    this.type = type;
059    this.bean = bean;
060  }
061
062
063  String initName(String name, Class<?> type) {
064    if (name != null) {
065      return name;
066    }
067    Named annotation = type.getAnnotation(Named.class);
068    return (annotation == null) ? null : annotation.value();
069  }
070
071  /**
072   * Return the dependency injection target type.
073   */
074  public Type type() {
075    return type;
076  }
077
078  /**
079   * Return the qualifier name of the supplied bean.
080   */
081  public String name() {
082    return name;
083  }
084
085  /**
086   * Return the bean instance to use for injection.
087   */
088  public abstract B bean();
089
090  /**
091   * Return the interfaces to additionally register along with the type.
092   */
093  public abstract Class<?>[] interfaces();
094
095  /**
096   * Type based supplied bean.
097   */
098  private static class ForType<B> extends SuppliedBean<B> {
099
100    private ForType(String name, Type type, B bean) {
101      super(name, type, bean);
102    }
103
104    @Override
105    public B bean() {
106      return bean;
107    }
108
109    @Override
110    public Class<?>[] interfaces() {
111      return new Class[0];
112    }
113  }
114
115  /**
116   * Class based supplied bean.
117   */
118  private static class ForClass<B> extends SuppliedBean<B> {
119
120    private final Consumer<B> consumer;
121    private final Class<B> classType;
122
123    ForClass(String name, Class<B> type, B bean, Consumer<B> consumer) {
124      super(name, type, bean);
125      this.classType = type;
126      this.consumer = consumer;
127    }
128
129    @Override
130    public B bean() {
131      if (bean == null) {
132        // should extract a SPI for this
133        bean = Mockito.mock(classType);
134      }
135      if (consumer != null) {
136        consumer.accept(bean);
137      }
138      return bean;
139    }
140
141    @Override
142    public Class<?>[] interfaces() {
143      return classType.getInterfaces();
144    }
145  }
146}