All Downloads are FREE. Search and download functionalities are using the official Maven repository.

global.namespace.neuron.di.internal.ProxyFactory Maven / Gradle / Ivy

/*
 * Copyright © 2016 Schlichtherle IT Services
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package global.namespace.neuron.di.internal;

import global.namespace.neuron.di.java.BreedingException;
import global.namespace.neuron.di.java.DependencyProvider;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

import static java.lang.invoke.MethodHandles.publicLookup;
import static java.lang.invoke.MethodType.methodType;

final class ProxyFactory implements Function {

    private static final MethodType dependencyProviderObjectMethodType =
            methodType(DependencyProvider.class, Object.class);

    private static final MethodType voidObjectDependencyProviderMethodType =
            methodType(Void.TYPE, Object.class, DependencyProvider.class);

    private static final MethodType objectMethodType =
            methodType(Object.class);

    private final Class proxyClass;
    private final MethodHandle constructorHandle;
    private final List methodHandlers;

    ProxyFactory(final Class clazz, final List> bindableElements) {
        this.proxyClass = ASM.proxyClass(clazz, map(bindableElements, MethodElement::method));
        try {
            final Constructor c = proxyClass.getDeclaredConstructor();
            c.setAccessible(true);
            this.constructorHandle = publicLookup().unreflectConstructor(c).asType(objectMethodType);
        } catch (ReflectiveOperationException e) {
            throw new BreedingException(e);
        }
        this.methodHandlers = map(bindableElements, MethodHandler::new);
    }

    private static  List map(List list, Function fun) {
        return list.stream().map(fun).collect(Collectors.toList());
    }

    @SuppressWarnings("unchecked")
    @Override
    public C apply(final MethodBinding binding) {
        return new Visitor() {

            final C proxy;

            BoundMethodHandler boundMethodHandler;

            {
                try {
                    proxy = (C) constructorHandle.invokeExact();
                } catch (Throwable e) {
                    throw new BreedingException(e);
                }
                for (final MethodHandler handler : methodHandlers) {
                    boundMethodHandler = handler.bind(proxy);
                    handler.accept(this);
                }
            }

            @Override
            public void visitSynapse(SynapseElement element) {
                boundMethodHandler.provider(element.decorate(binding.apply(element)
                        .orElseThrow(() -> new BreedingException("No binding defined for synapse method: " + element.method()))));
            }

            @Override
            public void visitMethod(MethodElement element) {
                boundMethodHandler.provider(element.decorate(binding.apply(element)
                        .orElseGet(boundMethodHandler::provider)));
            }

        }.proxy;
    }

    private final class MethodHandler {

        final MethodElement element;
        final MethodHandle getter, setter;

        MethodHandler(final MethodElement element) {
            this.element = element;
            final String fieldName = element.proxyFieldName();
            final MethodHandles.Lookup lookup = publicLookup();
            try {
                final Field field = proxyClass.getDeclaredField(fieldName);
                field.setAccessible(true);
                this.getter = lookup.unreflectGetter(field).asType(dependencyProviderObjectMethodType);
                this.setter = lookup.unreflectSetter(field).asType(voidObjectDependencyProviderMethodType);
            } catch (ReflectiveOperationException e) {
                throw new AssertionError(e);
            }
        }

        BoundMethodHandler bind(final C proxy) {
            return new BoundMethodHandler() {

                @Override
                public DependencyProvider provider() {
                    try {
                        return (DependencyProvider) getter.invokeExact(proxy);
                    } catch (Throwable e) {
                        throw new AssertionError(e);
                    }
                }

                @Override
                public void provider(final DependencyProvider provider) {
                    try {
                        setter.invokeExact(proxy, provider);
                    } catch (Throwable e) {
                        throw new AssertionError(e);
                    }
                }
            };
        }

        void accept(Visitor visitor) {
            element.accept(visitor);
        }
    }

    private interface BoundMethodHandler {

        DependencyProvider provider();

        void provider(DependencyProvider provider);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy