Reputation: 11916
I have a class Foo that a property of the type Bar.
public class Foo {
public Bar getBar() {
}
}
public class Bar {
public String getName();
}
Is there a helper class or method that gets you a java.lang.reflect.Method
object of the name property of Bar
, using Foo.class
and "bar.name"?
There is a class called PropertyUtils
in Commons BeanUtils, but its getPropertyDescriptor()
only works for Object
instances, not Class
instances.
I realize it's not hard at all to implement one, but I'd like to leverage what's already available.
Also, the fact that I need a Method object is not a result of bad design(hope not). What I'm working on is pretty much a JavaBeans editor.
Thanks!
Upvotes: 2
Views: 4327
Reputation: 204
Got here by trying to transform a Spring Data Sort to a Comparator.
public class ComparatorUtils {
public static <T> Comparator<T> fromSort(Sort sort, Class<T> type) {
final Iterator<Sort.Order> orderIterator = sort.iterator();
final Sort.Order order = orderIterator.next();
Comparator<T> comparator = fromSortOrder(order, type);
while (orderIterator.hasNext()) {
comparator = comparator.thenComparing(fromSortOrder(orderIterator.next(), type));
}
return comparator;
}
private static <T> Comparator<T> fromSortOrder(Sort.Order order, Class<T> type) {
final List<Method> accessMethods = new ArrayList<>();
Class<?> currentClass = type;
for (String property : order.getProperty().split("\\.")) {
Method m = Objects.requireNonNull(BeanUtils.getPropertyDescriptor(currentClass, property)).getReadMethod();
accessMethods.add(m);
currentClass = m.getReturnType();
}
Comparator<T> comparator = Comparator.comparing((T entity) -> {
try {
Object result = entity;
for (Method method : accessMethods) {
result = method.invoke(result);
}
return (Comparable) result;
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}, Comparator.nullsLast(Comparator.naturalOrder()));
if (order.isDescending())
return comparator.reversed();
return comparator;
}
}
Upvotes: 0
Reputation: 389
Here is a version from djmj answer with Java 8 streams returning Optional if anybody is interested.
/**
* Retrieves the type of the property with the given name of the given Class.
*
* Supports nested properties following bean naming convention "foo.bar.name"
*
* @return Optional.empty if no property exists.
* @see PropertyUtils#getPropertyDescriptors(Class)
*/
public static Optional<Class<?>> findPropertyType(@NotNull Class<?> clazz, @NotBlank String propertyName) {
return Arrays.stream(propertyName.split("\\.")).reduce(
Optional.ofNullable(clazz), // identity -> initial value of the accumulator
(Optional<Class<?>> accOptClazz, String nextPropertyName) -> // accumulator with current value and next value from string
stream(accOptClazz)
.map((Class<?> accClazz) -> Arrays.stream(PropertyUtils.getPropertyDescriptors(accClazz)))
.flatMap(Function.identity())
.filter(propDesc -> propDesc.getName().equals(nextPropertyName))
.findFirst().map(PropertyDescriptor::getPropertyType),
(clazzA, clazzB) -> null // needed but useless combiner (only for parallel reduce)
);
}
/**
* Turns an Optional<T> into a Stream<T> of length zero or one depending upon whether a value is present.
*/
public static <T> Stream<T> stream(Optional<T> opt) {
return opt.isPresent() ? Stream.of(opt.get()) : Stream.empty();
}
Upvotes: 0
Reputation: 5544
Here you go for nested support: Depending on the use-case the retrieved classes could be cached. For instance in an datatable crud application where heavy filtering is used.
/**
* Retrieves the type of the property with the given name of the given
* Class.<br>
* Supports nested properties following bean naming convention.
*
* "foo.bar.name"
*
* @see PropertyUtils#getPropertyDescriptors(Class)
*
* @param clazz
* @param propertyName
*
* @return Null if no property exists.
*/
public static Class<?> getPropertyType(Class<?> clazz, String propertyName)
{
if (clazz == null)
throw new IllegalArgumentException("Clazz must not be null.");
if (propertyName == null)
throw new IllegalArgumentException("PropertyName must not be null.");
final String[] path = propertyName.split("\\.");
for (int i = 0; i < path.length; i++)
{
propertyName = path[i];
final PropertyDescriptor[] propDescs = PropertyUtils.getPropertyDescriptors(clazz);
for (final PropertyDescriptor propDesc : propDescs)
if (propDesc.getName().equals(propertyName))
{
clazz = propDesc.getPropertyType();
if (i == path.length - 1)
return clazz;
}
}
return null;
}
Upvotes: 3
Reputation: 14232
I'd go with MVEL or OGNL and skip the "I need the Method object" requirement.
Upvotes: 1
Reputation: 23958
In Commons BeanUtils, PropertyUtils.getPropertyDescriptors()
takes a Class
as input and returns an array of PropertyDescriptor
.
I don't know if it will return "nested" names such as bar.name
but if not, it shouldn't be too hard to recurse on the result and construct your own list of nested names.
Just a quick sanity check though... does the world really need yet another JavaBeans editor?
Upvotes: 1