/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.ows.util;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
/**
* Provides lookup information about java bean properties in a class.
*
* @author Justin Deoliveira, OpenGEO
* @author Andrea Aime, OpenGEO
*
*/
public class ClassProperties {
private static final Multimap<String, Method> EMPTY = ImmutableMultimap.of();
private static final Set<String> COMMON_DERIVED_PROPERTIES = new HashSet<>(
Arrays.asList("prefixedName"));
Multimap<String, Method> methods;
Multimap<String, Method> getters;
Multimap<String, Method> setters;
public ClassProperties(Class clazz) {
methods = Multimaps.newListMultimap(new TreeMap<>(String.CASE_INSENSITIVE_ORDER), () -> new ArrayList<>());
getters = Multimaps.newListMultimap(new TreeMap<>(String.CASE_INSENSITIVE_ORDER), () -> new ArrayList<>());
setters = Multimaps.newListMultimap(new TreeMap<>(String.CASE_INSENSITIVE_ORDER), () -> new ArrayList<>());
for (Method method : clazz.getMethods()) {
final String name = method.getName();
methods.put(name, method);
final Class<?>[] params = method.getParameterTypes();
if ((name.startsWith("get") || name.startsWith("is") || COMMON_DERIVED_PROPERTIES
.contains(name)) && params.length == 0) {
getters.put(gp(method), method);
} else if(name.startsWith("set") && params.length == 1) {
setters.put(name.substring(3), method);
}
}
// avoid keeping lots of useless empty arrays in memory for
// the long term, use just one
if(methods.size() == 0)
methods = EMPTY;
if(getters.size() == 0)
getters = EMPTY;
if(setters.size() == 0)
setters = EMPTY;
}
/**
* Returns a list of all the properties of the class.
*
* @return A list of string.
*/
public List<String> properties() {
ArrayList<String> properties = new ArrayList<String>();
for ( String key : getters.keySet() ) {
if(key.equals("Resource")) {
properties.add(0, key);
} else {
properties.add(key);
}
}
return properties;
}
/**
* Looks up a setter method by property name.
* <p>
* setter("foo",Integer) --> void setFoo(Integer);
* </p>
* @param property The property.
* @param type The type of the property.
*
* @return The setter for the property, or null if it does not exist.
*/
public Method setter(String property, Class type) {
Collection<Method> methods = setters.get(property);
for (Method setter : methods) {
if(type == null) {
return setter;
} else {
Class target = setter.getParameterTypes()[0];
if(target.isAssignableFrom(type) ||
(target.isPrimitive() && type == wrapper(target)) ||
(type.isPrimitive() && target == wrapper(type))) {
return setter;
}
}
}
// could not be found, try again with a more lax match
String lax = lax(property);
if (!lax.equals(property)) {
return setter(lax, type);
}
return null;
}
/**
* Looks up a getter method by its property name.
* <p>
* getter("foo",Integer) --> Integer getFoo();
* </p>
* @param property The property.
* @param type The type of the property.
*
* @return The getter for the property, or null if it does not exist.
*/
public Method getter(String property, Class type) {
Collection<Method> methods = getters.get(property);
if(methods != null) {
for (Method getter : methods) {
if(type == null) {
return getter;
} else {
Class target = getter.getReturnType();
if(type.isAssignableFrom(target) ||
(target.isPrimitive() && type == wrapper(target)) ||
(type.isPrimitive() && target == wrapper(type))) {
return getter;
}
}
}
}
// could not be found, try again with a more lax match
String lax = lax(property);
if (!lax.equals(property)) {
return getter(lax, type);
}
return null;
}
/**
* Does some checks on the property name to turn it into a java bean property.
* <p>
* Checks include collapsing any "_" characters.
* </p>
*/
static String lax(String property) {
return property.replaceAll("_", "");
}
/**
* Returns the wrapper class for a primitive class.
*
* @param primitive A primtive class, like int.class, double.class, etc...
*/
static Class wrapper( Class primitive ) {
if ( boolean.class == primitive ) {
return Boolean.class;
}
if ( char.class == primitive ) {
return Character.class;
}
if ( byte.class == primitive ) {
return Byte.class;
}
if ( short.class == primitive ) {
return Short.class;
}
if ( int.class == primitive ) {
return Integer.class;
}
if ( long.class == primitive ) {
return Long.class;
}
if ( float.class == primitive ) {
return Float.class;
}
if ( double.class == primitive ) {
return Double.class;
}
return null;
}
/**
* Looks up a method by name.
*/
public Method method(String name) {
Collection<Method> results = methods.get(name);
if(results.isEmpty()) {
return null;
} else {
return results.iterator().next();
}
}
/**
* Returns the name of the property corresponding to the getter method.
*/
String gp( Method getter ) {
String name = getter.getName();
if (COMMON_DERIVED_PROPERTIES.contains(name)) {
return name;
}
return name.substring(name.startsWith("get") ? 3 : 2);
}
}