package com.opencsv.bean;

import com.opencsv.bean.function.AccessorInvoker;
import com.opencsv.bean.function.AssignmentInvoker;
import org.apache.commons.lang3.reflect.FieldUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Optional;

Encapsulates the logic for accessing member variables of classes.

The logic in opencsv is always:

  1. Use an accessor method first, if available, and this always has the form "get"/"set" + member name with initial capital.
  2. If this accessor method is available but deals in Optional, wrap or unwrap as necessary. Empty Optionals lead to null return values, and null values lead to empty Optionals.
  3. Use reflection bypassing all access control restrictions.
These are considered separately for reading and writing.

Author:Andrew Rucker Jones
Type parameters:
  • <T> – The type of the member variable being accessed
Since:5.0
/** * Encapsulates the logic for accessing member variables of classes. * <p>The logic in opencsv is always:<ol> * <li>Use an accessor method first, if available, and this always has the * form "get"/"set" + member name with initial capital.</li> * <li>If this accessor method is available but deals in * {@link java.util.Optional}, wrap or unwrap as necessary. Empty * {@link java.util.Optional}s lead to {@code null} return values, and * {@code null} values lead to empty {@link java.util.Optional}s.</li> * <li>Use reflection bypassing all access control restrictions.</li> * </ol>These are considered separately for reading and writing.</p> * * @param <T> The type of the member variable being accessed * @author Andrew Rucker Jones * @since 5.0 */
public class FieldAccess<T> {
The field being accessed.
/** The field being accessed. */
private final Field field;
A functional interface to read the field.
/** A functional interface to read the field. */
private final AccessorInvoker<Object, T> accessor;
A functional interface to write the field.
/** A functional interface to write the field. */
private final AssignmentInvoker<Object, T> assignment;
Constructs this instance by determining what mode of access will work for this field.
Params:
  • field – The field to be accessed.
/** * Constructs this instance by determining what mode of access will work * for this field. * * @param field The field to be accessed. */
public FieldAccess(Field field) { this.field = field; accessor = determineAccessorMethod(); assignment = determineAssignmentMethod(); } @SuppressWarnings("unchecked") private AccessorInvoker<Object, T> determineAccessorMethod() { AccessorInvoker<Object, T> localAccessor; String getterName = "get" + Character.toUpperCase(field.getName().charAt(0)) + field.getName().substring(1); try { Method getterMethod = field.getDeclaringClass().getMethod(getterName); if(getterMethod.getReturnType().equals(Optional.class)) { localAccessor = bean -> { Optional<T> opt = (Optional<T>) getterMethod.invoke(bean); return opt.orElse(null); }; } else { localAccessor = bean -> (T) getterMethod.invoke(bean); } } catch (NoSuchMethodException e) { localAccessor = bean -> (T)FieldUtils.readField(this.field, bean, true); } return localAccessor; } private AssignmentInvoker<Object, T> determineAssignmentMethod() { AssignmentInvoker<Object, T> localAssignment; String setterName = "set" + Character.toUpperCase(field.getName().charAt(0)) + field.getName().substring(1); try { Method setterMethod = field.getDeclaringClass().getMethod(setterName, field.getType()); localAssignment = setterMethod::invoke; } catch (NoSuchMethodException e1) { try { Method setterMethod = field.getDeclaringClass().getMethod(setterName, Optional.class); localAssignment = (bean, value) -> setterMethod.invoke(bean, Optional.ofNullable(value)); } catch(NoSuchMethodException e2) { localAssignment = (bean, value) -> FieldUtils.writeField(this.field, bean, value, true); } } return localAssignment; }
Returns the value of the field in the given bean.
Params:
  • bean – The bean from which the value of this field should be returned
Throws:
Returns:The value of this member variable
/** * Returns the value of the field in the given bean. * @param bean The bean from which the value of this field should be returned * @return The value of this member variable * @throws IllegalAccessException If there is a problem accessing the * member variable * @throws InvocationTargetException If there is a problem accessing the * member variable */
public T getField(Object bean) throws IllegalAccessException, InvocationTargetException { return accessor.invoke(bean); }
Sets the value of the field in the given bean.
Params:
  • bean – The bean in which the value of the field should be set
  • value – The value to be written into the member variable of the bean
Throws:
/** * Sets the value of the field in the given bean. * @param bean The bean in which the value of the field should be set * @param value The value to be written into the member variable of the bean * @throws IllegalAccessException If there is a problem accessing the * member variable * @throws InvocationTargetException If there is a problem accessing the * member variable */
public void setField(Object bean, T value) throws IllegalAccessException, InvocationTargetException{ assignment.invoke(bean, value); }
Creates a hash code for this object. This override delegates hash code creation to the field passed in through the constructor and does not includes any of its own state information.
/** * Creates a hash code for this object. * This override delegates hash code creation to the field passed in * through the constructor and does not includes any of its own state * information. */
@Override public int hashCode() { return field.hashCode(); }
Determines equality between this object and another. This override delegates equality determination to the field passed in through the constructor and does not includes any of its own state information.
/** * Determines equality between this object and another. * This override delegates equality determination to the field passed in * through the constructor and does not includes any of its own state * information. */
@Override public boolean equals(Object obj) { if(!(obj instanceof FieldAccess)) { return false; } return field.equals(((FieldAccess)obj).field); }
Returns a string representation of this object. This override delegates the string representation to the field passed in through the constructor and does not includes any of its own state information.
/** * Returns a string representation of this object. * This override delegates the string representation to the field passed in * through the constructor and does not includes any of its own state * information. */
@Override public String toString() { return field.toString(); } }