/**
 * Copyright 2007-2015, Kaazing Corporation. All rights reserved.
 *
 * 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
 *
 * http://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 org.kaazing.nuklei;

import static java.lang.reflect.Modifier.isPublic;
import static java.lang.reflect.Modifier.isStatic;
import static java.util.Collections.unmodifiableMap;
import static java.util.Objects.requireNonNull;
import static java.util.ServiceLoader.load;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;

import javax.annotation.Resource;

public final class NukleusFactory
{

    public static NukleusFactory instantiate()
    {
        return instantiate(load(NukleusFactorySpi.class));
    }

    public static NukleusFactory instantiate(ClassLoader classLoader)
    {
        return instantiate(load(NukleusFactorySpi.class, classLoader));
    }

    public Iterable<String> names()
    {
        return factorySpisByName.keySet();
    }

    public Nukleus create(String name, Configuration config)
    {
        requireNonNull(name);
        requireNonNull(config);

        NukleusFactorySpi factorySpi = factorySpisByName.get(name);
        if (factorySpi == null)
        {
            throw new IllegalArgumentException("Unregonized nukleus name: " + name);
        }

        return factorySpi.create(config);
    }

    private static NukleusFactory instantiate(ServiceLoader<NukleusFactorySpi> factories)
    {
        Map<String, NukleusFactorySpi> factorySpisByName = new HashMap<>();
        factories.forEach((factorySpi) -> { factorySpisByName.put(factorySpi.name(), factorySpi); });

        NukleusFactory factory = new NukleusFactory(unmodifiableMap(factorySpisByName));
        factorySpisByName.values().forEach((factorySpi) -> { inject(factorySpi, NukleusFactory.class, factory); });

        return factory;
    }

    private static <T> void inject(Object target, Class<T> type, T instance)
    {
        try
        {
            Class<? extends Object> targetClass = target.getClass();
            Method[] targetMethods = targetClass.getMethods();
            for (Method targetMethod : targetMethods)
            {
                if (targetMethod.getAnnotation(Resource.class) == null)
                {
                    continue;
                }

                if (!isPublic(targetMethod.getModifiers()) || isStatic(targetMethod.getModifiers()))
                {
                    continue;
                }

                Class<?>[] targetMethodParameterTypes = targetMethod.getParameterTypes();
                if (targetMethodParameterTypes.length != 1 || targetMethodParameterTypes[0] != type)
                {
                    continue;
                }

                try
                {
                    targetMethod.invoke(target, instance);
                }
                catch (IllegalArgumentException e)
                {
                    // failed to inject
                }
                catch (IllegalAccessException e)
                {
                    // failed to inject
                }
                catch (InvocationTargetException e)
                {
                    // failed to inject
                }
            }
        }
        catch (SecurityException e)
        {
            // failed to reflect
        }
    }

    private final Map<String, NukleusFactorySpi> factorySpisByName;

    private NukleusFactory(Map<String, NukleusFactorySpi> factorySpisByName)
    {
        this.factorySpisByName = factorySpisByName;
    }
}

@@ -654,110 +654,8 @@
 i;%0A%0A
-import static java.lang.reflect.Modifier.isPublic;%0Aimport static java.lang.reflect.Modifier.isStatic;%0A
 impo
@@ -699,24 +699,24 @@
 ifiableMap;%0A
+
 import stati
@@ -800,93 +800,8 @@
 d;%0A%0A
-import java.lang.reflect.InvocationTargetException;%0Aimport java.lang.reflect.Method;%0A
 impo
@@ -881,43 +881,8 @@
 r;%0A%0A
-import javax.annotation.Resource;%0A%0A
 publ
@@ -913,17 +913,16 @@
 ctory%0A%7B%0A
-%0A
     publ
@@ -1369,24 +1369,32 @@
 NonNull(name
+, %22name%22
 );%0A        r
@@ -1413,16 +1413,26 @@
 l(config
+, %22config%22
 );%0A%0A    
@@ -1969,32 +1969,14 @@
     
-NukleusFactory factory =
+return
  new
@@ -2032,1634 +2032,8 @@
 ));%0A
-        factorySpisByName.values().forEach((factorySpi) -%3E %7B inject(factorySpi, NukleusFactory.class, factory); %7D);%0A%0A        return factory;%0A    %7D%0A%0A    private static %3CT%3E void inject(Object target, Class%3CT%3E type, T instance)%0A    %7B%0A        try%0A        %7B%0A            Class%3C? extends Object%3E targetClass = target.getClass();%0A            Method%5B%5D targetMethods = targetClass.getMethods();%0A            for (Method targetMethod : targetMethods)%0A            %7B%0A                if (targetMethod.getAnnotation(Resource.class) == null)%0A                %7B%0A                    continue;%0A                %7D%0A%0A                if (!isPublic(targetMethod.getModifiers()) %7C%7C isStatic(targetMethod.getModifiers()))%0A                %7B%0A                    continue;%0A                %7D%0A%0A                Class%3C?%3E%5B%5D targetMethodParameterTypes = targetMethod.getParameterTypes();%0A                if (targetMethodParameterTypes.length != 1 %7C%7C targetMethodParameterTypes%5B0%5D != type)%0A                %7B%0A                    continue;%0A                %7D%0A%0A                try%0A                %7B%0A                    targetMethod.invoke(target, instance);%0A                %7D%0A                catch (IllegalArgumentException e)%0A                %7B%0A                    // failed to inject%0A                %7D%0A                catch (IllegalAccessException e)%0A                %7B%0A                    // failed to inject%0A                %7D%0A                catch (InvocationTargetException e)%0A                %7B%0A                    // failed to inject%0A                %7D%0A            %7D%0A        %7D%0A        catch (SecurityException e)%0A        %7B%0A            // failed to reflect%0A        %7D%0A
     

Remove injection of NukleusFactory into each Nukleus to reduce coupling