/* * The MIT License (MIT) * * Copyright (c) 2016. Diorite (by Bartłomiej Mazur (aka GotoFinal)) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package org.diorite.inject; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import org.diorite.inject.impl.asm.QualifierAndScopeImplementationGenerator; /** * Library utility class for creation qualifier instances. */ public final class Qualifiers { private static final Map<Class<? extends Annotation>, Function<Map<String, ?>, ? extends Annotation>> constructors = new ConcurrentHashMap<>(20); private static final Map<Class<? extends Annotation>, Function<? extends Annotation, ? extends Annotation>> unproxy = new ConcurrentHashMap<>(20); private Qualifiers() { } /** * Register new annotation constructor function. * * @param type * type of annotation, must be annotated with {@link Qualifier} or {@link Scope} * @param constructor * constructor function. * @param unproxy * function that change proxied class instance to generated class instance. * @param <T> * type of annotation */ public static <T extends Annotation> void register(Class<T> type, Function<Map<String, ?>, T> constructor, Function<T, T> unproxy) { if (! type.isAnnotation()) { throw new IllegalStateException("Given class isn't annotation class! Found " + type + " instead!"); } if (! (type.isAnnotationPresent(Qualifier.class) || type.isAnnotationPresent(Scope.class))) { throw new IllegalStateException("Given annotation isn't annotated with 'org.diorite.inject.Qualifier' or 'org.diorite.inject.Scope' annotation!"); } constructors.put(type, constructor); Qualifiers.unproxy.put(type, unproxy); } private static Function<? extends Annotation, ? extends Annotation> getUnproxyFunction(Class<? extends Annotation> type) { Function<? extends Annotation, ? extends Annotation> constructor = unproxy.get(type); if (constructor != null) { return constructor; } Class<? extends Annotation> transform = QualifierAndScopeImplementationGenerator.transform(type); if (transform == null) { throw new IllegalArgumentException("Given class isn't valid qualifier annotation: " + type); } forceInit(transform); return unproxy.get(type); } private static Function<Map<String, ?>, ? extends Annotation> getConstructor(Class<? extends Annotation> type) { Function<Map<String, ?>, ? extends Annotation> constructor = constructors.get(type); if (constructor != null) { return constructor; } Class<? extends Annotation> transform = QualifierAndScopeImplementationGenerator.transform(type); if (transform == null) { throw new IllegalArgumentException("Given class isn't valid qualifier annotation: " + type); } forceInit(transform); return constructors.get(type); } private static <T> Class<T> forceInit(Class<T> clazz) { try { Class.forName(clazz.getName(), true, clazz.getClassLoader()); } catch (ClassNotFoundException e) { throw new AssertionError(e); // Can't happen } return clazz; } /** * Convert java proxy annotation to specialized implementation, used to speedup equals/hashcode/toString methods. * * @param annotation * annotation to unproxy. * @param <T> * type of annotation. * * @return specialized annotation implementation instance. */ @SuppressWarnings({"rawtypes", "unchecked"}) public static <T extends Annotation> T unproxy(T annotation) { Function unproxyFunction = getUnproxyFunction(annotation.annotationType()); return (T) unproxyFunction.apply(annotation); } /** * Create annotation instance with given values. * * @param type * type of annotation, must be annotated with {@link Qualifier} or {@link Scope} * @param data * annotation values. * @param <T> * type of annotation. * * @return annotation instance with given values. */ @SuppressWarnings("unchecked") public static <T extends Annotation> T of(Class<T> type, Map<String, ?> data) { Function<Map<String, ?>, ? extends Annotation> constructor = getConstructor(type); return (T) constructor.apply(data); } /** * Create annotation instance with given values. * * @param type * type of annotation, must be annotated with {@link Qualifier} or {@link Scope} * @param value * value of annotation. * @param <T> * type of annotation. * * @return annotation instance with given values. */ public static <T extends Annotation> T of(Class<T> type, Object value) { Method best = null; for (Method method : type.getDeclaredMethods()) { String methodName = method.getName(); Object def = method.getDefaultValue(); if (methodName.equals("value") && (def == null)) { return of(type, Map.of("value", value)); } if (def == null) { best = method; } } if (best != null) { return of(type, Map.of(best.getName(), value)); } return of(type, Map.of("value", value)); } /** * Create annotation instance with given values. * * @param type * type of annotation, must be annotated with {@link Qualifier} or {@link Scope} * @param name * name of annotation value to use. * @param value * value of annotation. * @param <T> * type of annotation. * * @return annotation instance with given values. */ public static <T extends Annotation> T of(Class<T> type, String name, Object value) { return of(type, Map.of(name, value)); } /** * Create annotation instance with default values. * * @param type * type of annotation, must be annotated with {@link Qualifier} or {@link Scope} * @param <T> * type of annotation. * * @return annotation instance with given values. */ public static <T extends Annotation> T of(Class<T> type) { return of(type, Map.of()); } }