Skip to content

Instantly share code, notes, and snippets.

@Garciat
Last active April 19, 2021 20:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Garciat/f747fcb5d384ccdabb3ae49107db7fe2 to your computer and use it in GitHub Desktop.
Save Garciat/f747fcb5d384ccdabb3ae49107db7fe2 to your computer and use it in GitHub Desktop.
package example.playground;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import lombok.With;
import java.lang.reflect.Constructor;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
public final class MicroWire {
public static final String NO_NAME = "";
interface IThingA {
}
@Value
public static class ThingA implements IThingA {
}
interface IThingB {
}
@Value
public static class ThingB implements IThingB {
IThingA a;
}
@Value
public static class ThingC {
IThingB b;
}
public static void main(String[] args) {
Container container = builder()
.with(singleton(new ThingA()))
.with(constructing(ThingB.class).lowering(IThingB.class))
.with(constructing(ThingC.class).named("hello"))
.build();
System.out.println(container.get(ThingC.class));
System.out.println(container.get(ThingC.class) == container.get(ThingC.class));
}
public static Builder builder() {
return new TheBuilder();
}
public static <T> Binder<T> constructing(Class<T> type) {
return new Binder<>(
new Descriptor<>(NO_NAME, type),
new CachingProvider<>(new ConstructorProvider<>(type)));
}
public static <T> Binder<T> singleton(T object) {
@SuppressWarnings("unchecked")
Class<T> cls = (Class<T>) object.getClass();
return new Binder<>(
new Descriptor<>(NO_NAME, cls),
new InstanceProvider<>(object));
}
public interface Builder {
Builder with(Binder<?> binder);
Container build();
}
@Value
public static class Descriptor<T> {
@With String name;
@With Class<T> type;
public Provider<? extends T> cast(Provider<?> provider) {
if (!type.isAssignableFrom(provider.principalType())) {
throw new ClassCastException("bad bind");
}
@SuppressWarnings("unchecked")
Provider<? extends T> cast = (Provider<? extends T>) provider;
return cast;
}
}
public interface Provider<T> {
Class<? extends T> principalType();
T resolve(Container container);
}
@Value
public static class Binder<T> {
Descriptor<T> descriptor;
Provider<? extends T> provider;
public Binder<T> named(String name) {
return new Binder<>(descriptor.withName(name), provider);
}
public Binder<? super T> lowering(Class<? super T> cls) {
return lower(this, cls);
}
public static <T extends U, U> Binder<U> lower(Binder<T> binder, Class<U> cls) {
return new Binder<>(
new Descriptor<>(binder.getDescriptor().getName(), cls),
binder.getProvider());
}
}
public interface Container {
<T> T get(Descriptor<T> descriptor);
default <T> T get(Class<T> type) {
return get(type, NO_NAME);
}
default <T> T get(Class<T> type, String name) {
return get(new Descriptor<>(name, type));
}
default Object[] bindAll(Parameter[] parameters) {
return Arrays.stream(parameters)
.map(p -> get(p.getType(), p.getName()))
.toArray(Object[]::new);
}
}
public interface Generalizer {
<T> Set<Descriptor<? super T>> generalize(Descriptor<T> descriptor);
default Generalizer andThen(Generalizer next) {
return new Generalizer() {
@Override
public <T> Set<Descriptor<? super T>> generalize(Descriptor<T> d) {
return Generalizer.this.generalize(d)
.stream()
.map(next::generalize)
.flatMap(Set::stream)
.collect(Collectors.toSet());
}
};
}
static Generalizer direct() {
return new Generalizer() {
@Override
public <T> Set<Descriptor<? super T>> generalize(Descriptor<T> d) {
return new HashSet<>(Collections.singletonList(d));
}
};
}
static Generalizer anonymizing() {
return new Generalizer() {
@Override
public <T> Set<Descriptor<? super T>> generalize(Descriptor<T> d) {
return new HashSet<>(Arrays.asList(d, d.withName(NO_NAME)));
}
};
}
}
@RequiredArgsConstructor
private static class TypeHierarchyGeneralizer implements Generalizer {
@Override
public <T> Set<Descriptor<? super T>> generalize(Descriptor<T> descriptor) {
Stack<Class<? super T>> next = new Stack<>();
next.push(descriptor.getType());
HashSet<Class<? super T>> visited = new HashSet<>();
while (!next.isEmpty()) {
Class<? super T> type = next.pop();
if (!visited.contains(type)) {
visited.add(type);
next.addAll(immediateSupertypes(type));
}
}
HashSet<Descriptor<? super T>> set = new HashSet<>();
for (Class<? super T> type : visited) {
set.add(new Descriptor<>(descriptor.getName(), type));
}
return set;
}
private <T> List<Class<? super T>> immediateSupertypes(Class<T> type) {
List<Class<? super T>> supertypes = new ArrayList<>();
if (type.getSuperclass() != null) {
supertypes.add(type.getSuperclass());
}
for (Class<?> iface : type.getInterfaces()) {
@SuppressWarnings("unchecked")
Class<? super T> fixed = (Class<? super T>) iface;
supertypes.add(fixed);
}
return supertypes;
}
}
public interface ProviderSource {
<T> List<Provider<? extends T>> get(Descriptor<T> descriptor);
}
@RequiredArgsConstructor
public static class ProviderMap implements ProviderSource {
private final Map<Descriptor<?>, List<Provider<?>>> providers;
@Override
public <T> List<Provider<? extends T>> get(Descriptor<T> descriptor) {
return providers
.getOrDefault(descriptor, Collections.emptyList())
.stream()
.map(descriptor::cast)
.collect(Collectors.toList());
}
}
public interface ProviderSourceFactory {
ProviderSource create(List<Binder<?>> binders);
}
@RequiredArgsConstructor
public static class GeneralizingProviderMapFactory implements ProviderSourceFactory {
private final Generalizer generalizer;
@Override
public ProviderSource create(List<Binder<?>> binders) {
Map<Descriptor<?>, List<Provider<?>>> map = new HashMap<>();
for (Binder<?> binder : binders) {
for (Descriptor<?> descriptor : generalizer.generalize(binder.getDescriptor())) {
map.computeIfAbsent(descriptor, $ -> new ArrayList<>()).add(binder.getProvider());
}
}
return new ProviderMap(map);
}
}
public interface Picker {
<T> List<Provider<? extends T>> pick(ProviderSource source, Descriptor<T> descriptor);
}
@RequiredArgsConstructor
public static class LenientPicker implements Picker {
@Override
public <T> List<Provider<? extends T>> pick(ProviderSource source, Descriptor<T> descriptor) {
List<Provider<? extends T>> named = source.get(descriptor);
if (named.isEmpty()) {
return source.get(descriptor.withName(NO_NAME));
} else {
return named;
}
}
}
@RequiredArgsConstructor
public static class ConstructorProvider<T> implements Provider<T> {
private final Class<T> type;
@Override
public Class<? extends T> principalType() {
return type;
}
@Override
public T resolve(Container container) {
@SuppressWarnings("unchecked")
Constructor<T>[] constructors = (Constructor<T>[]) type.getConstructors();
switch (constructors.length) {
case 0:
throw new RuntimeException("no constructor");
case 1:
break;
default:
throw new RuntimeException("no canonical constructor");
}
Constructor<T> constructor = constructors[0];
Parameter[] parameters = constructor.getParameters();
Object[] arguments = container.bindAll(parameters);
try {
return constructor.newInstance(arguments);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
@RequiredArgsConstructor
public static class CachingProvider<T> implements Provider<T> {
private final AtomicReference<T> store = new AtomicReference<>();
private final Provider<T> subject;
@Override
public Class<? extends T> principalType() {
return subject.principalType();
}
@Override
public T resolve(Container container) {
return store.updateAndGet(current -> {
if (current == null) {
return Objects.requireNonNull(subject.resolve(container));
} else {
return current;
}
});
}
}
@RequiredArgsConstructor
public static class InstanceProvider<T> implements Provider<T> {
private final T instance;
@SuppressWarnings("unchecked")
@Override
public Class<? extends T> principalType() {
return (Class<? extends T>) instance.getClass();
}
@Override
public T resolve(Container container) {
return instance;
}
}
@RequiredArgsConstructor
private static class TheContainer implements Container {
private final ProviderSource providerSource;
private final Picker picker;
@Override
public <T> T get(Descriptor<T> descriptor) {
List<Provider<? extends T>> candidates = picker.pick(providerSource, descriptor);
switch (candidates.size()) {
case 0:
throw new RuntimeException("no providers");
case 1:
Provider<? extends T> provider = candidates.get(0);
return provider.resolve(this);
default:
throw new RuntimeException("many providers: " + candidates.size());
}
}
}
@RequiredArgsConstructor
private static class TheBuilder implements Builder {
private final List<Binder<?>> binders = new ArrayList<>();
@Override
public Builder with(Binder<?> binder) {
binders.add(binder);
return this;
}
@Override
public Container build() {
Generalizer generalizer =
Generalizer.direct()
.andThen(Generalizer.anonymizing())
.andThen(new TypeHierarchyGeneralizer());
return new TheContainer(
new GeneralizingProviderMapFactory(generalizer).create(binders),
new LenientPicker());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment