package org.nustaq.serialization; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.LinkedHashSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * Represents a ordered class lineage based on the specificity of classes, where specificity is defined as follows: * <nl> * <li>null has specificity 0</li> * <li>java.lang.Object has specificity 0</li> * <li>an interface without any extends clause has specificity 1</li> * <li>a class or interface has a specificity of 1 + the specificity of the superclass + the sum of the specificity of the implemented interfaces.</li> * </nl> * @author Odd Moeller 2017-03-08. */ public final class FSTClazzLineageInfo { private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0]; private static final LineageInfo OBJECT_LINEAGE_INFO = new LineageInfo(new LinkedHashSet<Class<?>>(Collections.singletonList(Object.class)), 0); private static final ConcurrentMap<Class<?>, LineageInfo> lineageInfos = new ConcurrentHashMap<Class<?>, LineageInfo>(); public static final Comparator<Class> SPECIFICITY_CLASS_COMPARATOR = new Comparator<Class>() { @Override public int compare(final Class c1, final Class c2) { return getLineageInfo(c2).specificity - getLineageInfo(c1).specificity; } }; private FSTClazzLineageInfo() {} /** * Returns the specificity of the specified class as defined above. */ public static int getSpecificity(final Class<?> clazz) { if (clazz == null) return 0; final LineageInfo lineageInfo = FSTClazzLineageInfo.getLineageInfo(clazz); return lineageInfo == null ? 0 : lineageInfo.specificity; } /** * Returns the lineage of the specified class ordered by specificity (the class itself is at position 0 since it is most specific in its lineage). */ public static Class<?>[] getLineage(final Class<?> clazz) { final LineageInfo lineageInfo = getLineageInfo(clazz); return lineageInfo == null ? EMPTY_CLASS_ARRAY : lineageInfo.lineage.toArray(new Class<?>[lineageInfo.lineage.size()]); } private static LineageInfo getLineageInfo(final Class<?> clazz) { if (clazz == null) return null; else if (clazz.equals(Object.class)) return OBJECT_LINEAGE_INFO; final LineageInfo lineageInfo = lineageInfos.get(clazz); if (lineageInfo != null) { return lineageInfo; } int specificity = 1; final LinkedHashSet<Class<?>> ancestors = new LinkedHashSet<Class<?>>(); final Class<?> sc = getSuperclass(clazz); final LineageInfo sl = getLineageInfo(sc); if (sl != null) { ancestors.addAll(sl.lineage); specificity += sl.specificity; } for (final Class<?> i : getInterfaces(clazz)) { final LineageInfo il = getLineageInfo(i); if (il != null) { ancestors.removeAll(il.lineage); ancestors.addAll(il.lineage); specificity += il.specificity; } } final Class<?>[] array = ancestors.toArray(new Class<?>[ancestors.size()]); Arrays.sort(array, SPECIFICITY_CLASS_COMPARATOR); final LinkedHashSet<Class<?>> lineage = new LinkedHashSet<Class<?>>(array.length + 1); lineage.add(clazz); Collections.addAll(lineage, array); final LineageInfo result = new LineageInfo(lineage, specificity); lineageInfos.putIfAbsent(clazz, result); return result; } private static Class<?> getSuperclass(final Class<?> c) { if (c == null) return null; return c.getSuperclass(); } private static Class<?>[] getInterfaces(final Class<?> c) { if (c == null) return null; return c.getInterfaces(); } private static final class LineageInfo { private final LinkedHashSet<Class<?>> lineage; private final int specificity; private LineageInfo(final LinkedHashSet<Class<?>> lineage, final int specificity) { this.lineage = lineage; this.specificity = specificity; } } }