|
|
|
@ -20,11 +20,14 @@ import org.springframework.core.MethodParameter; |
|
|
|
import org.springframework.core.convert.ConversionFailedException; |
|
|
|
import org.springframework.core.convert.ConversionFailedException; |
|
|
|
import org.springframework.core.convert.TypeConverter; |
|
|
|
import org.springframework.core.convert.TypeConverter; |
|
|
|
import org.springframework.core.convert.TypeDescriptor; |
|
|
|
import org.springframework.core.convert.TypeDescriptor; |
|
|
|
|
|
|
|
import org.springframework.core.style.StylerUtils; |
|
|
|
import org.springframework.ui.alert.Alert; |
|
|
|
import org.springframework.ui.alert.Alert; |
|
|
|
import org.springframework.ui.alert.Severity; |
|
|
|
import org.springframework.ui.alert.Severity; |
|
|
|
import org.springframework.ui.binding.Binding; |
|
|
|
import org.springframework.ui.binding.Binding; |
|
|
|
import org.springframework.ui.binding.support.GenericBinder.BindingContext; |
|
|
|
import org.springframework.ui.binding.support.GenericBinder.BindingContext; |
|
|
|
import org.springframework.ui.format.Formatter; |
|
|
|
import org.springframework.ui.format.Formatter; |
|
|
|
|
|
|
|
import org.springframework.ui.message.MessageBuilder; |
|
|
|
|
|
|
|
import org.springframework.ui.message.ResolvableArgument; |
|
|
|
import org.springframework.util.ReflectionUtils; |
|
|
|
import org.springframework.util.ReflectionUtils; |
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
@ -38,8 +41,7 @@ public class PropertyBinding implements Binding { |
|
|
|
|
|
|
|
|
|
|
|
private Object sourceValue; |
|
|
|
private Object sourceValue; |
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unused") |
|
|
|
private Exception invalidSourceValueCause; |
|
|
|
private ParseException sourceValueParseException; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private ValueBuffer buffer; |
|
|
|
private ValueBuffer buffer; |
|
|
|
|
|
|
|
|
|
|
|
@ -49,20 +51,26 @@ public class PropertyBinding implements Binding { |
|
|
|
this.property = property; |
|
|
|
this.property = property; |
|
|
|
this.object = object; |
|
|
|
this.object = object; |
|
|
|
this.bindingContext = bindingContext; |
|
|
|
this.bindingContext = bindingContext; |
|
|
|
this.buffer = new ValueBuffer(getModel()); |
|
|
|
buffer = new ValueBuffer(getModel()); |
|
|
|
status = BindingStatus.CLEAN; |
|
|
|
status = BindingStatus.CLEAN; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public String getRenderValue() { |
|
|
|
|
|
|
|
return format(getValue(), bindingContext.getFormatter()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public Object getValue() { |
|
|
|
public Object getValue() { |
|
|
|
if (status == BindingStatus.INVALID_SOURCE_VALUE) { |
|
|
|
if (status == BindingStatus.DIRTY || status == BindingStatus.COMMIT_FAILURE) { |
|
|
|
return sourceValue; |
|
|
|
return buffer.getValue(); |
|
|
|
} else if (status == BindingStatus.DIRTY || status == BindingStatus.COMMIT_FAILURE) { |
|
|
|
|
|
|
|
return formatValue(buffer.getValue()); |
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
return formatValue(getModel().getValue()); |
|
|
|
return getModel().getValue(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Class<?> getValueType() { |
|
|
|
|
|
|
|
return getModel().getValueType(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public boolean isEditable() { |
|
|
|
public boolean isEditable() { |
|
|
|
return isWriteableProperty() && bindingContext.getEditableCondition().isTrue(); |
|
|
|
return isWriteableProperty() && bindingContext.getEditableCondition().isTrue(); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -80,12 +88,17 @@ public class PropertyBinding implements Binding { |
|
|
|
assertEnabled(); |
|
|
|
assertEnabled(); |
|
|
|
if (sourceValue instanceof String) { |
|
|
|
if (sourceValue instanceof String) { |
|
|
|
try { |
|
|
|
try { |
|
|
|
buffer.setValue(bindingContext.getFormatter().parse((String) sourceValue, getLocale())); |
|
|
|
Object parsed = bindingContext.getFormatter().parse((String) sourceValue, getLocale()); |
|
|
|
|
|
|
|
buffer.setValue(coerseToValueType(parsed)); |
|
|
|
sourceValue = null; |
|
|
|
sourceValue = null; |
|
|
|
status = BindingStatus.DIRTY; |
|
|
|
status = BindingStatus.DIRTY; |
|
|
|
} catch (ParseException e) { |
|
|
|
} catch (ParseException e) { |
|
|
|
this.sourceValue = sourceValue; |
|
|
|
this.sourceValue = sourceValue; |
|
|
|
sourceValueParseException = e; |
|
|
|
invalidSourceValueCause = e; |
|
|
|
|
|
|
|
status = BindingStatus.INVALID_SOURCE_VALUE; |
|
|
|
|
|
|
|
} catch (ConversionFailedException e) { |
|
|
|
|
|
|
|
this.sourceValue = sourceValue; |
|
|
|
|
|
|
|
invalidSourceValueCause = e; |
|
|
|
status = BindingStatus.INVALID_SOURCE_VALUE; |
|
|
|
status = BindingStatus.INVALID_SOURCE_VALUE; |
|
|
|
} |
|
|
|
} |
|
|
|
} else if (sourceValue instanceof String[]) { |
|
|
|
} else if (sourceValue instanceof String[]) { |
|
|
|
@ -98,24 +111,36 @@ public class PropertyBinding implements Binding { |
|
|
|
for (int i = 0; i < sourceValues.length; i++) { |
|
|
|
for (int i = 0; i < sourceValues.length; i++) { |
|
|
|
Object parsedValue; |
|
|
|
Object parsedValue; |
|
|
|
try { |
|
|
|
try { |
|
|
|
parsedValue = bindingContext.getElementFormatter().parse(sourceValues[i], |
|
|
|
parsedValue = bindingContext.getElementFormatter().parse(sourceValues[i], getLocale()); |
|
|
|
LocaleContextHolder.getLocale()); |
|
|
|
|
|
|
|
Array.set(parsed, i, parsedValue); |
|
|
|
Array.set(parsed, i, parsedValue); |
|
|
|
} catch (ParseException e) { |
|
|
|
} catch (ParseException e) { |
|
|
|
this.sourceValue = sourceValue; |
|
|
|
this.sourceValue = sourceValue; |
|
|
|
sourceValueParseException = e; |
|
|
|
invalidSourceValueCause = e; |
|
|
|
status = BindingStatus.INVALID_SOURCE_VALUE; |
|
|
|
status = BindingStatus.INVALID_SOURCE_VALUE; |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (status != BindingStatus.INVALID_SOURCE_VALUE) { |
|
|
|
if (status != BindingStatus.INVALID_SOURCE_VALUE) { |
|
|
|
buffer.setValue(parsed); |
|
|
|
try { |
|
|
|
|
|
|
|
buffer.setValue(coerseToValueType(parsed)); |
|
|
|
|
|
|
|
} catch (ConversionFailedException e) { |
|
|
|
|
|
|
|
this.sourceValue = sourceValue; |
|
|
|
|
|
|
|
invalidSourceValueCause = e; |
|
|
|
|
|
|
|
status = BindingStatus.INVALID_SOURCE_VALUE; |
|
|
|
|
|
|
|
} |
|
|
|
sourceValue = null; |
|
|
|
sourceValue = null; |
|
|
|
status = BindingStatus.DIRTY; |
|
|
|
status = BindingStatus.DIRTY; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Object getInvalidSourceValue() { |
|
|
|
|
|
|
|
if (status != BindingStatus.INVALID_SOURCE_VALUE) { |
|
|
|
|
|
|
|
throw new IllegalStateException("No invalid source value"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return sourceValue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public BindingStatus getStatus() { |
|
|
|
public BindingStatus getStatus() { |
|
|
|
return status; |
|
|
|
return status; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -128,7 +153,25 @@ public class PropertyBinding implements Binding { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public String getMessage() { |
|
|
|
public String getMessage() { |
|
|
|
return "Could not parse source value"; |
|
|
|
MessageBuilder builder = new MessageBuilder(bindingContext.getMessageSource()); |
|
|
|
|
|
|
|
builder.code(getCode()); |
|
|
|
|
|
|
|
if (invalidSourceValueCause instanceof ParseException) { |
|
|
|
|
|
|
|
ParseException e = (ParseException) invalidSourceValueCause; |
|
|
|
|
|
|
|
builder.arg("label", new ResolvableArgument(property.getName())); |
|
|
|
|
|
|
|
builder.arg("value", sourceValue); |
|
|
|
|
|
|
|
builder.arg("errorOffset", e.getErrorOffset()); |
|
|
|
|
|
|
|
builder.defaultMessage("Failed to bind to property '" + property + "'; the user value " |
|
|
|
|
|
|
|
+ StylerUtils.style(sourceValue) + " has an invalid format and could no be parsed"); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
ConversionFailedException e = (ConversionFailedException) invalidSourceValueCause; |
|
|
|
|
|
|
|
builder.arg("label", new ResolvableArgument(property.getName())); |
|
|
|
|
|
|
|
builder.arg("value", sourceValue); |
|
|
|
|
|
|
|
builder.defaultMessage("Failed to bind to property '" + property + "'; the user value " |
|
|
|
|
|
|
|
+ StylerUtils.style(sourceValue) + " has could not be converted to " |
|
|
|
|
|
|
|
+ e.getTargetType().getName()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return builder.build(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public Severity getSeverity() { |
|
|
|
public Severity getSeverity() { |
|
|
|
@ -136,35 +179,19 @@ public class PropertyBinding implements Binding { |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
} else if (status == BindingStatus.COMMIT_FAILURE) { |
|
|
|
} else if (status == BindingStatus.COMMIT_FAILURE) { |
|
|
|
if (buffer.getFlushException() instanceof ConversionFailedException) { |
|
|
|
|
|
|
|
return new AbstractAlert() { |
|
|
|
|
|
|
|
public String getCode() { |
|
|
|
|
|
|
|
return "typeMismatch"; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public String getMessage() { |
|
|
|
|
|
|
|
return "Could not convert source value"; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Severity getSeverity() { |
|
|
|
|
|
|
|
return Severity.ERROR; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
return new AbstractAlert() { |
|
|
|
return new AbstractAlert() { |
|
|
|
public String getCode() { |
|
|
|
public String getCode() { |
|
|
|
return "internalError"; |
|
|
|
return "internalError"; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public String getMessage() { |
|
|
|
public String getMessage() { |
|
|
|
return "Internal error occurred " + buffer.getFlushException(); |
|
|
|
return "Internal error occurred; message = [" + buffer.getFlushException().getMessage() + "]"; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public Severity getSeverity() { |
|
|
|
public Severity getSeverity() { |
|
|
|
return Severity.FATAL; |
|
|
|
return Severity.FATAL; |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
} else if (status == BindingStatus.COMMITTED) { |
|
|
|
} else if (status == BindingStatus.COMMITTED) { |
|
|
|
return new AbstractAlert() { |
|
|
|
return new AbstractAlert() { |
|
|
|
public String getCode() { |
|
|
|
public String getCode() { |
|
|
|
@ -202,7 +229,7 @@ public class PropertyBinding implements Binding { |
|
|
|
public void revert() { |
|
|
|
public void revert() { |
|
|
|
if (status == BindingStatus.INVALID_SOURCE_VALUE) { |
|
|
|
if (status == BindingStatus.INVALID_SOURCE_VALUE) { |
|
|
|
sourceValue = null; |
|
|
|
sourceValue = null; |
|
|
|
sourceValueParseException = null; |
|
|
|
invalidSourceValueCause = null; |
|
|
|
status = BindingStatus.CLEAN; |
|
|
|
status = BindingStatus.CLEAN; |
|
|
|
} else if (status == BindingStatus.DIRTY || status == BindingStatus.COMMIT_FAILURE) { |
|
|
|
} else if (status == BindingStatus.DIRTY || status == BindingStatus.COMMIT_FAILURE) { |
|
|
|
buffer.clear(); |
|
|
|
buffer.clear(); |
|
|
|
@ -223,11 +250,6 @@ public class PropertyBinding implements Binding { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void setValue(Object value) { |
|
|
|
public void setValue(Object value) { |
|
|
|
TypeDescriptor targetType = new TypeDescriptor(new MethodParameter(property.getWriteMethod(), 0)); |
|
|
|
|
|
|
|
TypeConverter converter = bindingContext.getTypeConverter(); |
|
|
|
|
|
|
|
if (value != null && converter.canConvert(value.getClass(), targetType)) { |
|
|
|
|
|
|
|
value = converter.convert(value, targetType); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
ReflectionUtils.invokeMethod(property.getWriteMethod(), object, value); |
|
|
|
ReflectionUtils.invokeMethod(property.getWriteMethod(), object, value); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
@ -275,6 +297,10 @@ public class PropertyBinding implements Binding { |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
formatter = bindingContext.getFormatter(); |
|
|
|
formatter = bindingContext.getFormatter(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return format(value, formatter); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private String format(Object value, Formatter formatter) { |
|
|
|
Class<?> formattedType = getFormattedObjectType(formatter.getClass()); |
|
|
|
Class<?> formattedType = getFormattedObjectType(formatter.getClass()); |
|
|
|
value = bindingContext.getTypeConverter().convert(value, formattedType); |
|
|
|
value = bindingContext.getTypeConverter().convert(value, formattedType); |
|
|
|
return formatter.format(value, getLocale()); |
|
|
|
return formatter.format(value, getLocale()); |
|
|
|
@ -327,6 +353,16 @@ public class PropertyBinding implements Binding { |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Object coerseToValueType(Object parsed) { |
|
|
|
|
|
|
|
TypeDescriptor targetType = new TypeDescriptor(new MethodParameter(property.getWriteMethod(), 0)); |
|
|
|
|
|
|
|
TypeConverter converter = bindingContext.getTypeConverter(); |
|
|
|
|
|
|
|
if (parsed != null && converter.canConvert(parsed.getClass(), targetType)) { |
|
|
|
|
|
|
|
return converter.convert(parsed, targetType); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
return parsed; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unused") |
|
|
|
@SuppressWarnings("unused") |
|
|
|
private CollectionTypeDescriptor getCollectionTypeDescriptor() { |
|
|
|
private CollectionTypeDescriptor getCollectionTypeDescriptor() { |
|
|
|
Class<?> elementType = GenericCollectionTypeResolver.getCollectionReturnType(property.getReadMethod()); |
|
|
|
Class<?> elementType = GenericCollectionTypeResolver.getCollectionReturnType(property.getReadMethod()); |
|
|
|
|