ProjectAres/API/api/src/main/java/tc/oc/api/model/ModelBinder.java

152 lines
5.7 KiB
Java

package tc.oc.api.model;
import java.util.Map;
import java.util.Set;
import javax.inject.Singleton;
import com.google.inject.Provides;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.multibindings.OptionalBinder;
import tc.oc.api.docs.virtual.Model;
import tc.oc.api.docs.virtual.PartialModel;
import tc.oc.api.queue.QueueQueryService;
import tc.oc.commons.core.inject.Binders;
import tc.oc.commons.core.inject.KeyedManifest;
import tc.oc.commons.core.inject.SingletonManifest;
import tc.oc.commons.core.reflect.ResolvableType;
import tc.oc.commons.core.reflect.TypeArgument;
import tc.oc.commons.core.reflect.TypeLiterals;
import tc.oc.commons.core.stream.Collectors;
import tc.oc.commons.core.util.ImmutableTypeMap;
import tc.oc.commons.core.util.TypeMap;
import tc.oc.inject.ProtectedBinder;
import tc.oc.minecraft.suspend.SuspendableBinder;
public class ModelBinder<M extends Model, P extends PartialModel> implements ModelTypeLiterals, TypeLiterals {
private final TypeLiteral<M> M;
private final TypeLiteral<P> P;
private final Binders binder;
private final Multibinder<ModelMeta> metas;
private final OptionalBinder<QueryService<M>> queryServiceBinder;
private final OptionalBinder<UpdateService<P>> updateServiceBinder;
private final OptionalBinder<ModelService<M, P>> serviceBinder;
private final OptionalBinder<ModelStore<M>> storeBinder;
public static <M extends Model> ModelBinder<M, M> of(ProtectedBinder binder, Class<M> M) {
return of(binder, M, M);
}
public static <M extends Model> ModelBinder<M, M> of(ProtectedBinder binder, TypeLiteral<M> M) {
return of(binder, M, M);
}
public static <M extends Model, P extends PartialModel> ModelBinder<M, P> of(ProtectedBinder binder, Class<M> M, Class<P> P) {
return of(binder, TypeLiteral.get(M), TypeLiteral.get(P));
}
public static <M extends Model, P extends PartialModel> ModelBinder<M, P> of(ProtectedBinder binder, TypeLiteral<M> M, TypeLiteral<P> P) {
return new ModelBinder<>(binder, M, P);
}
private ModelBinder(ProtectedBinder protectedBinder, TypeLiteral<M> M, TypeLiteral<P> P) {
this.binder = Binders.wrap(protectedBinder.publicBinder());
this.M = M;
this.P = P;
this.metas = Multibinder.newSetBinder(binder, ModelMeta.class);
this.serviceBinder = OptionalBinder.newOptionalBinder(binder, ModelService(M, P));
this.queryServiceBinder = OptionalBinder.newOptionalBinder(binder, QueryService(M));
this.updateServiceBinder = OptionalBinder.newOptionalBinder(binder, UpdateService(P));
this.storeBinder = OptionalBinder.newOptionalBinder(binder, ModelStore(M));
binder.install(new OneTime());
binder.install(new PerModel());
}
public LinkedBindingBuilder<ModelStore<M>> bindStore() {
binder.provisionEagerly(ModelStore(M));
new SuspendableBinder(binder).addBinding().to(ModelStore(M));
return storeBinder.setBinding();
}
public OptionalBinder<QueryService<M>> queryService() {
return queryServiceBinder;
}
public OptionalBinder<UpdateService<P>> updateService() {
return updateServiceBinder;
}
public LinkedBindingBuilder<ModelService<M, P>> bindDefaultService() {
queryService().setDefault().to(ModelService(M, P));
updateService().setDefault().to(ModelService(M, P));
return serviceBinder.setDefault();
}
public LinkedBindingBuilder<ModelService<M, P>> bindService() {
queryService().setBinding().to(ModelService(M, P));
updateService().setBinding().to(ModelService(M, P));
return serviceBinder.setBinding();
}
public TypeLiteral<NullModelService<M, P>> nullService() {
return NullModelService(M, P);
}
public TypeLiteral<NullQueryService<M>> nullQueryService() {
return NullQueryService(M);
}
public TypeLiteral<HttpModelService<M, P>> httpService() {
return HttpModelService(M, P);
}
public TypeLiteral<HttpQueryService<M>> httpQueryService() {
return HttpQueryService(M);
}
public TypeLiteral<QueueQueryService<M>> queueQueryService() {
return QueueQueryService(M);
}
private class PerModel extends KeyedManifest {
@Override
protected Object manifestKey() {
return M;
}
@Override
protected void configure() {
final TypeLiteral<ModelMeta<M, P>> meta = ModelMeta(M, P);
metas.addBinding().to(meta);
bind(meta).in(Singleton.class);
bind(new ResolvableType<ModelMeta<M, ?>>(){}.with(new TypeArgument<M>(M){})).to(meta);
bind(new ResolvableType<ModelMeta<?, P>>(){}.with(new TypeArgument<P>(P){})).to(meta);
}
}
private class OneTime extends SingletonManifest {
@Provides @Singleton
Map<String, ModelMeta> byName(Set<ModelMeta> metas) {
return metas.stream().collect(Collectors.indexingBy(ModelMeta::name));
}
@Provides @Singleton
TypeMap<Model, ModelMeta> byType(Set<ModelMeta> metas) {
final ImmutableTypeMap.Builder<Model, ModelMeta> builder = ImmutableTypeMap.builder();
metas.forEach(meta -> builder.put(meta.completeType(), meta));
return builder.build();
}
@Provides @Singleton
TypeMap<PartialModel, ModelMeta> byPartialType(Set<ModelMeta> metas) {
final ImmutableTypeMap.Builder<PartialModel, ModelMeta> builder = ImmutableTypeMap.builder();
metas.forEach(meta -> builder.put(meta.partialType(), meta));
return builder.build();
}
}
}