package org.limewire.util;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* A helper class that makes setting and reverting the values of private fields easier,
* more consistent, and slightly safer.
* <p>
* NOTE: This class should be avoided at all costs. Currently it only exists for testing
* classes that use LimeWireUtils. For static classes reset should always be called prior
* to any conditions that might end execution of a test case.
*/
public class PrivateAccessor {
private final Field field;
private final Object instance;
private final Object originalValue;
private final Object fieldAccessor;
private final Method setMethod;
/**
* Constructs and initializes the PrivateAccessor. Does not allow overriding of final.
*
* @param clazz the Class object to access the field in
* @param instance the instance of the class, or null for static classes
* @param fieldName the name of a private field to access
*/
public PrivateAccessor(Class<?> clazz, Object instance, String fieldName) throws
SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException,
ClassNotFoundException, NoSuchMethodException, InvocationTargetException {
this(clazz, instance, fieldName, false);
}
/**
* Constructs and initializes the PrivateAccessor. Allows overriding of final.
*
* <p>Should not be used in real test cases, for adhoc testing only.
*
* @param clazz the Class object to access the field in
* @param instance the instance of the class, or null for static classes
* @param fieldName the name of a private field to access
* @param overrideFile allows overriding of final variables, use with
* caution final primitives and strings
* defined at compile time will usually be inlined and thus
* this will not have predictable results.
*/
@Deprecated
public PrivateAccessor(Class<?> clazz, Object instance, String fieldName, boolean overrideFinal)
throws SecurityException, NoSuchFieldException, IllegalArgumentException,
IllegalAccessException, ClassNotFoundException,
NoSuchMethodException, InvocationTargetException {
this.instance = instance;
field = clazz.getDeclaredField(fieldName);
if (!field.isAccessible()) {
field.setAccessible(true);
originalValue = field.get(instance);
field.setAccessible(false);
if (overrideFinal) {
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
int modifiers = modifiersField.getInt(field);
modifiers &= ~Modifier.FINAL;
modifiersField.setInt(field, modifiers);
Class<?> reflectionFactoryClass = Class.forName("sun.reflect.ReflectionFactory");
Method reflectionFactoryGetter = reflectionFactoryClass.getMethod("getReflectionFactory");
Object reflectionFactory = reflectionFactoryGetter.invoke(null);
Method createFieldAccessorMethod = reflectionFactory.getClass().getDeclaredMethod("newFieldAccessor",
Field.class, boolean.class);
fieldAccessor = createFieldAccessorMethod.invoke(reflectionFactory, field, false);
setMethod = fieldAccessor.getClass().getMethod("set", Object.class, Object.class);
setMethod.setAccessible(true);
}
else {
fieldAccessor = null;
setMethod = null;
}
}
else {
throw new IllegalArgumentException("PrivateAcessor is not used for accessable fields");
}
}
/**
* Resets the field to its original value.
*/
public void reset() {
try {
setValue(originalValue);
} catch (InvocationTargetException e) {
throw new IllegalStateException("Field should be resetable after a successful construction?", e);
}
}
/**
* Changes the field's value to the one provided.
*/
public void setValue(Object value) throws InvocationTargetException {
field.setAccessible(true);
try {
if (setMethod != null) {
setMethod.invoke(fieldAccessor, instance, value);
}
else {
field.set(instance, value);
}
} catch (IllegalAccessException e) {
throw new IllegalStateException("Field should be accessable after a successful construction?", e);
} finally {
field.setAccessible(false);
}
}
/**
* Returns the field's original value before any modifications were made.
*/
public Object getOriginalValue() {
return originalValue;
}
/**
* Returns the field's current value.
*/
public Object getValue() {
Object value = null;
field.setAccessible(true);
try {
value = field.get(instance);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Field should be accessable after a successful construction?");
} finally {
field.setAccessible(false);
}
return value;
}
}