/*==========================================================================*\
| $Id: Field.java,v 1.2 2011/06/09 15:31:24 stedwar2 Exp $
|*-------------------------------------------------------------------------*|
| Copyright (C) 2011 Virginia Tech
|
| This file is part of the Student-Library.
|
| The Student-Library is free software; you can redistribute it and/or
| modify it under the terms of the GNU Lesser General Public License as
| published by the Free Software Foundation; either version 3 of the
| License, or (at your option) any later version.
|
| The Student-Library is distributed in the hope that it will be useful,
| but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
| GNU Lesser General Public License for more details.
|
| You should have received a copy of the GNU Lesser General Public License
| along with the Student-Library; if not, see <http://www.gnu.org/licenses/>.
\*==========================================================================*/
package student.testingsupport.reflection;
import java.util.ArrayList;
import java.util.List;
import student.testingsupport.reflection.internal.Fields;
//-------------------------------------------------------------------------
/**
* A "filter" that represents a set of {@link java.lang.reflect.Field}
* objects meeting specified constraints. This class provides a fluent
* interface for specifying constraints on the set of fields, as well as
* common predicates useful for testing the properties of the set of fields.
* A getter and setter method is also provided. This class is simpler to
* use than {@link java.lang.reflect.Field}, without requiring any explicit
* exception handling.
*
* TODO: add full set of usage examples
*
* @param <FieldType> The type used to declare the corresponding field.
*
* @author Stephen Edwards
* @author Last changed by $Author: stedwar2 $
* @version $Revision: 1.2 $, $Date: 2011/06/09 15:31:24 $
*/
public class Field<FieldType>
extends NameFilter<Field<FieldType>, java.lang.reflect.Field>
{
//~ Constructor ...........................................................
// ----------------------------------------------------------
/**
* Create a new Field object that represents a field filter.
* @param previous The previous filter in the chain of filters.
* @param descriptionOfThisStage A description of this stage in the
* filter chain.
*/
protected Field(Field<FieldType> previous, String descriptionOfThisStage)
{
super(previous, descriptionOfThisStage);
}
//~ Public Methods ........................................................
// ----------------------------------------------------------
/**
* A starting point for field filters where the name is unspecified.
*/
public static final Field<Object> field = new Field<Object>(null, null);
// ----------------------------------------------------------
/**
* Create a new Field object.
* @param name The name of the field.
* @return a new Field filter object representing the named field.
*/
public static Field<Object> field(String name)
{
return field.withName(name);
}
// ----------------------------------------------------------
/**
* Create a new Field object from a {@link java.lang.reflect.Field}
* object.
* @param aField The field to represent.
* @return a new Field filter object representing the given field.
*/
public static Field<Object> field(java.lang.reflect.Field aField)
{
if (aField == null)
{
return field;
}
return field.createFreshFilter(aField);
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param type TODO: describe
* @param <T> TODO: describe
* @return TODO: describe
*/
@SuppressWarnings("unchecked")
public <T> Field<T> ofType(final Class<T> type)
{
if (type == null)
{
Field<T> result = (Field<T>)this;
return result;
}
return new Field<T>(
(Field<T>)this, "of type " + type.getCanonicalName())
{
// ----------------------------------------------------------
@Override
protected boolean thisFilterAccepts(java.lang.reflect.Field object)
{
return type.equals(object.getType());
}
};
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param type TODO: describe
* @param <T> TODO: describe
* @return TODO: describe
*/
public <T> Field<T> ofType(final Type<T> type)
{
if (type == null)
{
@SuppressWarnings("unchecked")
Field<T> result = (Field<T>)this;
return result;
}
// Note: This only works if the type has a single, unique match.
// If the type represents multiple possibilities, the NxM filtering
// is not performed.
// TODO: fix this to use the atLeastOne() quantifier
return ofType(type.raw());
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param type TODO: document.
* @return TODO: describe
*/
public boolean isOfType(final Type<?> type)
{
if (type == null)
{
return false;
}
else if (exists())
{
// TODO: fix this to use the atLeastOne() quantifier
return quantify.evaluate(new Predicate<java.lang.reflect.Field>()
{
public boolean isSatisfiedBy(java.lang.reflect.Field object)
{
return type.equals(object.getType());
}
});
}
else
{
return false;
}
}
// ----------------------------------------------------------
/**
* TODO: document.
* @return TODO: describe
*/
public Type<FieldType> getType()
{
@SuppressWarnings("unchecked")
Type<FieldType> result =
Type.type((Class<FieldType>)uniqueMatch().getType());
return result;
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param type TODO: describe
* @return TODO: describe
*/
public Field<FieldType> in(final Type<?> type)
{
if (type == null)
{
return this;
}
// TODO: needs to work for a type that represents a set of classes!
return in(type.raw());
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param type TODO: describe
* @return TODO: describe
*/
public Field<FieldType> in(final Class<?> type)
{
if (type == null)
{
return this;
}
return new Field<FieldType>(
this, "in " + type.getCanonicalName())
{
// ----------------------------------------------------------
@Override
protected List<java.lang.reflect.Field> candidatesFromThisFilter()
{
return Fields.fieldsIn(type);
}
};
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param receiver TODO: describe
* @return TODO: describe
*/
public Field<FieldType> in(final Object receiver)
{
if (receiver == null)
{
return this;
}
return new Field<FieldType>(in(receiver.getClass()),
"on receiver " + receiver)
{
// ----------------------------------------------------------
protected Object receiver()
{
return receiver;
}
};
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param type TODO: document.
* @return TODO: describe
*/
public boolean isIn(final Type<?> type)
{
if (type == null)
{
return false;
}
else if (exists())
{
return quantify.evaluate(new Predicate<java.lang.reflect.Field>()
{
public boolean isSatisfiedBy(java.lang.reflect.Field object)
{
return Type.type(object.getDeclaringClass())
.isAssignableFrom(type);
}
});
}
else
{
return false;
}
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param type TODO: document.
* @return TODO: describe
*/
public boolean isIn(Class<?> type)
{
if (type == null)
{
return false;
}
return isIn(Type.type(type));
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param receiver TODO: document.
* @return TODO: describe
*/
public boolean isIn(Object receiver)
{
if (receiver == null)
{
return false;
}
return isIn(Type.type(receiver.getClass()));
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param type TODO: describe
* @return TODO: describe
*/
public Field<FieldType> declaredIn(final Type<?> type)
{
if (type == null)
{
return this;
}
return declaredIn(type.raw());
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param type TODO: describe
* @return TODO: describe
*/
public Field<FieldType> declaredIn(final Class<?> type)
{
if (type == null)
{
return this;
}
return new Field<FieldType>(
this, "declared in " + type.getCanonicalName())
{
// ----------------------------------------------------------
@Override
protected List<java.lang.reflect.Field> candidatesFromThisFilter()
{
return Fields.fieldsDeclaredIn(type);
}
};
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param receiver TODO: describe
* @return TODO: describe
*/
public Field<FieldType> declaredIn(final Object receiver)
{
if (receiver == null)
{
return this;
}
return new Field<FieldType>(
declaredIn(receiver.getClass()),
"on receiver <" + receiver + ">")
{
// ----------------------------------------------------------
protected Object receiver()
{
return receiver;
}
};
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param type TODO: document.
* @return TODO: describe
*/
public boolean isDeclaredIn(final Type<?> type)
{
if (type == null)
{
return false;
}
else if (exists())
{
return quantify.evaluate(new Predicate<java.lang.reflect.Field>()
{
public boolean isSatisfiedBy(java.lang.reflect.Field object)
{
return Type.type(object.getDeclaringClass())
.equals(type);
}
});
}
else
{
return false;
}
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param type TODO: document.
* @return TODO: describe
*/
public boolean isDeclaredIn(Class<?> type)
{
if (type == null)
{
return false;
}
return isDeclaredIn(Type.type(type));
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param receiver TODO: document.
* @return TODO: describe
*/
public boolean isDeclaredIn(Object receiver)
{
if (receiver == null)
{
return false;
}
return isDeclaredIn(Type.type(receiver.getClass()));
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param type TODO: describe
* @return TODO: describe
*/
public Field<FieldType> visibleIn(final Type<?> type)
{
if (type == null)
{
return this;
}
return visibleIn(type.raw());
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param type TODO: describe
* @return TODO: describe
*/
public Field<FieldType> visibleIn(final Class<?> type)
{
if (type == null)
{
return this;
}
return new Field<FieldType>(
this, "visible in " + type.getCanonicalName())
{
// ----------------------------------------------------------
@Override
protected List<java.lang.reflect.Field> candidatesFromThisFilter()
{
return Fields.fieldsVisibleIn(type);
}
};
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param receiver TODO: describe
* @return TODO: describe
*/
public Field<FieldType> visibleIn(final Object receiver)
{
if (receiver == null)
{
return this;
}
return new Field<FieldType>(
visibleIn(receiver.getClass()),
"on receiver <" + receiver + ">")
{
// ----------------------------------------------------------
protected Object receiver()
{
return receiver;
}
};
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param type TODO: document.
* @return TODO: describe
*/
public boolean isVisibleIn(final Type<?> type)
{
if (type == null)
{
return false;
}
else if (exists())
{
return quantify.evaluate(new Predicate<java.lang.reflect.Field>()
{
public boolean isSatisfiedBy(java.lang.reflect.Field object)
{
return Type.type(object.getDeclaringClass())
.equals(type)
|| (Fields.isPackageVisible(object)
&& type.isInSamePackageAs(
object.getDeclaringClass()));
}
});
}
else
{
return false;
}
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param type TODO: document.
* @return TODO: describe
*/
public boolean isVisibleIn(Class<?> type)
{
if (type == null)
{
return false;
}
return isVisibleIn(Type.type(type));
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param receiver TODO: document.
* @return TODO: describe
*/
public boolean isVisibleIn(Object receiver)
{
if (receiver == null)
{
return false;
}
return isVisibleIn(Type.type(receiver.getClass()));
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param sourceType TODO: describe
* @return TODO: describe
*/
public Field<FieldType> assignableFrom(final Type<?> sourceType)
{
if (sourceType == null)
{
return this;
}
return new Field<FieldType>(
this, "assignable from " + sourceType.getName())
{
// ----------------------------------------------------------
@Override
protected boolean thisFilterAccepts(java.lang.reflect.Field object)
{
return Type.type(object.getType())
.isAssignableFrom(sourceType);
}
};
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param sourceType TODO: describe
* @return TODO: describe
*/
public boolean isAssignableFrom(final Type<?> sourceType)
{
if (sourceType == null)
{
return false;
}
return quantify.evaluate(new Predicate<java.lang.reflect.Field>()
{
public boolean isSatisfiedBy(java.lang.reflect.Field object)
{
return Type.type(object.getType())
.isAssignableFrom(sourceType);
};
});
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param sourceType TODO: describe
* @return TODO: describe
*/
public Field<FieldType> assignableFrom(final Class<?> sourceType)
{
if (sourceType == null)
{
return this;
}
return assignableFrom(Type.type(sourceType));
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param sourceType TODO: describe
* @return TODO: describe
*/
public boolean isAssignableFrom(final Class<?> sourceType)
{
if (sourceType == null)
{
return false;
}
return quantify.evaluate(new Predicate<java.lang.reflect.Field>()
{
private Type<?> source = Type.type(sourceType);
public boolean isSatisfiedBy(java.lang.reflect.Field object)
{
return Type.type(object.getType())
.isAssignableFrom(source);
};
});
}
// ----------------------------------------------------------
/**
* TODO: document.
* @return TODO: document.
*/
public FieldType get()
{
java.lang.reflect.Field field = raw();
field.setAccessible(true);
Object receiver = receiver();
if (receiver == null && !isStatic())
{
throw new ReflectionError(
"no receiver object provided for get() on non-static field "
+ field + ".");
}
try
{
@SuppressWarnings("unchecked")
FieldType result = (FieldType)field.get(receiver);
return result;
}
catch (IllegalAccessException e)
{
throw new ReflectionError("get() on field " + field
+ " produced an IllegalAccessException on receiver <"
+ receiver + ">: " + e.getMessage());
}
catch (IllegalArgumentException e)
{
throw new ReflectionError("get() on field " + field
+ " is not applicable to receiver <" + receiver
+ ">: " + e.getMessage());
}
}
// ----------------------------------------------------------
/**
* TODO: document.
* @param value TODO: document.
*/
public void set(FieldType value)
{
java.lang.reflect.Field field = raw();
field.setAccessible(true);
Object receiver = receiver();
if (receiver == null && !isStatic())
{
throw new ReflectionError(
"no receiver object provided for set() on non-static field "
+ field + ".");
}
try
{
field.set(receiver, value);
}
catch (IllegalAccessException e)
{
throw new ReflectionError("set() on field " + field
+ " produced an IllegalAccessException on receiver <"
+ receiver + ">: " + e.getMessage());
}
catch (IllegalArgumentException e)
{
throw new ReflectionError("set() on field " + field
+ " is not applicable to receiver <" + receiver
+ ">: " + e.getMessage());
}
}
//~ Protected Methods .....................................................
// ----------------------------------------------------------
@Override
protected Field<FieldType> createFreshFilter(
Field<FieldType> previous, String descriptionOfThisStage)
{
return new Field<FieldType>(previous, descriptionOfThisStage);
}
// ----------------------------------------------------------
protected Field<FieldType> createFreshFilter(
final java.lang.reflect.Field rawField)
{
return new Field<FieldType>(null, describe(rawField))
{
private List<java.lang.reflect.Field> result =
new ArrayList<java.lang.reflect.Field>();
{
result.add(rawField);
}
// ----------------------------------------------------------
@Override
protected List<java.lang.reflect.Field> candidatesFromThisFilter()
{
return result;
}
};
}
// ----------------------------------------------------------
@Override
protected String filteredObjectDescription()
{
return "field";
}
// ----------------------------------------------------------
@Override
protected String nameOf(java.lang.reflect.Field object)
{
return object.getName();
}
// ----------------------------------------------------------
@Override
protected int modifiersFor(java.lang.reflect.Field object)
{
return object.getModifiers();
}
// ----------------------------------------------------------
@Override
protected int defaultRequiredModifiers()
{
return 0;
}
// ----------------------------------------------------------
/**
* TODO: document.
* @return TODO: describe
*/
protected Object receiver()
{
return null;
}
}