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}