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

ru.vyarus.java.generics.resolver.context.GenericsInfoFactory Maven / Gradle / Ivy

There is a newer version: 3.0.3
Show newest version
package ru.vyarus.java.generics.resolver.context;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Analyze class hierarchy and produce class hierarchy resolved generics object.
 * Resolved generics descriptors are cached.
 * 

Note: when ignore classes used, cache will not work: such descriptors are always resolved.

* * @author Vyacheslav Rusakov * @since 16.10.2014 */ public final class GenericsInfoFactory { private static final Map, GenericsInfo> CACHE = new WeakHashMap, GenericsInfo>(); // lock will not affect performance for cached descriptors, just to make sure nothing was build two times private static final ReentrantLock LOCK = new ReentrantLock(); @SuppressWarnings("PMD.LooseCoupling") private static final LinkedHashMap EMPTY_MAP = new LinkedHashMap(0); private static final String GROOVY_OBJECT = "GroovyObject"; private GenericsInfoFactory() { } /** * Note: ignore classes switch off caching for resolved descriptor (and if completely resolved version * contained in cache limited version will be composed one more time). * * @param type finder type to investigate * @param ignoreClasses list of classes to ignore during inspection (useful to avoid interface clashes) * @return descriptor for class hierarchy generics substitution */ public static GenericsInfo create(final Class type, final Class... ignoreClasses) { GenericsInfo descriptor = ignoreClasses.length > 0 ? buildDescriptor(type, ignoreClasses) : CACHE.get(type); if (descriptor == null) { LOCK.lock(); try { if (CACHE.get(type) != null) { // descriptor could be created while thread wait for lock descriptor = CACHE.get(type); } else { descriptor = buildDescriptor(type); // internal check if (CACHE.get(type) != null) { throw new IllegalStateException("Bad concurrency: descriptor already present in cache"); } CACHE.put(type, descriptor); } } finally { LOCK.unlock(); } } return descriptor; } private static GenericsInfo buildDescriptor(final Class type, final Class... ignoreClasses) { final Map, LinkedHashMap> generics = new HashMap, LinkedHashMap>(); analyzeType(generics, type, Arrays.asList(ignoreClasses)); return new GenericsInfo(type, generics); } private static void analyzeType(final Map, LinkedHashMap> types, final Class type, final List> ignoreClasses) { Class supertype = type; while (true) { for (Type iface : supertype.getGenericInterfaces()) { analyzeInterface(types, iface, supertype, ignoreClasses); } final Class next = supertype.getSuperclass(); if (next == null || Object.class == next || ignoreClasses.contains(next)) { break; } types.put(next, analyzeParent(supertype, types.get(supertype))); supertype = next; } } private static void analyzeInterface(final Map, LinkedHashMap> types, final Type iface, final Class supertype, final List> ignoreClasses) { final Class interfaceType = iface instanceof ParameterizedType ? (Class) ((ParameterizedType) iface).getRawType() : (Class) iface; if (!ignoreClasses.contains(interfaceType)) { if (iface instanceof ParameterizedType) { final ParameterizedType parametrization = (ParameterizedType) iface; final LinkedHashMap generics = resolveGenerics(parametrization, types.get(supertype)); // no generics case and same resolved generics are ok (even if types in different branches of hierarchy) if (types.containsKey(interfaceType) && !generics.equals(types.get(interfaceType))) { throw new IllegalStateException(String.format( "Duplicate interface %s declaration in hierarchy: " + "can't properly resolve generics.", interfaceType.getName())); } types.put(interfaceType, generics); } else if (!GROOVY_OBJECT.equals(interfaceType.getSimpleName())) { // avoid groovy specific interface (all groovy objects implements it) types.put(interfaceType, EMPTY_MAP); } analyzeType(types, interfaceType, ignoreClasses); } } // LinkedHashMap used instead of usual map to avoid accidental simple map usage (order is important!) @SuppressWarnings("PMD.LooseCoupling") private static LinkedHashMap analyzeParent(final Class type, final Map rootGenerics) { LinkedHashMap generics = null; final Class parent = type.getSuperclass(); if (!type.isInterface() && parent != null && parent != Object.class && type.getGenericSuperclass() instanceof ParameterizedType) { generics = resolveGenerics((ParameterizedType) type.getGenericSuperclass(), rootGenerics); } return generics == null ? EMPTY_MAP : generics; } @SuppressWarnings("PMD.LooseCoupling") private static LinkedHashMap resolveGenerics(final ParameterizedType type, final Map rootGenerics) { final LinkedHashMap generics = new LinkedHashMap(); final Type[] genericTypes = type.getActualTypeArguments(); final Class interfaceType = (Class) type.getRawType(); final TypeVariable[] genericNames = interfaceType.getTypeParameters(); final int cnt = genericNames.length; for (int i = 0; i < cnt; i++) { final Type genericType = genericTypes[i]; Type resolvedGenericType; if (genericType instanceof TypeVariable) { // simple named generics resolved to target types resolvedGenericType = rootGenerics.get(((TypeVariable) genericType).getName()); } else { // composite generics passed as is resolvedGenericType = genericType; } generics.put(genericNames[i].getName(), resolvedGenericType); } return generics; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy