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