/*
* Copyright 2018 John Grosh (john.a.grosh@gmail.com).
*
* 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 com.jagrosh.vortex.pro;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author Michael Ritter (Kantenkugel)
*/
public class VortexPro
{
private static final Map<Class<?>, Object> IMPLS = new HashMap<>();
@SuppressWarnings("unchecked")
public static <T> T from(Class<T> apiClass, Object... args)
{
if(IMPLS.containsKey(apiClass))
{
return (T) IMPLS.get(apiClass);
}
ProFeature anno = apiClass.getAnnotation(ProFeature.class);
Class<? extends T> implClass = null;
try
{
Class<?> tmpClass = Class.forName(anno.value());
if(!apiClass.isAssignableFrom(tmpClass) || tmpClass.isInterface())
{
throw new IllegalStateException("Class " + tmpClass.getName() + " can't be converted to a instance of " + apiClass.getName());
}
implClass = (Class<? extends T>) tmpClass;
}
catch(ClassNotFoundException ignored) {}
if(implClass == null && anno.mock() != Object.class)
{
if(!apiClass.isAssignableFrom(anno.mock()) || anno.mock().isInterface())
{
throw new IllegalStateException("Class " + anno.mock().getName() + " can't be converted to a instance of " + apiClass.getName());
}
implClass = (Class<? extends T>) anno.mock();
}
try
{
T instance = null;
if(implClass != null)
{
Set<Constructor<? extends T>> constructors = Arrays.stream(implClass.getConstructors())
.filter(c -> c.getParameterCount() == 0 || c.getParameterCount() == args.length)
.map(c -> (Constructor<? extends T>) c)
.collect(Collectors.toSet());
if(constructors.size() != 1)
{
throw new IllegalArgumentException("Could not find a (single) Constructor with 0 or matching amount of arguments. Arguments provided: " + Arrays.toString(args));
}
Constructor<? extends T> constructor = constructors.iterator().next();
if(constructor.getParameterCount() == 0)
instance = constructor.newInstance();
else
instance = constructor.newInstance(args);
}
IMPLS.put(apiClass, instance);
return instance;
}
catch(InvocationTargetException | InstantiationException | IllegalAccessException e)
{
throw new IllegalStateException("Can not instanciate class "+implClass.getName()+". Does it have a public no-arg constructor?", e);
}
}
private VortexPro() {}
}