/* * Copyright 2015, The Querydsl Team (http://www.querydsl.com/team) * * 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.querydsl.codegen; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; import javax.inject.Inject; import javax.inject.Named; /** * {@code AbstractModule} provides a base class for annotation based dependency injection * * @author tiwe * */ public abstract class AbstractModule { private final Map<Class<?>, Object> instances = new HashMap<Class<?>, Object>(); private final Map<Class<?>, Class<?>> bindings = new HashMap<Class<?>, Class<?>>(); private final Map<String, Object> namedInstances = new HashMap<String, Object>(); private final Map<String, Class<?>> namedBindings = new HashMap<String, Class<?>>(); public AbstractModule() { configure(); } public final <T> AbstractModule bind(Class<T> clazz) { if (clazz.isInterface()) { throw new IllegalArgumentException("Interfaces can't be instantiated"); } bind(clazz, clazz); return this; } public final <T> AbstractModule bind(String name, Class<? extends T> implementation) { namedBindings.put(name, implementation); return this; } public final <T> AbstractModule bind(String name, T implementation) { namedInstances.put(name, implementation); return this; } public final <T> AbstractModule bindInstance(String name, Class<? extends T> implementation) { namedInstances.put(name, implementation); return this; } public final <T> AbstractModule bind(Class<T> iface, Class<? extends T> implementation) { bindings.put(iface, implementation); return this; } public final <T> AbstractModule bind(Class<T> iface, T implementation) { instances.put(iface, implementation); return this; } protected abstract void configure(); @SuppressWarnings("unchecked") public final <T> T get(Class<T> iface) { if (instances.containsKey(iface)) { return (T) instances.get(iface); } else if (bindings.containsKey(iface)) { Class<?> implementation = bindings.get(iface); T instance = (T) createInstance(implementation); instances.put(iface, instance); return instance; } else { throw new IllegalArgumentException(iface.getName() + " is not registered"); } } @SuppressWarnings("unchecked") public final <T> T get(Class<T> iface, String name) { if (namedInstances.containsKey(name)) { return (T) namedInstances.get(name); } else if (namedBindings.containsKey(name)) { Class<?> implementation = namedBindings.get(name); if (implementation != null) { T instance = (T) createInstance(implementation); namedInstances.put(name, instance); return instance; } else { return null; } } else { throw new IllegalArgumentException(iface.getName() + " " + name + " is not registered"); } } @SuppressWarnings("unchecked") private <T> T createInstance(Class<? extends T> implementation) { Constructor<?> constructor = null; for (Constructor<?> c : implementation.getConstructors()) { if (c.isAnnotationPresent(Inject.class)) { constructor = c; break; } } // fallback to default constructor if (constructor == null) { try { constructor = implementation.getConstructor(); } catch (SecurityException e) { throw new RuntimeException(e); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } if (constructor != null) { Object[] args = new Object[constructor.getParameterTypes().length]; for (int i = 0; i < constructor.getParameterTypes().length; i++) { Named named = getNamedAnnotation(constructor.getParameterAnnotations()[i]); if (named != null) { args[i] = get(constructor.getParameterTypes()[i], named.value()); } else { args[i] = get(constructor.getParameterTypes()[i]); } } try { return (T) constructor.newInstance(args); // TODO : populate fields as well?!? } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } } else { throw new IllegalArgumentException("Got no annotated constructor for " + implementation.getName()); } } private Named getNamedAnnotation(Annotation[] annotations) { for (Annotation annotation : annotations) { if (annotation.annotationType().equals(Named.class)) { return (Named) annotation; } } return null; } }