ObjectBuilderProviderImpl.java

package space.sunqian.common.object.data;

import space.sunqian.annotations.Nonnull;
import space.sunqian.annotations.Nullable;
import space.sunqian.common.FsLoader;
import space.sunqian.common.collect.ListKit;
import space.sunqian.common.invoke.Invocable;
import space.sunqian.common.invoke.InvocationException;
import space.sunqian.common.reflect.TypeKit;

import java.lang.reflect.Constructor;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

final class ObjectBuilderProviderImpl implements ObjectBuilderProvider, ObjectBuilderProvider.Handler {

    static final @Nonnull ObjectBuilderProvider DEFAULT = new ObjectBuilderProviderImpl(
        new BuilderCacheImpl(new ConcurrentHashMap<>()),
        // Collections.singletonList(DefaultHandler.INST)
        FsLoader.loadInstances(
            FsLoader.loadClassByDependent(
                "space.sunqian.common.third.protobuf.ProtobufBuilderHandler",
                "com.google.protobuf.Message"
            ),
            DefaultHandler.INST
        )
    );

    private final @Nonnull ObjectBuilderProvider.BuilderCache builderCache;
    private final @Nonnull List<ObjectBuilderProvider.@Nonnull Handler> handlers;

    ObjectBuilderProviderImpl(
        @Nonnull ObjectBuilderProvider.BuilderCache builderCache,
        @Nonnull List<@Nonnull Handler> handlers
    ) {
        this.builderCache = builderCache;
        this.handlers = handlers;
    }

    @Override
    public @Nullable ObjectBuilder builder(@Nonnull Type target) throws DataObjectException {
        return builderCache.get(target, t -> {
            try {
                for (Handler handler : handlers) {
                    ObjectBuilder ob = handler.newBuilder(t);
                    if (ob != null) {
                        return ob;
                    }
                }
                return null;
            } catch (Exception e) {
                throw new DataObjectException(e);
            }
        });
    }

    @Override
    public @Nullable ObjectBuilder newBuilder(@Nonnull Type target) {
        return builder(target);
    }

    @Override
    public @Nonnull List<@Nonnull Handler> handlers() {
        return handlers;
    }

    public @Nonnull ObjectBuilderProvider withFirstHandler(@Nonnull Handler handler) {
        Handler[] newHandlers = new Handler[handlers().size() + 1];
        int i = 0;
        newHandlers[i++] = handler;
        for (Handler h : handlers()) {
            newHandlers[i++] = h;
        }
        return new ObjectBuilderProviderImpl(builderCache, ListKit.list(newHandlers));
    }

    public @Nonnull ObjectBuilderProvider withLastHandler(@Nonnull Handler handler) {
        Handler[] newHandlers = new Handler[handlers().size() + 1];
        int i = 0;
        for (Handler h : handlers()) {
            newHandlers[i++] = h;
        }
        newHandlers[i] = handler;
        return new ObjectBuilderProviderImpl(builderCache, ListKit.list(newHandlers));
    }

    @Override
    public @Nonnull Handler asHandler() {
        return this;
    }

    static final class BuilderCacheImpl implements BuilderCache {

        private final @Nonnull Map<@Nonnull Type, @Nonnull ObjectBuilder> map;

        BuilderCacheImpl(@Nonnull Map<@Nonnull Type, @Nonnull ObjectBuilder> map) {
            this.map = map;
        }

        @Override
        public @Nullable ObjectBuilder get(
            @Nonnull Type target,
            @Nonnull Function<? super @Nonnull Type, ? extends @Nullable ObjectBuilder> loader
        ) throws DataObjectException {
            return map.computeIfAbsent(target, loader);
        }
    }

    private enum DefaultHandler implements Handler {

        INST;

        @Override
        public @Nullable ObjectBuilder newBuilder(@Nonnull Type target) throws Exception {
            Class<?> rawTarget = TypeKit.getRawClass(target);
            if (rawTarget == null) {
                return null;
            }
            try {
                Constructor<?> cst = rawTarget.getConstructor();
                return new SimpleBuilder(cst, target);
            } catch (NoSuchMethodException e) {
                return null;
            }
        }
    }

    private static final class SimpleBuilder implements ObjectBuilder {

        private final @Nonnull Invocable constructor;
        private final @Nonnull Type target;

        private SimpleBuilder(@Nonnull Constructor<?> constructor, @Nonnull Type target) {
            this.constructor = Invocable.of(constructor);
            this.target = target;
        }

        @Override
        public @Nonnull Object newBuilder() throws DataObjectException {
            try {
                return constructor.invoke(null);
            } catch (InvocationException e) {
                throw new DataObjectException(e.getCause());
            }
        }

        @Override
        public @Nonnull Type builderType() {
            return target;
        }

        @Override
        public @Nonnull Object build(@Nonnull Object builder) throws DataObjectException {
            return builder;
        }
    }
}