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

com.rt.storage.api.client.util.ClassInfo Maven / Gradle / Ivy

package com.rt.storage.api.client.util;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * Computes class information to determine data key name/value pairs associated with the class.
 *
 * 

Implementation is thread-safe. * * @since 1.0 * @author Yaniv Inbar */ public final class ClassInfo { /** Class information cache, with case-sensitive field names. */ private static final ConcurrentMap, ClassInfo> CACHE = new ConcurrentHashMap, ClassInfo>(); /** Class information cache, with case-insensitive fields names. */ private static final ConcurrentMap, ClassInfo> CACHE_IGNORE_CASE = new ConcurrentHashMap, ClassInfo>(); /** Class. */ private final Class clazz; /** Whether field names are case sensitive. */ private final boolean ignoreCase; /** Map from {@link FieldInfo#getName()} to the field information. */ private final IdentityHashMap nameToFieldInfoMap = new IdentityHashMap(); /** * Unmodifiable sorted (with any possible {@code null} member first) list (without duplicates) of * {@link FieldInfo#getName()}. */ final List names; /** * Returns the class information for the given underlying class. * * @param underlyingClass underlying class or {@code null} for {@code null} result * @return class information or {@code null} for {@code null} input */ public static ClassInfo of(Class underlyingClass) { return of(underlyingClass, false); } /** * Returns the class information for the given underlying class. * * @param underlyingClass underlying class or {@code null} for {@code null} result * @param ignoreCase whether field names are case sensitive * @return class information or {@code null} for {@code null} input * @since 1.10 */ public static ClassInfo of(Class underlyingClass, boolean ignoreCase) { if (underlyingClass == null) { return null; } final ConcurrentMap, ClassInfo> cache = ignoreCase ? CACHE_IGNORE_CASE : CACHE; // Logic copied from ConcurrentMap.computeIfAbsent ClassInfo v, newValue; return ((v = cache.get(underlyingClass)) == null && (newValue = new ClassInfo(underlyingClass, ignoreCase)) != null && (v = cache.putIfAbsent(underlyingClass, newValue)) == null) ? newValue : v; } /** * Returns the underlying class. * * @since 1.4 */ public Class getUnderlyingClass() { return clazz; } /** * Returns whether field names are case sensitive. * * @since 1.10 */ public final boolean getIgnoreCase() { return ignoreCase; } /** * Returns the information for the given {@link FieldInfo#getName()}. * * @param name {@link FieldInfo#getName()} or {@code null} * @return field information or {@code null} for none */ public FieldInfo getFieldInfo(String name) { if (name != null) { if (ignoreCase) { name = name.toLowerCase(Locale.US); } name = name.intern(); } return nameToFieldInfoMap.get(name); } /** * Returns the field for the given {@link FieldInfo#getName()}. * * @param name {@link FieldInfo#getName()} or {@code null} * @return field or {@code null} for none */ public Field getField(String name) { FieldInfo fieldInfo = getFieldInfo(name); return fieldInfo == null ? null : fieldInfo.getField(); } /** * Returns the underlying class is an enum. * * @since 1.4 */ public boolean isEnum() { return clazz.isEnum(); } /** * Returns an unmodifiable sorted set (with any possible {@code null} member first) of {@link * FieldInfo#getName() names}. */ public Collection getNames() { return names; } private ClassInfo(Class srcClass, boolean ignoreCase) { clazz = srcClass; this.ignoreCase = ignoreCase; Preconditions.checkArgument( !ignoreCase || !srcClass.isEnum(), "cannot ignore case on an enum: " + srcClass); // name set has a special comparator to keep null first TreeSet nameSet = new TreeSet( new Comparator() { public int compare(String s0, String s1) { return Objects.equal(s0, s1) ? 0 : s0 == null ? -1 : s1 == null ? 1 : s0.compareTo(s1); } }); // iterate over declared fields for (Field field : srcClass.getDeclaredFields()) { FieldInfo fieldInfo = FieldInfo.of(field); if (fieldInfo == null) { continue; } String fieldName = fieldInfo.getName(); if (ignoreCase) { fieldName = fieldName.toLowerCase(Locale.US).intern(); } FieldInfo conflictingFieldInfo = nameToFieldInfoMap.get(fieldName); Preconditions.checkArgument( conflictingFieldInfo == null, "two fields have the same %sname <%s>: %s and %s", ignoreCase ? "case-insensitive " : "", fieldName, field, conflictingFieldInfo == null ? null : conflictingFieldInfo.getField()); nameToFieldInfoMap.put(fieldName, fieldInfo); nameSet.add(fieldName); } // inherit from super class and add only fields not already in nameToFieldInfoMap Class superClass = srcClass.getSuperclass(); if (superClass != null) { ClassInfo superClassInfo = ClassInfo.of(superClass, ignoreCase); nameSet.addAll(superClassInfo.names); for (Map.Entry e : superClassInfo.nameToFieldInfoMap.entrySet()) { String name = e.getKey(); if (!nameToFieldInfoMap.containsKey(name)) { nameToFieldInfoMap.put(name, e.getValue()); } } } names = nameSet.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(new ArrayList(nameSet)); } /** * Returns an unmodifiable collection of the {@code FieldInfo}s for this class, without any * guarantee of order. * *

If you need sorted order, instead use {@link #getNames()} with {@link * #getFieldInfo(String)}. * * @since 1.16 */ public Collection getFieldInfos() { return Collections.unmodifiableCollection(nameToFieldInfoMap.values()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy