Big update. Bug fixes, code re-formatting, changing tests.
This commit is contained in:
@@ -10,7 +10,9 @@ public interface Handler<T, V> {
|
||||
/**
|
||||
* Accept an argument and possibly produce a result.
|
||||
*
|
||||
* @param t arg
|
||||
* @param t
|
||||
* arg
|
||||
*
|
||||
* @return Some object or {@literal null} if no result.
|
||||
*/
|
||||
V handle(T t);
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.springframework.data.rest.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonProperty;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class Links {
|
||||
|
||||
private List<Link> links = new ArrayList<Link>();
|
||||
|
||||
public Links add(Link link) {
|
||||
links.add(link);
|
||||
return this;
|
||||
}
|
||||
|
||||
@JsonProperty("_links")
|
||||
public List<Link> getLinks() {
|
||||
return this.links;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,10 +7,11 @@ import java.net.URI;
|
||||
*
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class SimpleLink implements Link {
|
||||
public class SimpleLink
|
||||
implements Link {
|
||||
|
||||
private String rel;
|
||||
private URI href;
|
||||
private URI href;
|
||||
|
||||
public SimpleLink() {
|
||||
}
|
||||
|
||||
@@ -7,9 +7,14 @@ import org.springframework.core.convert.ConverterNotFoundException;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
* This {@link ConversionService} implementation delegates the actual conversion the ConversionService if finds in its
|
||||
* internal List that claims to be able to convert a given class. It will roll through the internal <code>Stack</code>
|
||||
* of <code>ConversionService</code>s until it finds one that can convert the given type.
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
public class DelegatingConversionService implements ConversionService {
|
||||
public class DelegatingConversionService
|
||||
implements ConversionService {
|
||||
|
||||
private Stack<ConversionService> conversionServices = new Stack<ConversionService>();
|
||||
|
||||
@@ -20,21 +25,39 @@ public class DelegatingConversionService implements ConversionService {
|
||||
addConversionServices(svcs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add {@link ConversionService}s to the internal list of those to delegate to.
|
||||
*
|
||||
* @param svcs
|
||||
* The ConversionServices to delegate to (in order).
|
||||
*
|
||||
* @return @this
|
||||
*/
|
||||
public DelegatingConversionService addConversionServices(ConversionService... svcs) {
|
||||
for (ConversionService svc : svcs) {
|
||||
for(ConversionService svc : svcs) {
|
||||
conversionServices.add(svc);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@link ConversionService} to the internal list at a specific index for controlling the priority.
|
||||
*
|
||||
* @param atIndex
|
||||
* Where in the stack to add this ConversionService.
|
||||
* @param svc
|
||||
* The ConversionService to add.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public DelegatingConversionService addConversionService(int atIndex, ConversionService svc) {
|
||||
conversionServices.add(atIndex, svc);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public boolean canConvert(Class<?> from, Class<?> to) {
|
||||
for (ConversionService svc : conversionServices) {
|
||||
if (svc.canConvert(from, to)) {
|
||||
for(ConversionService svc : conversionServices) {
|
||||
if(svc.canConvert(from, to)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -42,8 +65,8 @@ public class DelegatingConversionService implements ConversionService {
|
||||
}
|
||||
|
||||
@Override public boolean canConvert(TypeDescriptor from, TypeDescriptor to) {
|
||||
for (ConversionService svc : conversionServices) {
|
||||
if (svc.canConvert(from, to)) {
|
||||
for(ConversionService svc : conversionServices) {
|
||||
if(svc.canConvert(from, to)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -51,8 +74,8 @@ public class DelegatingConversionService implements ConversionService {
|
||||
}
|
||||
|
||||
@Override public <T> T convert(Object o, Class<T> type) {
|
||||
for (ConversionService svc : conversionServices) {
|
||||
if (svc.canConvert(o.getClass(), type)) {
|
||||
for(ConversionService svc : conversionServices) {
|
||||
if(svc.canConvert(o.getClass(), type)) {
|
||||
return svc.convert(o, type);
|
||||
}
|
||||
}
|
||||
@@ -60,8 +83,8 @@ public class DelegatingConversionService implements ConversionService {
|
||||
}
|
||||
|
||||
@Override public Object convert(Object o, TypeDescriptor from, TypeDescriptor to) {
|
||||
for (ConversionService svc : conversionServices) {
|
||||
if (svc.canConvert(from, to)) {
|
||||
for(ConversionService svc : conversionServices) {
|
||||
if(svc.canConvert(from, to)) {
|
||||
return svc.convert(o, from, to);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ import org.springframework.core.convert.converter.Converter;
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class StringToUUIDConverter implements Converter<String, UUID> {
|
||||
public class StringToUUIDConverter
|
||||
implements Converter<String, UUID> {
|
||||
@Override public UUID convert(String s) {
|
||||
return (null != s ? UUID.fromString(s) : null);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ import org.springframework.core.convert.converter.Converter;
|
||||
/**
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class UUIDToStringConverter implements Converter<UUID, String> {
|
||||
public class UUIDToStringConverter
|
||||
implements Converter<UUID, String> {
|
||||
@Override public String convert(UUID uuid) {
|
||||
return (null != uuid ? uuid.toString() : null);
|
||||
}
|
||||
|
||||
@@ -30,13 +30,14 @@ public abstract class BeanUtils {
|
||||
|
||||
public static ConfigurableConversionService CONVERSION_SERVICE = new DefaultConversionService();
|
||||
|
||||
private static final LoadingCache<Object[], Field> fields = CacheBuilder.newBuilder().build(
|
||||
private static final LoadingCache<Object[], Field> fields = CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<Object[], Field>() {
|
||||
@Override public Field load(Object[] key) throws Exception {
|
||||
Class<?> clazz = (Class<?>) key[0];
|
||||
String name = (String) key[1];
|
||||
@Override public Field load(Object[] key)
|
||||
throws Exception {
|
||||
Class<?> clazz = (Class<?>)key[0];
|
||||
String name = (String)key[1];
|
||||
Field f = ReflectionUtils.findField(clazz, name);
|
||||
if (null != f) {
|
||||
if(null != f) {
|
||||
ReflectionUtils.makeAccessible(f);
|
||||
return f;
|
||||
} else {
|
||||
@@ -47,14 +48,15 @@ public abstract class BeanUtils {
|
||||
);
|
||||
private static final LoadingCache<Object[], Method> methods = CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<Object[], Method>() {
|
||||
@Override public Method load(Object[] key) throws Exception {
|
||||
Class<?> clazz = (Class<?>) key[0];
|
||||
String name = (String) key[1];
|
||||
Integer paramCnt = key.length == 3 ? (Integer) key[2] : 0;
|
||||
@Override public Method load(Object[] key)
|
||||
throws Exception {
|
||||
Class<?> clazz = (Class<?>)key[0];
|
||||
String name = (String)key[1];
|
||||
Integer paramCnt = key.length == 3 ? (Integer)key[2] : 0;
|
||||
|
||||
for (Method m : clazz.getDeclaredMethods()) {
|
||||
if (m.getName().equals(name)) {
|
||||
if (m.getParameterTypes().length == paramCnt) {
|
||||
for(Method m : clazz.getDeclaredMethods()) {
|
||||
if(m.getName().equals(name)) {
|
||||
if(m.getParameterTypes().length == paramCnt) {
|
||||
ReflectionUtils.makeAccessible(m);
|
||||
return m;
|
||||
}
|
||||
@@ -67,28 +69,28 @@ public abstract class BeanUtils {
|
||||
);
|
||||
|
||||
public static boolean hasProperty(String property, Object... objs) {
|
||||
for (Object obj : objs) {
|
||||
if (obj instanceof Map) {
|
||||
return ((Map) obj).containsKey(property);
|
||||
for(Object obj : objs) {
|
||||
if(obj instanceof Map) {
|
||||
return ((Map)obj).containsKey(property);
|
||||
}
|
||||
Class<?> type = obj.getClass();
|
||||
try {
|
||||
if (FluentBeanUtils.isFluentBean(type)) {
|
||||
if(FluentBeanUtils.isFluentBean(type)) {
|
||||
return null != methods.get(new Object[]{type, property});
|
||||
} else {
|
||||
if (null == methods.get(new Object[]{type, "get" + StringUtils.capitalize(property)})) {
|
||||
if(null == methods.get(new Object[]{type, "get" + StringUtils.capitalize(property)})) {
|
||||
return null != fields.get(new Object[]{type, property});
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (UncheckedExecutionException e) {
|
||||
if (e.getCause().getClass() == IllegalArgumentException.class) {
|
||||
} catch(UncheckedExecutionException e) {
|
||||
if(e.getCause().getClass() == IllegalArgumentException.class) {
|
||||
return false;
|
||||
} else {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
} catch (ExecutionException e) {
|
||||
} catch(ExecutionException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
@@ -97,9 +99,9 @@ public abstract class BeanUtils {
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public static <T> T findFirst(Class<T> clazz, List<?> stack) {
|
||||
for (Object o : stack) {
|
||||
if (ClassUtils.isAssignable(clazz, o.getClass())) {
|
||||
return (T) o;
|
||||
for(Object o : stack) {
|
||||
if(ClassUtils.isAssignable(clazz, o.getClass())) {
|
||||
return (T)o;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -107,44 +109,44 @@ public abstract class BeanUtils {
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public static Object findFirst(Object o, Object... objs) {
|
||||
for (Object obj : objs) {
|
||||
if (o == obj || null != o && o.equals(obj)) {
|
||||
for(Object obj : objs) {
|
||||
if(o == obj || null != o && o.equals(obj)) {
|
||||
return obj;
|
||||
} else if (obj instanceof List) {
|
||||
return Collections.binarySearch((List) obj, o);
|
||||
} else if (obj instanceof Object[]) {
|
||||
return Arrays.binarySearch((Object[]) obj, o);
|
||||
} else if(obj instanceof List) {
|
||||
return Collections.binarySearch((List)obj, o);
|
||||
} else if(obj instanceof Object[]) {
|
||||
return Arrays.binarySearch((Object[])obj, o);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Object findFirst(String property, Object... objs) {
|
||||
for (Object obj : objs) {
|
||||
if (obj instanceof Map) {
|
||||
return ((Map) obj).get(property);
|
||||
for(Object obj : objs) {
|
||||
if(obj instanceof Map) {
|
||||
return ((Map)obj).get(property);
|
||||
}
|
||||
Class<?> type = obj.getClass();
|
||||
try {
|
||||
Field f = fields.get(new Object[]{type, property});
|
||||
if (FluentBeanUtils.isFluentBean(type)) {
|
||||
if(FluentBeanUtils.isFluentBean(type)) {
|
||||
return FluentBeanUtils.get(property, obj);
|
||||
} else {
|
||||
Method getter = methods.get(new Object[]{type, "get" + StringUtils.capitalize(property)});
|
||||
try {
|
||||
if (null != getter) {
|
||||
if(null != getter) {
|
||||
return getter.invoke(obj);
|
||||
} else {
|
||||
return f.get(obj);
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
} catch(IllegalAccessException e) {
|
||||
throw new IllegalStateException(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
} catch(InvocationTargetException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
} catch (ExecutionException e) {
|
||||
} catch(IllegalArgumentException e) {
|
||||
} catch(ExecutionException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
@@ -157,8 +159,8 @@ public abstract class BeanUtils {
|
||||
}
|
||||
|
||||
public static boolean containsType(Class<?> type, Object[] objs) {
|
||||
for (Object obj : objs) {
|
||||
if (null != obj && ClassUtils.isAssignable(obj.getClass(), type)) {
|
||||
for(Object obj : objs) {
|
||||
if(null != obj && ClassUtils.isAssignable(obj.getClass(), type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -172,7 +174,7 @@ public abstract class BeanUtils {
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public static <T> T invoke(String methodName, Object target, Class<T> returnType, Object... args) {
|
||||
if (null == target) {
|
||||
if(null == target) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -181,11 +183,11 @@ public abstract class BeanUtils {
|
||||
Method m = methods.get(new Object[]{type, methodName, args.length});
|
||||
List<Object> newArgs = new ArrayList<Object>(args.length);
|
||||
Class<?>[] paramTypes = m.getParameterTypes();
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
for(int i = 0; i < args.length; i++) {
|
||||
Object o = args[i];
|
||||
Class<?> oType = o.getClass();
|
||||
Class<?> pType = paramTypes[i];
|
||||
if (!ClassUtils.isAssignable(oType, pType)) {
|
||||
if(!ClassUtils.isAssignable(oType, pType)) {
|
||||
newArgs.add(CONVERSION_SERVICE.convert(o, pType));
|
||||
} else {
|
||||
newArgs.add(o);
|
||||
@@ -193,15 +195,15 @@ public abstract class BeanUtils {
|
||||
}
|
||||
|
||||
Object rtnVal = m.invoke(target, newArgs.toArray());
|
||||
if ((returnType != Void.TYPE || returnType != Object.class)
|
||||
if((returnType != Void.TYPE || returnType != Object.class)
|
||||
&& null != rtnVal
|
||||
&& !ClassUtils.isAssignable(returnType, rtnVal.getClass())) {
|
||||
return CONVERSION_SERVICE.convert(rtnVal, returnType);
|
||||
} else {
|
||||
return (T) rtnVal;
|
||||
return (T)rtnVal;
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
} catch (Exception e) {
|
||||
} catch(IllegalArgumentException e) {
|
||||
} catch(Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,9 +18,10 @@ import org.springframework.util.ClassUtils;
|
||||
*
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class FluentBeanDeserializer extends StdDeserializer {
|
||||
public class FluentBeanDeserializer
|
||||
extends StdDeserializer {
|
||||
|
||||
private ConversionService conversionService;
|
||||
private ConversionService conversionService;
|
||||
private FluentBeanUtils.Metadata beanMeta;
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@@ -29,7 +30,7 @@ public class FluentBeanDeserializer extends StdDeserializer {
|
||||
this.conversionService = conversionService;
|
||||
this.beanMeta = FluentBeanUtils.metadata(valueClass);
|
||||
|
||||
if (!FluentBeanUtils.isFluentBean(valueClass)) {
|
||||
if(!FluentBeanUtils.isFluentBean(valueClass)) {
|
||||
throw new IllegalArgumentException("Class of type " + valueClass + " is not a FluentBean");
|
||||
}
|
||||
}
|
||||
@@ -39,46 +40,46 @@ public class FluentBeanDeserializer extends StdDeserializer {
|
||||
DeserializationContext ctxt)
|
||||
throws IOException,
|
||||
JsonProcessingException {
|
||||
if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
|
||||
if(jp.getCurrentToken() != JsonToken.START_OBJECT) {
|
||||
throw ctxt.mappingException(_valueClass);
|
||||
}
|
||||
|
||||
Object bean;
|
||||
try {
|
||||
bean = _valueClass.newInstance();
|
||||
} catch (InstantiationException e) {
|
||||
} catch(InstantiationException e) {
|
||||
throw new IllegalStateException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
} catch(IllegalAccessException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
while (jp.nextToken() != JsonToken.END_OBJECT) {
|
||||
while(jp.nextToken() != JsonToken.END_OBJECT) {
|
||||
String name = jp.getCurrentName();
|
||||
Method setter = beanMeta.setters().get(name);
|
||||
|
||||
Object obj;
|
||||
if (null != setter) {
|
||||
if(null != setter) {
|
||||
Class<?> targetType = setter.getParameterTypes()[0];
|
||||
if (ClassUtils.isAssignable(targetType, Long.class)) {
|
||||
if(ClassUtils.isAssignable(targetType, Long.class)) {
|
||||
obj = jp.nextLongValue(-1);
|
||||
} else if (ClassUtils.isAssignable(targetType, Integer.class)) {
|
||||
} else if(ClassUtils.isAssignable(targetType, Integer.class)) {
|
||||
obj = jp.nextIntValue(-1);
|
||||
} else if (ClassUtils.isAssignable(targetType, Boolean.class)) {
|
||||
} else if(ClassUtils.isAssignable(targetType, Boolean.class)) {
|
||||
obj = jp.nextBooleanValue();
|
||||
} else {
|
||||
obj = jp.nextTextValue();
|
||||
}
|
||||
|
||||
if (null != obj) {
|
||||
if (!ClassUtils.isAssignable(obj.getClass(), targetType)) {
|
||||
if(null != obj) {
|
||||
if(!ClassUtils.isAssignable(obj.getClass(), targetType)) {
|
||||
obj = conversionService.convert(obj, targetType);
|
||||
}
|
||||
|
||||
try {
|
||||
setter.invoke(bean, obj);
|
||||
} catch (IllegalAccessException e) {
|
||||
} catch(IllegalAccessException e) {
|
||||
throw new IllegalStateException(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
} catch(InvocationTargetException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,60 +16,62 @@ import org.springframework.util.ClassUtils;
|
||||
*
|
||||
* @author Jon Brisbin <jbrisbin@vmware.com>
|
||||
*/
|
||||
public class FluentBeanSerializer extends SerializerBase<Object> {
|
||||
public class FluentBeanSerializer
|
||||
extends SerializerBase<Object> {
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public FluentBeanSerializer( final Class t ) {
|
||||
super( t );
|
||||
public FluentBeanSerializer(final Class t) {
|
||||
super(t);
|
||||
|
||||
if ( !FluentBeanUtils.isFluentBean( t ) ) {
|
||||
throw new IllegalArgumentException( "Class of type " + t + " is not a FluentBean" );
|
||||
if(!FluentBeanUtils.isFluentBean(t)) {
|
||||
throw new IllegalArgumentException("Class of type " + t + " is not a FluentBean");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@Override
|
||||
public void serialize( final Object value,
|
||||
final JsonGenerator jgen,
|
||||
final SerializerProvider provider )
|
||||
public void serialize(final Object value,
|
||||
final JsonGenerator jgen,
|
||||
final SerializerProvider provider)
|
||||
throws IOException,
|
||||
JsonGenerationException {
|
||||
if ( null == value ) {
|
||||
provider.defaultSerializeNull( jgen );
|
||||
if(null == value) {
|
||||
provider.defaultSerializeNull(jgen);
|
||||
} else {
|
||||
Class<?> type = value.getClass();
|
||||
if ( ClassUtils.isAssignable( type, Collection.class ) ) {
|
||||
if(ClassUtils.isAssignable(type, Collection.class)) {
|
||||
jgen.writeStartArray();
|
||||
for ( Object o : (Collection) value ) {
|
||||
write( o, jgen, provider );
|
||||
for(Object o : (Collection)value) {
|
||||
write(o, jgen, provider);
|
||||
}
|
||||
jgen.writeEndArray();
|
||||
} else if ( ClassUtils.isAssignable( type, Map.class ) ) {
|
||||
} else if(ClassUtils.isAssignable(type, Map.class)) {
|
||||
jgen.writeStartObject();
|
||||
for ( Map.Entry<String, Object> entry : ((Map<String, Object>) value).entrySet() ) {
|
||||
jgen.writeFieldName( entry.getKey() );
|
||||
write( entry.getValue(), jgen, provider );
|
||||
for(Map.Entry<String, Object> entry : ((Map<String, Object>)value).entrySet()) {
|
||||
jgen.writeFieldName(entry.getKey());
|
||||
write(entry.getValue(), jgen, provider);
|
||||
}
|
||||
jgen.writeEndObject();
|
||||
} else {
|
||||
write( value, jgen, provider );
|
||||
write(value, jgen, provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void write( final Object value,
|
||||
final JsonGenerator jgen,
|
||||
final SerializerProvider provider ) throws IOException {
|
||||
private void write(final Object value,
|
||||
final JsonGenerator jgen,
|
||||
final SerializerProvider provider)
|
||||
throws IOException {
|
||||
Class<?> type = value.getClass();
|
||||
if ( ClassUtils.isAssignable( type, _handledType ) ) {
|
||||
if(ClassUtils.isAssignable(type, _handledType)) {
|
||||
jgen.writeStartObject();
|
||||
for ( String fname : FluentBeanUtils.metadata( type ).fieldNames() ) {
|
||||
jgen.writeFieldName( fname );
|
||||
write( FluentBeanUtils.get( fname, value ), jgen, provider );
|
||||
for(String fname : FluentBeanUtils.metadata(type).fieldNames()) {
|
||||
jgen.writeFieldName(fname);
|
||||
write(FluentBeanUtils.get(fname, value), jgen, provider);
|
||||
}
|
||||
jgen.writeEndObject();
|
||||
} else {
|
||||
jgen.writeObject( value );
|
||||
jgen.writeObject(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,24 +22,27 @@ import org.springframework.util.ReflectionUtils;
|
||||
*/
|
||||
public abstract class FluentBeanUtils {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(FluentBeanUtils.class);
|
||||
private static final Logger log = LoggerFactory.getLogger(FluentBeanUtils.class);
|
||||
private static final LoadingCache<Class<?>, Metadata> metadata = CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<Class<?>, Metadata>() {
|
||||
@Override public Metadata load(Class<?> type) throws Exception {
|
||||
@Override public Metadata load(Class<?> type)
|
||||
throws Exception {
|
||||
final Metadata meta = new Metadata();
|
||||
ReflectionUtils.doWithFields(
|
||||
type,
|
||||
new ReflectionUtils.FieldCallback() {
|
||||
@Override public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
|
||||
@Override public void doWith(Field field)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
final String fname = field.getName();
|
||||
if (!fname.startsWith("_")) {
|
||||
if(!fname.startsWith("_")) {
|
||||
ReflectionUtils.doWithMethods(field.getDeclaringClass(), new ReflectionUtils.MethodCallback() {
|
||||
@Override
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
if (method.getName().equals(fname)) {
|
||||
if (method.getParameterTypes().length == 0) {
|
||||
public void doWith(Method method)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
if(method.getName().equals(fname)) {
|
||||
if(method.getParameterTypes().length == 0) {
|
||||
meta.getters.put(fname, method);
|
||||
} else if (method.getParameterTypes().length == 1) {
|
||||
} else if(method.getParameterTypes().length == 1) {
|
||||
meta.setters.put(fname, method);
|
||||
}
|
||||
meta.fieldNames.add(fname);
|
||||
@@ -58,13 +61,15 @@ public abstract class FluentBeanUtils {
|
||||
/**
|
||||
* Interrogate a bean and collect {@link Metadata} on it.
|
||||
*
|
||||
* @param targetType The type to interrogate.
|
||||
* @param targetType
|
||||
* The type to interrogate.
|
||||
*
|
||||
* @return {@link Metadata} for the fluent bean.
|
||||
*/
|
||||
public static Metadata metadata(Class<?> targetType) {
|
||||
try {
|
||||
return metadata.get(targetType);
|
||||
} catch (ExecutionException e) {
|
||||
} catch(ExecutionException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
@@ -72,27 +77,31 @@ public abstract class FluentBeanUtils {
|
||||
/**
|
||||
* Set the property of a fluent bean.
|
||||
*
|
||||
* @param property Name of the property to set.
|
||||
* @param value Value of the property.
|
||||
* @param bean Bean on which to set this property.
|
||||
* @param property
|
||||
* Name of the property to set.
|
||||
* @param value
|
||||
* Value of the property.
|
||||
* @param bean
|
||||
* Bean on which to set this property.
|
||||
*
|
||||
* @return Usually {@literal null} but will return whatever the "setter" returns, which could be {@this} or something
|
||||
* else.
|
||||
*/
|
||||
public static Object set(String property, Object value, Object bean) {
|
||||
if (null == bean) {
|
||||
if(null == bean) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<?> type = bean.getClass();
|
||||
try {
|
||||
Method setter = metadata.get(type).setters.get(property);
|
||||
if (null != setter) {
|
||||
if(null != setter) {
|
||||
return setter.invoke(bean, value);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
if (log.isDebugEnabled()) {
|
||||
} catch(Throwable t) {
|
||||
if(log.isDebugEnabled()) {
|
||||
log.debug(t.getMessage(), t);
|
||||
}
|
||||
return null;
|
||||
@@ -102,25 +111,28 @@ public abstract class FluentBeanUtils {
|
||||
/**
|
||||
* Get the value of a property.
|
||||
*
|
||||
* @param property Name of the property.
|
||||
* @param bean Bean of which to get the property.
|
||||
* @param property
|
||||
* Name of the property.
|
||||
* @param bean
|
||||
* Bean of which to get the property.
|
||||
*
|
||||
* @return Value of the property. Could be {@literal null}
|
||||
*/
|
||||
public static Object get(String property, Object bean) {
|
||||
if (null == bean) {
|
||||
if(null == bean) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<?> type = bean.getClass();
|
||||
try {
|
||||
Method getter = metadata.get(type).getters.get(property);
|
||||
if (null != getter) {
|
||||
if(null != getter) {
|
||||
return getter.invoke(bean);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
if (log.isDebugEnabled()) {
|
||||
} catch(Throwable t) {
|
||||
if(log.isDebugEnabled()) {
|
||||
log.debug(t.getMessage(), t);
|
||||
}
|
||||
return null;
|
||||
@@ -132,21 +144,23 @@ public abstract class FluentBeanUtils {
|
||||
* to a field of the same name. A "getter" is that method which is named the same as the field and has 0 parameters.
|
||||
* The "setter" is that method which is named the same as the field and has a single argument.
|
||||
*
|
||||
* @param type The class to inspect.
|
||||
* @param type
|
||||
* The class to inspect.
|
||||
*
|
||||
* @return {@literal true} if this looks like a fluent bean, {@literal false} otherwise.
|
||||
*/
|
||||
public static boolean isFluentBean(Class<?> type) {
|
||||
try {
|
||||
return metadata.get(type).getters.size() > 0;
|
||||
} catch (ExecutionException e) {
|
||||
} catch(ExecutionException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Metadata {
|
||||
List<String> fieldNames = new ArrayList<String>();
|
||||
Map<String, Method> getters = new HashMap<String, Method>();
|
||||
Map<String, Method> setters = new HashMap<String, Method>();
|
||||
List<String> fieldNames = new ArrayList<String>();
|
||||
Map<String, Method> getters = new HashMap<String, Method>();
|
||||
Map<String, Method> setters = new HashMap<String, Method>();
|
||||
|
||||
public List<String> fieldNames() {
|
||||
return fieldNames;
|
||||
|
||||
@@ -24,8 +24,11 @@ public abstract class UriUtils {
|
||||
* http://localhost:8080/data/person}, this method would report the baseUri being a valid base of the given URI.
|
||||
* </p>
|
||||
*
|
||||
* @param baseUri {@link URI} to check.
|
||||
* @param uri {@link URI} against which to compare the base.
|
||||
* @param baseUri
|
||||
* {@link URI} to check.
|
||||
* @param uri
|
||||
* {@link URI} against which to compare the base.
|
||||
*
|
||||
* @return {@literal true} if the baseUri is valid against the given {@link URI}, {@literal false} otherwise.
|
||||
*/
|
||||
public static boolean validBaseUri(URI baseUri, URI uri) {
|
||||
@@ -41,16 +44,21 @@ public abstract class UriUtils {
|
||||
* second time passing a relative {@link URI} of "1".
|
||||
* </p>
|
||||
*
|
||||
* @param baseUri base {@link URI}
|
||||
* @param uri {@link URI} to explode and iteratre over.
|
||||
* @param handler {@link Handler} to call for each segment of the URI's path.
|
||||
* @param <V> Return type of the handler.
|
||||
* @param baseUri
|
||||
* base {@link URI}
|
||||
* @param uri
|
||||
* {@link URI} to explode and iteratre over.
|
||||
* @param handler
|
||||
* {@link Handler} to call for each segment of the URI's path.
|
||||
* @param <V>
|
||||
* Return type of the handler.
|
||||
*
|
||||
* @return Handler return value, or possibly {@literal null}.
|
||||
*/
|
||||
public static <V> V foreach(URI baseUri, URI uri, Handler<URI, V> handler) {
|
||||
List<URI> uris = explode(baseUri, uri);
|
||||
V v = null;
|
||||
for (URI u : uris) {
|
||||
for(URI u : uris) {
|
||||
v = handler.handle(u);
|
||||
}
|
||||
return v;
|
||||
@@ -63,16 +71,19 @@ public abstract class UriUtils {
|
||||
* in
|
||||
* a {@link Stack} of relative {@link URI}s of size 2--one for "person" and one for "1".</p>
|
||||
*
|
||||
* @param baseUri base {@link URI}
|
||||
* @param uri {@link URI} to explode
|
||||
* @param baseUri
|
||||
* base {@link URI}
|
||||
* @param uri
|
||||
* {@link URI} to explode
|
||||
*
|
||||
* @return {@link Stack} of relative {@link URI}s.
|
||||
*/
|
||||
public static Stack<URI> explode(URI baseUri, URI uri) {
|
||||
Stack<URI> uris = new Stack<URI>();
|
||||
if (StringUtils.hasText(uri.getPath())) {
|
||||
if(StringUtils.hasText(uri.getPath())) {
|
||||
URI relativeUri = baseUri.relativize(uri);
|
||||
if (StringUtils.hasText(relativeUri.getPath())) {
|
||||
for (String part : relativeUri.getPath().split("/")) {
|
||||
if(StringUtils.hasText(relativeUri.getPath())) {
|
||||
for(String part : relativeUri.getPath().split("/")) {
|
||||
uris.add(URI.create(part + (StringUtils.hasText(uri.getQuery()) ? "?" + uri.getQuery() : "")));
|
||||
}
|
||||
}
|
||||
@@ -86,38 +97,41 @@ public abstract class UriUtils {
|
||||
* <p>e.g. merging base URI {@literal http://localhost:8080/data} and relative uri {@literal person/1?name=John+Doe}
|
||||
* would result in an absolute URI of {@literal http://localhost:8080/data/person/1?name=John+Doe}</p>
|
||||
*
|
||||
* @param baseUri base {@link URI}
|
||||
* @param uris {@link URI}s to merge
|
||||
* @param baseUri
|
||||
* base {@link URI}
|
||||
* @param uris
|
||||
* {@link URI}s to merge
|
||||
*
|
||||
* @return {@link URI} that is the combination of all the given (possibly relative, possibly absolute) URIs.
|
||||
*/
|
||||
public static URI merge(URI baseUri, URI... uris) {
|
||||
StringBuilder query = new StringBuilder();
|
||||
|
||||
UriComponentsBuilder ub = UriComponentsBuilder.fromUri(baseUri);
|
||||
for (URI uri : uris) {
|
||||
for(URI uri : uris) {
|
||||
String s = uri.getScheme();
|
||||
if (null != s) {
|
||||
if(null != s) {
|
||||
ub.scheme(s);
|
||||
}
|
||||
|
||||
s = uri.getUserInfo();
|
||||
if (null != s) {
|
||||
if(null != s) {
|
||||
ub.userInfo(s);
|
||||
}
|
||||
|
||||
s = uri.getHost();
|
||||
if (null != s) {
|
||||
if(null != s) {
|
||||
ub.host(s);
|
||||
}
|
||||
|
||||
int i = uri.getPort();
|
||||
if (i > 0) {
|
||||
if(i > 0) {
|
||||
ub.port(i);
|
||||
}
|
||||
|
||||
s = uri.getPath();
|
||||
if (null != s) {
|
||||
if (!uri.isAbsolute() && StringUtils.hasText(s)) {
|
||||
if(null != s) {
|
||||
if(!uri.isAbsolute() && StringUtils.hasText(s)) {
|
||||
ub.pathSegment(s);
|
||||
} else {
|
||||
ub.path(s);
|
||||
@@ -125,20 +139,20 @@ public abstract class UriUtils {
|
||||
}
|
||||
|
||||
s = uri.getQuery();
|
||||
if (null != s) {
|
||||
if (query.length() > 0) {
|
||||
if(null != s) {
|
||||
if(query.length() > 0) {
|
||||
query.append("&");
|
||||
}
|
||||
query.append(s);
|
||||
}
|
||||
|
||||
s = uri.getFragment();
|
||||
if (null != s) {
|
||||
if(null != s) {
|
||||
ub.fragment(s);
|
||||
}
|
||||
}
|
||||
|
||||
if (query.length() > 0) {
|
||||
if(query.length() > 0) {
|
||||
ub.query(query.toString());
|
||||
}
|
||||
|
||||
@@ -149,14 +163,15 @@ public abstract class UriUtils {
|
||||
* Just the path portion of the {@link URI}, but with any trailing slash "/" removed.
|
||||
*
|
||||
* @param uri
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String path(URI uri) {
|
||||
if (null == uri) {
|
||||
if(null == uri) {
|
||||
return null;
|
||||
}
|
||||
String s = uri.getPath();
|
||||
if (s.endsWith("/")) {
|
||||
if(s.endsWith("/")) {
|
||||
return s.substring(0, s.length() - 1);
|
||||
} else {
|
||||
return s;
|
||||
@@ -166,8 +181,11 @@ public abstract class UriUtils {
|
||||
/**
|
||||
* The very last segment of the {@link URI}.
|
||||
*
|
||||
* @param baseUri base {@link URI}
|
||||
* @param uri {@link URI} to explode
|
||||
* @param baseUri
|
||||
* base {@link URI}
|
||||
* @param uri
|
||||
* {@link URI} to explode
|
||||
*
|
||||
* @return Relative {@link URI} that is the last segment of the path for the given URI.
|
||||
*/
|
||||
public static URI tail(URI baseUri, URI uri) {
|
||||
|
||||
Reference in New Issue
Block a user