ValueReference.java

package org.cyclopsgroup.caff.ref;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import javax.annotation.Nullable;

/**
 * Interface that allow to read a value from given owner or write value to owner
 *
 * @author <a href="mailto:jiaqi@cyclopsgroup.org">Jiaqi Guo</a>
 * @param <T> Type of owner object
 */
public abstract class ValueReference<T> {
  @Nullable
  private static Method findMethod(Class<?> type, String methodName, Class<?>... parameterTypes)
      throws SecurityException {
    try {
      return type.getDeclaredMethod(methodName, parameterTypes);
    } catch (NoSuchMethodException e) {
    }
    try {
      return type.getMethod(methodName, parameterTypes);
    } catch (NoSuchMethodException e) {
      return null;
    }
  }

  public static <T> ValueReference<T> forProperty(
      String propertyName, Class<T> type, Class<?> propertyType) {
    Preconditions.checkArgument(
        propertyName != null && !propertyName.isEmpty(), "Invalid property name " + propertyName);
    Preconditions.checkNotNull(type, "Bean type can't be null.");
    String name = Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
    Method getter = findMethod(type, "get" + name);
    if (getter != null) {
      Preconditions.checkArgument(
          propertyType.isAssignableFrom(getter.getReturnType()),
          "Property type is %s while getter returns %s.",
          propertyType,
          getter.getReturnType());
    }
    Method setter = findMethod(type, "set" + name, propertyType);
    return new PropertyValueReference<>(propertyName, propertyType, getter, setter);
  }

  /**
   * @param <T> Type of owner
   * @param field A reflective field
   * @return Value reference for given field
   */
  public static <T> ValueReference<T> instanceOf(Field field) {
    return new FieldValueReference<T>(field);
  }

  /**
   * @param <T> Type of owner
   * @param prop A Java bean field descriptor
   * @return Value reference for given descriptor
   */
  public static <T> ValueReference<T> instanceOf(PropertyDescriptor prop) {
    return new PropertyValueReference<T>(prop);
  }

  /**
   * Gets annotation with given type or null if not found.
   *
   * @param annotationType the type of annotation to find.
   * @param <A> the type of annotation to find and return.
   * @return The annotation that matches given type or null if nothing is found.
   */
  @Nullable
  public abstract <A extends Annotation> A getAnnotation(Class<A> annotationType);

  /**
   * Get all annotated elements.
   *
   * @return List of found annotated elements.
   */
  public abstract ImmutableList<AnnotatedElement> getAnontatedElements();

  /**
   * @return A unique name for this holder
   */
  public abstract String getName();

  /**
   * @return Type of value
   */
  public abstract Class<?> getType();

  /**
   * @return True if value really is readable
   */
  public abstract boolean isReadable();

  /**
   * @return True if value really is writable
   */
  public abstract boolean isWritable();

  /**
   * @param owner Object from which value is read
   * @return Reading result
   * @throws AccessFailureException If reading operation failed
   */
  public abstract Object readValue(T owner) throws AccessFailureException;

  /**
   * @param value Value to write to owner
   * @param owner Owner object where value is written to
   * @throws AccessFailureException If writing operation failed
   */
  public abstract void writeValue(Object value, T owner) throws AccessFailureException;
}