list element binding
This commit is contained in:
parent
2da1bb8607
commit
603ffe80c8
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.springframework.ui.binding;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.ui.alert.Alert;
|
||||
import org.springframework.ui.alert.Severity;
|
||||
|
||||
|
|
@ -26,7 +27,7 @@ import org.springframework.ui.alert.Severity;
|
|||
public interface Binding {
|
||||
|
||||
/**
|
||||
* The value to display in the UI.
|
||||
* The model value formatted for display in a single field in the UI.
|
||||
* Is the formatted model value if {@link BindingStatus#CLEAN} or {@link BindingStatus#COMMITTED}.
|
||||
* Is the formatted buffered value if {@link BindingStatus#DIRTY} or {@link BindingStatus#COMMIT_FAILURE}.
|
||||
*/
|
||||
|
|
@ -129,6 +130,11 @@ public interface Binding {
|
|||
*/
|
||||
Class<?> getValueType();
|
||||
|
||||
/**
|
||||
* The model value type descriptor.
|
||||
*/
|
||||
TypeDescriptor<?> getValueTypeDescriptor();
|
||||
|
||||
/**
|
||||
* Set the model value.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import java.lang.reflect.ParameterizedType;
|
|||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.text.ParseException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
|
@ -47,16 +48,24 @@ public class PropertyBinding implements Binding {
|
|||
|
||||
private BindingStatus status;
|
||||
|
||||
private Map<Integer, ListElementBinding> listElementBindings;
|
||||
|
||||
private Class<?> elementType;
|
||||
|
||||
public PropertyBinding(PropertyDescriptor property, Object object, BindingContext bindingContext) {
|
||||
this.property = property;
|
||||
this.object = object;
|
||||
this.bindingContext = bindingContext;
|
||||
buffer = new ValueBuffer(getModel());
|
||||
status = BindingStatus.CLEAN;
|
||||
if (isList()) {
|
||||
listElementBindings = new HashMap<Integer, ListElementBinding>();
|
||||
elementType = GenericCollectionTypeResolver.getCollectionReturnType(property.getReadMethod());
|
||||
}
|
||||
}
|
||||
|
||||
public String getRenderValue() {
|
||||
return format(getValue(), bindingContext.getFormatter());
|
||||
return format(getValue(), getFormatter());
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
|
|
@ -88,7 +97,7 @@ public class PropertyBinding implements Binding {
|
|||
assertEnabled();
|
||||
if (sourceValue instanceof String) {
|
||||
try {
|
||||
Object parsed = bindingContext.getFormatter().parse((String) sourceValue, getLocale());
|
||||
Object parsed = getFormatter().parse((String) sourceValue, getLocale());
|
||||
buffer.setValue(coerseToValueType(parsed));
|
||||
sourceValue = null;
|
||||
status = BindingStatus.DIRTY;
|
||||
|
|
@ -123,13 +132,23 @@ public class PropertyBinding implements Binding {
|
|||
if (status != BindingStatus.INVALID_SOURCE_VALUE) {
|
||||
try {
|
||||
buffer.setValue(coerseToValueType(parsed));
|
||||
sourceValue = null;
|
||||
status = BindingStatus.DIRTY;
|
||||
} catch (ConversionFailedException e) {
|
||||
this.sourceValue = sourceValue;
|
||||
invalidSourceValueCause = e;
|
||||
status = BindingStatus.INVALID_SOURCE_VALUE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
buffer.setValue(coerseToValueType(sourceValue));
|
||||
sourceValue = null;
|
||||
status = BindingStatus.DIRTY;
|
||||
} catch (ConversionFailedException e) {
|
||||
this.sourceValue = sourceValue;
|
||||
invalidSourceValueCause = e;
|
||||
status = BindingStatus.INVALID_SOURCE_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -179,19 +198,19 @@ public class PropertyBinding implements Binding {
|
|||
}
|
||||
};
|
||||
} else if (status == BindingStatus.COMMIT_FAILURE) {
|
||||
return new AbstractAlert() {
|
||||
public String getCode() {
|
||||
return "internalError";
|
||||
}
|
||||
return new AbstractAlert() {
|
||||
public String getCode() {
|
||||
return "internalError";
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return "Internal error occurred; message = [" + buffer.getFlushException().getMessage() + "]";
|
||||
}
|
||||
public String getMessage() {
|
||||
return "Internal error occurred; message = [" + buffer.getFlushException().getMessage() + "]";
|
||||
}
|
||||
|
||||
public Severity getSeverity() {
|
||||
return Severity.FATAL;
|
||||
}
|
||||
};
|
||||
public Severity getSeverity() {
|
||||
return Severity.FATAL;
|
||||
}
|
||||
};
|
||||
} else if (status == BindingStatus.COMMITTED) {
|
||||
return new AbstractAlert() {
|
||||
public String getCode() {
|
||||
|
|
@ -248,6 +267,10 @@ public class PropertyBinding implements Binding {
|
|||
public Class<?> getValueType() {
|
||||
return property.getPropertyType();
|
||||
}
|
||||
|
||||
public TypeDescriptor<?> getValueTypeDescriptor() {
|
||||
return new TypeDescriptor(new MethodParameter(property.getReadMethod(), -1));
|
||||
}
|
||||
|
||||
public void setValue(Object value) {
|
||||
ReflectionUtils.invokeMethod(property.getWriteMethod(), object, value);
|
||||
|
|
@ -264,17 +287,21 @@ public class PropertyBinding implements Binding {
|
|||
}
|
||||
|
||||
public boolean isList() {
|
||||
return getModel().getValueType().isArray() || List.class.isAssignableFrom(getModel().getValueType());
|
||||
return List.class.isAssignableFrom(getValueType());
|
||||
}
|
||||
|
||||
public Binding getListElementBinding(int index) {
|
||||
assertListProperty();
|
||||
//return new IndexedBinding(index, (List) getValue(), getCollectionTypeDescriptor(), typeConverter);
|
||||
return null;
|
||||
ListElementBinding binding = listElementBindings.get(index);
|
||||
if (binding == null) {
|
||||
binding = new ListElementBinding(index);
|
||||
listElementBindings.put(index, binding);
|
||||
}
|
||||
return binding;
|
||||
}
|
||||
|
||||
public boolean isMap() {
|
||||
return Map.class.isAssignableFrom(getModel().getValueType());
|
||||
return Map.class.isAssignableFrom(getValueType());
|
||||
}
|
||||
|
||||
public Binding getMapValueBinding(Object key) {
|
||||
|
|
@ -299,7 +326,7 @@ public class PropertyBinding implements Binding {
|
|||
}
|
||||
return format(value, formatter);
|
||||
}
|
||||
|
||||
|
||||
private String format(Object value, Formatter formatter) {
|
||||
Class<?> formattedType = getFormattedObjectType(formatter.getClass());
|
||||
value = bindingContext.getTypeConverter().convert(value, formattedType);
|
||||
|
|
@ -308,6 +335,10 @@ public class PropertyBinding implements Binding {
|
|||
|
||||
// internal helpers
|
||||
|
||||
protected Formatter getFormatter() {
|
||||
return bindingContext.getFormatter();
|
||||
}
|
||||
|
||||
private Locale getLocale() {
|
||||
return LocaleContextHolder.getLocale();
|
||||
}
|
||||
|
|
@ -354,7 +385,7 @@ public class PropertyBinding implements Binding {
|
|||
}
|
||||
|
||||
private Object coerseToValueType(Object parsed) {
|
||||
TypeDescriptor targetType = new TypeDescriptor(new MethodParameter(property.getWriteMethod(), 0));
|
||||
TypeDescriptor targetType = getModel().getValueTypeDescriptor();
|
||||
TypeConverter converter = bindingContext.getTypeConverter();
|
||||
if (parsed != null && converter.canConvert(parsed.getClass(), targetType)) {
|
||||
return converter.convert(parsed, targetType);
|
||||
|
|
@ -363,12 +394,6 @@ public class PropertyBinding implements Binding {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private CollectionTypeDescriptor getCollectionTypeDescriptor() {
|
||||
Class<?> elementType = GenericCollectionTypeResolver.getCollectionReturnType(property.getReadMethod());
|
||||
return new CollectionTypeDescriptor(getModel().getValueType(), elementType);
|
||||
}
|
||||
|
||||
private void assertScalarProperty() {
|
||||
if (isList()) {
|
||||
throw new IllegalArgumentException("Is a Collection but should be a scalar");
|
||||
|
|
@ -411,5 +436,72 @@ public class PropertyBinding implements Binding {
|
|||
return getCode() + " - " + getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
class ListElementBinding extends PropertyBinding {
|
||||
|
||||
private int index;
|
||||
|
||||
public ListElementBinding(int index) {
|
||||
super(property, object, bindingContext);
|
||||
this.index = index;
|
||||
growListIfNecessary();
|
||||
}
|
||||
|
||||
protected Formatter getFormatter() {
|
||||
return bindingContext.getElementFormatter();
|
||||
}
|
||||
|
||||
public Model getModel() {
|
||||
return new Model() {
|
||||
public Object getValue() {
|
||||
return getList().get(index);
|
||||
}
|
||||
|
||||
public Class<?> getValueType() {
|
||||
if (elementType != null) {
|
||||
return elementType;
|
||||
} else {
|
||||
return getValue().getClass();
|
||||
}
|
||||
}
|
||||
|
||||
public TypeDescriptor<?> getValueTypeDescriptor() {
|
||||
return TypeDescriptor.valueOf(getValueType());
|
||||
}
|
||||
|
||||
public void setValue(Object value) {
|
||||
getList().set(index, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
|
||||
private void growListIfNecessary() {
|
||||
if (index >= getList().size()) {
|
||||
for (int i = getList().size(); i <= index; i++) {
|
||||
addValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List getList() {
|
||||
return (List) PropertyBinding.this.getValue();
|
||||
}
|
||||
|
||||
private void addValue() {
|
||||
try {
|
||||
Object value = getValueType().newInstance();
|
||||
getList().add(value);
|
||||
} catch (InstantiationException e) {
|
||||
throw new IllegalStateException("Could not lazily instantiate model of type ["
|
||||
+ getValueType().getName() + "] to grow List", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalStateException("Could not lazily instantiate model of type ["
|
||||
+ getValueType().getName() + "] to grow List", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -19,9 +19,11 @@ public class PropertyPath implements Iterable<PropertyPathElement> {
|
|||
props = new String[] { propertyPath };
|
||||
}
|
||||
for (String prop : props) {
|
||||
if (prop.startsWith("[")) {
|
||||
int end = prop.indexOf(']');
|
||||
String index = prop.substring(0, end);
|
||||
if (prop.contains("[")) {
|
||||
int start = prop.indexOf('[');
|
||||
int end = prop.indexOf(']', start);
|
||||
String index = prop.substring(start + 1, end);
|
||||
elements.add(new PropertyPathElement(prop.substring(0, start), true));
|
||||
elements.add(new PropertyPathElement(index, true));
|
||||
} else {
|
||||
elements.add(new PropertyPathElement(prop, false));
|
||||
|
|
@ -35,7 +37,7 @@ public class PropertyPath implements Iterable<PropertyPathElement> {
|
|||
|
||||
public List<PropertyPathElement> getNestedElements() {
|
||||
if (elements.size() > 1) {
|
||||
return elements.subList(1, elements.size() - 1);
|
||||
return elements.subList(1, elements.size());
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import static org.junit.Assert.assertTrue;
|
|||
import java.math.BigDecimal;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
|
|
@ -210,18 +211,19 @@ public class GenericBinderTests {
|
|||
assertEquals(FooEnum.BOOP, value.get(2));
|
||||
}
|
||||
|
||||
/*
|
||||
@Test
|
||||
public void getBindingMultiValuedIndexAccess() {
|
||||
binder.addBinding("foos");
|
||||
bean.setFoos(Arrays.asList(new FooEnum[] { FooEnum.BAR }));
|
||||
Binding b = binder.getBinding("foos[0]");
|
||||
assertFalse(b.isIndexable());
|
||||
assertEquals("BAR", b.getValue());
|
||||
b.setValue("BAZ");
|
||||
assertEquals("BAZ", b.getValue());
|
||||
assertFalse(b.isList());
|
||||
assertEquals(FooEnum.BAR, b.getValue());
|
||||
assertEquals("BAR", b.getRenderValue());
|
||||
b.applySourceValue("BAZ");
|
||||
assertEquals("BAZ", b.getRenderValue());
|
||||
assertEquals(FooEnum.BAZ, b.getValue());
|
||||
}
|
||||
|
||||
/*
|
||||
@Test
|
||||
public void getBindingMultiValuedTypeConversionFailure() {
|
||||
binder.addBinding("foos");
|
||||
|
|
|
|||
Loading…
Reference in New Issue