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

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

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

import com.google.common.base.Ascii;
import com.rt.storage.api.client.util.Data;
import com.rt.storage.api.client.util.NullValue;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;

/**
 * Parses field information to determine data key name/value pair associated with the field.
 *
 * 

Implementation is thread-safe. * * @since 1.0 * @author Yaniv Inbar */ public class FieldInfo { /** Cached field information. */ private static final Map CACHE = new WeakHashMap(); /** * Returns the field information for the given enum value. * * @param enumValue enum value * @return field information * @throws IllegalArgumentException if the enum value has no value annotation * @since 1.4 */ public static FieldInfo of(Enum enumValue) { try { FieldInfo result = FieldInfo.of(enumValue.getClass().getField(enumValue.name())); Preconditions.checkArgument( result != null, "enum constant missing @Value or @NullValue annotation: %s", enumValue); return result; } catch (NoSuchFieldException e) { // not possible throw new RuntimeException(e); } } /** * Returns the field information for the given field. * * @param field field or {@code null} for {@code null} result * @return field information or {@code null} if the field has no {@link #name} or for {@code null} * input */ public static FieldInfo of(Field field) { if (field == null) { return null; } synchronized (CACHE) { FieldInfo fieldInfo = CACHE.get(field); boolean isEnumContant = field.isEnumConstant(); if (fieldInfo == null && (isEnumContant || !Modifier.isStatic(field.getModifiers()))) { String fieldName; if (isEnumContant) { // check for @Value annotation Value value = field.getAnnotation(Value.class); if (value != null) { fieldName = value.value(); } else { // check for @NullValue annotation NullValue nullValue = field.getAnnotation(NullValue.class); if (nullValue != null) { fieldName = null; } else { // else ignore return null; } } } else { // check for @Key annotation Key key = field.getAnnotation(Key.class); if (key == null) { // else ignore return null; } fieldName = key.value(); field.setAccessible(true); } if ("##default".equals(fieldName)) { fieldName = field.getName(); } fieldInfo = new FieldInfo(field, fieldName); CACHE.put(field, fieldInfo); } return fieldInfo; } } /** Whether the field class is "primitive" as defined by {@link Data#isPrimitive(Type)}. */ private final boolean isPrimitive; /** Field. */ private final Field field; private final Method[] setters; /** * Data key name associated with the field for a non-enum-constant with a {@link Key} annotation, * or data key value associated with the enum constant with a {@link Value} annotation or {@code * null} for an enum constant with a {@link NullValue} annotation. * *

This string is interned. */ private final String name; FieldInfo(Field field, String name) { this.field = field; this.name = name == null ? null : name.intern(); isPrimitive = Data.isPrimitive(getType()); this.setters = settersMethodForField(field); } /** Creates list of setter methods for a field only in declaring class. */ private Method[] settersMethodForField(Field field) { List methods = new ArrayList<>(); for (Method method : field.getDeclaringClass().getDeclaredMethods()) { if (Ascii.toLowerCase(method.getName()).equals("set" + Ascii.toLowerCase(field.getName())) && method.getParameterTypes().length == 1) { methods.add(method); } } return methods.toArray(new Method[0]); } /** * Returns the field. * * @since 1.4 */ public Field getField() { return field; } /** * Returns the data key name associated with the field for a non-enum-constant with a {@link Key} * annotation, or data key value associated with the enum constant with a {@link Value} annotation * or {@code null} for an enum constant with a {@link NullValue} annotation. * *

This string is interned. * * @since 1.4 */ public String getName() { return name; } /** * Returns the field's type. * * @since 1.4 */ public Class getType() { return field.getType(); } /** * Returns the field's generic type, which is a class, parameterized type, generic array type, or * type variable, but not a wildcard type. * * @since 1.4 */ public Type getGenericType() { return field.getGenericType(); } /** * Returns whether the field is final. * * @since 1.4 */ public boolean isFinal() { return Modifier.isFinal(field.getModifiers()); } /** * Returns whether the field is primitive as defined by {@link Data#isPrimitive(Type)}. * * @since 1.4 */ public boolean isPrimitive() { return isPrimitive; } /** Returns the value of the field in the given object instance using reflection. */ public Object getValue(Object obj) { return getFieldValue(field, obj); } /** * Sets this field in the given object to the given value using reflection. * *

If the field is final, it checks that the value being set is identical to the existing * value. */ public void setValue(Object obj, Object value) { if (setters.length > 0) { for (Method method : setters) { if (value == null || method.getParameterTypes()[0].isAssignableFrom(value.getClass())) { try { method.invoke(obj, value); return; } catch (IllegalAccessException | InvocationTargetException e) { // try to set field directly } } } } setFieldValue(field, obj, value); } /** Returns the class information of the field's declaring class. */ public ClassInfo getClassInfo() { return ClassInfo.of(field.getDeclaringClass()); } @SuppressWarnings("unchecked") public > T enumValue() { return Enum.valueOf((Class) field.getDeclaringClass(), field.getName()); } /** Returns the value of the given field in the given object using reflection. */ public static Object getFieldValue(Field field, Object obj) { try { return field.get(obj); } catch (IllegalAccessException e) { throw new IllegalArgumentException(e); } } /** * Sets the given field in the given object to the given value using reflection. * *

If the field is final, it checks that the value being set is identical to the existing * value. */ public static void setFieldValue(Field field, Object obj, Object value) { if (Modifier.isFinal(field.getModifiers())) { Object finalValue = getFieldValue(field, obj); if (value == null ? finalValue != null : !value.equals(finalValue)) { throw new IllegalArgumentException( "expected final value <" + finalValue + "> but was <" + value + "> on " + field.getName() + " field in " + obj.getClass().getName()); } } else { try { field.set(obj, value); } catch (SecurityException e) { throw new IllegalArgumentException(e); } catch (IllegalAccessException e) { throw new IllegalArgumentException(e); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy