13 changed files with 713 additions and 2 deletions
@ -0,0 +1,282 @@
@@ -0,0 +1,282 @@
|
||||
package org.springframework.ui.binding; |
||||
|
||||
import java.lang.annotation.Annotation; |
||||
import java.lang.reflect.Array; |
||||
import java.text.ParseException; |
||||
import java.util.Collection; |
||||
import java.util.HashMap; |
||||
import java.util.Locale; |
||||
import java.util.Map; |
||||
|
||||
import org.springframework.context.expression.MapAccessor; |
||||
import org.springframework.context.i18n.LocaleContextHolder; |
||||
import org.springframework.core.convert.TypeConverter; |
||||
import org.springframework.core.convert.TypeDescriptor; |
||||
import org.springframework.core.convert.support.DefaultTypeConverter; |
||||
import org.springframework.expression.EvaluationContext; |
||||
import org.springframework.expression.EvaluationException; |
||||
import org.springframework.expression.Expression; |
||||
import org.springframework.expression.ExpressionException; |
||||
import org.springframework.expression.ExpressionParser; |
||||
import org.springframework.expression.spel.standard.SpelExpressionParser; |
||||
import org.springframework.expression.spel.support.StandardEvaluationContext; |
||||
import org.springframework.expression.spel.support.StandardTypeConverter; |
||||
import org.springframework.ui.format.Formatter; |
||||
|
||||
public class Binder<T> { |
||||
|
||||
private static final String[] EMPTY_STRING_ARRAY = new String[0]; |
||||
|
||||
private T model; |
||||
|
||||
private Map<String, Binding> bindings; |
||||
|
||||
private Map<Class<?>, Formatter<?>> typeFormatters = new HashMap<Class<?>, Formatter<?>>(); |
||||
|
||||
private Map<Annotation, Formatter<?>> annotationFormatters = new HashMap<Annotation, Formatter<?>>(); |
||||
|
||||
private ExpressionParser expressionParser; |
||||
|
||||
private TypeConverter typeConverter; |
||||
|
||||
private boolean optimisticBinding = true; |
||||
|
||||
private static Formatter<?> defaultFormatter = new Formatter<?>() { |
||||
|
||||
public Class<?> getFormattedObjectType() { |
||||
return String.class; |
||||
} |
||||
|
||||
public String format(Object object, Locale locale) { |
||||
if (object == null) { |
||||
return ""; |
||||
} else { |
||||
return object.toString(); |
||||
} |
||||
} |
||||
|
||||
public Object parse(String formatted, Locale locale) |
||||
throws ParseException { |
||||
if (formatted == "") { |
||||
return null; |
||||
} else { |
||||
return formatted; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
public Binder(T model) { |
||||
this.model = model; |
||||
bindings = new HashMap<String, Binding>(); |
||||
expressionParser = new SpelExpressionParser(); |
||||
typeConverter = new DefaultTypeConverter(); |
||||
} |
||||
|
||||
public Binding add(BindingConfiguration binding) { |
||||
Binding newBinding; |
||||
try { |
||||
newBinding = new BindingImpl(binding); |
||||
} catch (org.springframework.expression.ParseException e) { |
||||
throw new IllegalArgumentException(e); |
||||
} |
||||
bindings.put(binding.getProperty(), newBinding); |
||||
return newBinding; |
||||
} |
||||
|
||||
public void add(Formatter<?> formatter, Class<?> propertyType) { |
||||
if (propertyType == null) { |
||||
propertyType = formatter.getFormattedObjectType(); |
||||
} |
||||
typeFormatters.put(propertyType, formatter); |
||||
} |
||||
|
||||
public void add(Formatter<?> formatter, Annotation propertyAnnotation) { |
||||
annotationFormatters.put(propertyAnnotation, formatter); |
||||
} |
||||
|
||||
public T getModel() { |
||||
return model; |
||||
} |
||||
|
||||
public Binding getBinding(String property) { |
||||
Binding binding = bindings.get(property); |
||||
if (binding == null && optimisticBinding) { |
||||
return add(new BindingConfiguration(property, null, false)); |
||||
} else { |
||||
return binding; |
||||
} |
||||
} |
||||
|
||||
public void bind(Map<String, ? extends Object> propertyValues) { |
||||
for (Map.Entry<String, ? extends Object> entry : propertyValues |
||||
.entrySet()) { |
||||
Binding binding = getBinding(entry.getKey()); |
||||
Object value = entry.getValue(); |
||||
if (value instanceof String[]) { |
||||
binding.setValues((String[])value); |
||||
} else if (value instanceof String) { |
||||
binding.setValue((String)entry.getValue()); |
||||
} else { |
||||
throw new IllegalArgumentException("Illegal argument " + value); |
||||
} |
||||
} |
||||
} |
||||
|
||||
class BindingImpl implements Binding { |
||||
|
||||
private Expression property; |
||||
|
||||
private Formatter formatter; |
||||
|
||||
private boolean required; |
||||
|
||||
public BindingImpl(BindingConfiguration config) |
||||
throws org.springframework.expression.ParseException { |
||||
property = expressionParser.parseExpression(config.getProperty()); |
||||
formatter = config.getFormatter(); |
||||
required = config.isRequired(); |
||||
} |
||||
|
||||
public String getFormattedValue() { |
||||
try { |
||||
return format(property.getValue(createEvaluationContext())); |
||||
} catch (ExpressionException e) { |
||||
throw new IllegalArgumentException(e); |
||||
} |
||||
} |
||||
|
||||
public void setValue(String formatted) { |
||||
Object value = parse(formatted); |
||||
assertRequired(value); |
||||
setValue(value); |
||||
} |
||||
|
||||
public String format(Object possibleValue) { |
||||
Formatter formatter = getFormatter(); |
||||
possibleValue = typeConverter.convert(possibleValue, formatter.getFormattedObjectType()); |
||||
return formatter.format(possibleValue, LocaleContextHolder.getLocale()); |
||||
} |
||||
|
||||
public boolean isCollection() { |
||||
TypeDescriptor<?> type = TypeDescriptor.valueOf(getValueType()); |
||||
return type.isCollection() || type.isArray(); |
||||
} |
||||
|
||||
public String[] getFormattedValues() { |
||||
Object multiValue; |
||||
try { |
||||
multiValue = property.getValue(createEvaluationContext()); |
||||
} catch (EvaluationException e) { |
||||
throw new IllegalStateException(e); |
||||
} |
||||
if (multiValue == null) { |
||||
return EMPTY_STRING_ARRAY; |
||||
} |
||||
TypeDescriptor<?> type = TypeDescriptor.valueOf(multiValue.getClass()); |
||||
String[] formattedValues; |
||||
if (type.isCollection()) { |
||||
Collection<?> values = ((Collection<?>)multiValue); |
||||
formattedValues = (String[]) Array.newInstance(String.class, values.size()); |
||||
copy(values, formattedValues); |
||||
} else if (type.isArray()) { |
||||
formattedValues = (String[]) Array.newInstance(String.class, Array.getLength(multiValue)); |
||||
copy((Iterable<?>) multiValue, formattedValues); |
||||
} else { |
||||
throw new IllegalStateException(); |
||||
} |
||||
return formattedValues; |
||||
} |
||||
|
||||
public void setValues(String[] formattedValues) { |
||||
Object values = Array.newInstance(getFormatter().getFormattedObjectType(), formattedValues.length); |
||||
for (int i = 0; i < formattedValues.length; i++) { |
||||
Array.set(values, i, parse(formattedValues[i])); |
||||
} |
||||
setValue(values); |
||||
} |
||||
|
||||
public boolean isRequired() { |
||||
return required; |
||||
} |
||||
|
||||
public Messages getMessages() { |
||||
return null; |
||||
} |
||||
|
||||
// internal helpers
|
||||
|
||||
private void assertRequired(Object value) { |
||||
if (required && value == null) { |
||||
throw new IllegalArgumentException("Value required"); |
||||
} |
||||
} |
||||
|
||||
private Object parse(String formatted) { |
||||
try { |
||||
return getFormatter().parse(formatted, LocaleContextHolder.getLocale()); |
||||
} catch (ParseException e) { |
||||
throw new IllegalArgumentException("Invalid format " + formatted, e); |
||||
} |
||||
} |
||||
|
||||
private Formatter getFormatter() { |
||||
if (formatter != null) { |
||||
return formatter; |
||||
} else { |
||||
Class<?> type = getValueType(); |
||||
Formatter<?> formatter = typeFormatters.get(type); |
||||
if (formatter != null) { |
||||
return formatter; |
||||
} else { |
||||
Annotation[] annotations = getAnnotations(); |
||||
for (Annotation a : annotations) { |
||||
formatter = annotationFormatters.get(a); |
||||
if (formatter != null) { |
||||
return formatter; |
||||
} |
||||
} |
||||
return defaultFormatter; |
||||
} |
||||
} |
||||
} |
||||
|
||||
private Class<?> getValueType() { |
||||
try { |
||||
// TODO Spring EL currently returns null here when value is null - not correct
|
||||
return property.getValueType(createEvaluationContext()); |
||||
} catch (EvaluationException e) { |
||||
throw new IllegalStateException(e); |
||||
} |
||||
} |
||||
|
||||
private Annotation[] getAnnotations() { |
||||
// TODO Spring EL presently gives us no way to get this information
|
||||
return new Annotation[0]; |
||||
} |
||||
|
||||
private void copy(Iterable values, String[] formattedValues) { |
||||
int i = 0; |
||||
for (Object value : values) { |
||||
formattedValues[i] = format(value); |
||||
i++; |
||||
} |
||||
} |
||||
|
||||
private void setValue(Object values) { |
||||
try { |
||||
property.setValue(createEvaluationContext(), values); |
||||
} catch (ExpressionException e) { |
||||
throw new IllegalArgumentException(e); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
private EvaluationContext createEvaluationContext() { |
||||
StandardEvaluationContext context = new StandardEvaluationContext(); |
||||
context.setRootObject(model); |
||||
context.addPropertyAccessor(new MapAccessor()); |
||||
context.setTypeConverter(new StandardTypeConverter(typeConverter)); |
||||
return context; |
||||
} |
||||
} |
||||
@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
|
||||
package org.springframework.ui.binding; |
||||
|
||||
public interface Binding { |
||||
|
||||
// single-value properties
|
||||
|
||||
String getFormattedValue(); |
||||
|
||||
void setValue(String formatted); |
||||
|
||||
String format(Object possibleValue); |
||||
|
||||
// multi-value properties
|
||||
|
||||
boolean isCollection(); |
||||
|
||||
String[] getFormattedValues(); |
||||
|
||||
void setValues(String[] formattedValues); |
||||
|
||||
// validation metadata
|
||||
|
||||
boolean isRequired(); |
||||
|
||||
Messages getMessages(); |
||||
|
||||
} |
||||
@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
|
||||
package org.springframework.ui.binding; |
||||
|
||||
import org.springframework.ui.format.Formatter; |
||||
|
||||
public class BindingConfiguration { |
||||
|
||||
private String property; |
||||
|
||||
private Formatter<?> formatter; |
||||
|
||||
private boolean required; |
||||
|
||||
public BindingConfiguration(String property, Formatter<?> formatter, boolean required) { |
||||
this.property = property; |
||||
this.formatter = formatter; |
||||
this.required = required; |
||||
} |
||||
|
||||
public String getProperty() { |
||||
return property; |
||||
} |
||||
|
||||
public Formatter<?> getFormatter() { |
||||
return formatter; |
||||
} |
||||
|
||||
public boolean isRequired() { |
||||
return required; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
package org.springframework.ui.binding; |
||||
|
||||
public interface Message { |
||||
|
||||
String getText(); |
||||
|
||||
String getSeverity(); |
||||
|
||||
} |
||||
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
package org.springframework.ui.binding; |
||||
|
||||
public interface MessageCriteria { |
||||
|
||||
} |
||||
@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
|
||||
package org.springframework.ui.binding; |
||||
|
||||
import java.util.List; |
||||
|
||||
public interface Messages { |
||||
|
||||
int getCount(); |
||||
|
||||
Severity getMaximumSeverity(); |
||||
|
||||
List<Message> getAll(); |
||||
|
||||
List<Message> getBySeverity(Severity severity); |
||||
|
||||
} |
||||
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
package org.springframework.ui.binding; |
||||
|
||||
public enum Severity { |
||||
INFO, WARNING, ERROR; |
||||
} |
||||
@ -0,0 +1,92 @@
@@ -0,0 +1,92 @@
|
||||
/* |
||||
* Copyright 2004-2009 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package org.springframework.ui.format; |
||||
|
||||
import java.text.DateFormat; |
||||
import java.text.ParseException; |
||||
import java.text.SimpleDateFormat; |
||||
import java.util.Date; |
||||
import java.util.Locale; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
|
||||
/** |
||||
* A formatter for {@link Date} types. |
||||
* Allows the configuration of an explicit date pattern and locale. |
||||
* @see SimpleDateFormat |
||||
* @author Keith Donald |
||||
*/ |
||||
public class DateFormatter implements Formatter<Date> { |
||||
|
||||
private static Log logger = LogFactory.getLog(DateFormatter.class); |
||||
|
||||
/** |
||||
* The default date pattern. |
||||
*/ |
||||
private static final String DEFAULT_PATTERN = "yyyy-MM-dd"; |
||||
|
||||
private String pattern; |
||||
|
||||
/** |
||||
* Sets the pattern to use to format date values. |
||||
* If not specified, the default pattern 'yyyy-MM-dd' is used. |
||||
* @param pattern the date formatting pattern |
||||
*/ |
||||
public void setPattern(String pattern) { |
||||
this.pattern = pattern; |
||||
} |
||||
|
||||
public Class<Date> getFormattedObjectType() { |
||||
return Date.class; |
||||
} |
||||
|
||||
public String format(Date date, Locale locale) { |
||||
if (date == null) { |
||||
return ""; |
||||
} |
||||
return getDateFormat(locale).format(date); |
||||
} |
||||
|
||||
public Date parse(String formatted, Locale locale) throws ParseException { |
||||
if (formatted.length() == 0) { |
||||
return null; |
||||
} |
||||
return getDateFormat(locale).parse(formatted); |
||||
} |
||||
|
||||
// subclassing hookings
|
||||
|
||||
protected DateFormat getDateFormat(Locale locale) { |
||||
DateFormat format = DateFormat.getDateInstance(DateFormat.SHORT, locale); |
||||
format.setLenient(false); |
||||
if (format instanceof SimpleDateFormat) { |
||||
String pattern = determinePattern(this.pattern); |
||||
((SimpleDateFormat) format).applyPattern(pattern); |
||||
} else { |
||||
logger.warn("Unable to apply format pattern '" + pattern |
||||
+ "'; Returned DateFormat is not a SimpleDateFormat"); |
||||
} |
||||
return format; |
||||
} |
||||
|
||||
// internal helpers
|
||||
|
||||
private String determinePattern(String pattern) { |
||||
return pattern != null ? pattern : DEFAULT_PATTERN; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,222 @@
@@ -0,0 +1,222 @@
|
||||
package org.springframework.ui.binding; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
import static org.junit.Assert.assertFalse; |
||||
import static org.junit.Assert.assertTrue; |
||||
|
||||
import java.math.BigDecimal; |
||||
import java.text.ParseException; |
||||
import java.util.ArrayList; |
||||
import java.util.Date; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Locale; |
||||
import java.util.Map; |
||||
|
||||
import org.junit.Ignore; |
||||
import org.junit.Test; |
||||
import org.springframework.ui.format.DateFormatter; |
||||
import org.springframework.ui.format.number.CurrencyFormatter; |
||||
|
||||
public class BinderTests { |
||||
|
||||
@Test |
||||
public void bindSingleValuesWithDefaultTypeConverterConversion() { |
||||
Binder<TestBean> binder = new Binder<TestBean>(new TestBean()); |
||||
Map<String, String> propertyValues = new HashMap<String, String>(); |
||||
propertyValues.put("string", "test"); |
||||
propertyValues.put("integer", "3"); |
||||
propertyValues.put("foo", "BAR"); |
||||
binder.bind(propertyValues); |
||||
assertEquals("test", binder.getModel().getString()); |
||||
assertEquals(3, binder.getModel().getInteger()); |
||||
assertEquals(FooEnum.BAR, binder.getModel().getFoo()); |
||||
} |
||||
|
||||
// TODO should update error context, not throw exception
|
||||
@Test(expected=IllegalArgumentException.class) |
||||
public void bindSingleValuesWithDefaultTypeCoversionFailures() { |
||||
Binder<TestBean> binder = new Binder<TestBean>(new TestBean()); |
||||
Map<String, String> propertyValues = new HashMap<String, String>(); |
||||
propertyValues.put("string", "test"); |
||||
propertyValues.put("integer", "bogus"); |
||||
propertyValues.put("foo", "bogus"); |
||||
binder.bind(propertyValues); |
||||
} |
||||
|
||||
@Test |
||||
public void bindSingleValuePropertyFormatterParsing() throws ParseException { |
||||
Binder<TestBean> binder = new Binder<TestBean>(new TestBean()); |
||||
binder.add(new BindingConfiguration("date", new DateFormatter(), false)); |
||||
Map<String, String> propertyValues = new HashMap<String, String>(); |
||||
propertyValues.put("date", "2009-06-01"); |
||||
binder.bind(propertyValues); |
||||
assertEquals(new DateFormatter().parse("2009-06-01", Locale.US), binder.getModel().getDate()); |
||||
} |
||||
|
||||
// TODO should update error context, not throw exception
|
||||
@Test(expected=IllegalArgumentException.class) |
||||
public void bindSingleValuePropertyFormatterParseException() { |
||||
Binder<TestBean> binder = new Binder<TestBean>(new TestBean()); |
||||
binder.add(new BindingConfiguration("date", new DateFormatter(), false)); |
||||
Map<String, String> propertyValues = new HashMap<String, String>(); |
||||
propertyValues.put("date", "bogus"); |
||||
binder.bind(propertyValues); |
||||
} |
||||
|
||||
@Test |
||||
@Ignore |
||||
public void bindSingleValueTypeFormatterParsing() throws ParseException { |
||||
Binder<TestBean> binder = new Binder<TestBean>(new TestBean()); |
||||
binder.add(new DateFormatter(), Date.class); |
||||
Map<String, String> propertyValues = new HashMap<String, String>(); |
||||
propertyValues.put("date", "2009-06-01"); |
||||
// TODO presently fails because Spring EL does not obtain property valueType using property metadata
|
||||
// instead it relies on value itself being not null
|
||||
// talk to andy about this
|
||||
binder.bind(propertyValues); |
||||
assertEquals(new DateFormatter().parse("2009-06-01", Locale.US), binder.getModel().getDate()); |
||||
} |
||||
|
||||
@Test |
||||
@Ignore |
||||
public void bindSingleValueAnnotationFormatterParsing() throws ParseException { |
||||
Binder<TestBean> binder = new Binder<TestBean>(new TestBean()); |
||||
binder.add(new CurrencyFormatter(), Currency.class); |
||||
Map<String, String> propertyValues = new HashMap<String, String>(); |
||||
propertyValues.put("currency", "$23.56"); |
||||
// TODO presently fails because Spring EL does not obtain property valueType using property metadata
|
||||
// instead it relies on value itself being not null
|
||||
// talk to andy about this
|
||||
binder.bind(propertyValues); |
||||
assertEquals(new BigDecimal("23.56"), binder.getModel().getCurrency()); |
||||
} |
||||
|
||||
@Test |
||||
public void getBindingOptimistic() { |
||||
Binder<TestBean> binder = new Binder<TestBean>(new TestBean()); |
||||
Binding b = binder.getBinding("integer"); |
||||
assertFalse(b.isRequired()); |
||||
assertFalse(b.isCollection()); |
||||
assertEquals("0", b.getFormattedValue()); |
||||
b.setValue("5"); |
||||
assertEquals("5", b.getFormattedValue()); |
||||
} |
||||
|
||||
@Test |
||||
public void getBindingCustomFormatter() { |
||||
Binder<TestBean> binder = new Binder<TestBean>(new TestBean()); |
||||
binder.add(new BindingConfiguration("currency", new CurrencyFormatter(), false)); |
||||
Binding b = binder.getBinding("currency"); |
||||
assertFalse(b.isRequired()); |
||||
assertFalse(b.isCollection()); |
||||
assertEquals("", b.getFormattedValue()); |
||||
b.setValue("$23.56"); |
||||
assertEquals("$23.56", b.getFormattedValue()); |
||||
} |
||||
|
||||
// TODO should update error context, not throw exception
|
||||
@Test(expected=IllegalArgumentException.class) |
||||
public void getBindingRequired() { |
||||
Binder<TestBean> binder = new Binder<TestBean>(new TestBean()); |
||||
binder.add(new BindingConfiguration("string", null, true)); |
||||
Binding b = binder.getBinding("string"); |
||||
assertTrue(b.isRequired()); |
||||
assertFalse(b.isCollection()); |
||||
assertEquals("", b.getFormattedValue()); |
||||
b.setValue(""); |
||||
} |
||||
|
||||
@Test |
||||
public void getBindingMultiValued() { |
||||
Binder<TestBean> binder = new Binder<TestBean>(new TestBean()); |
||||
Binding b = binder.getBinding("foos"); |
||||
assertTrue(b.isCollection()); |
||||
assertEquals(0, b.getFormattedValues().length); |
||||
b.setValues(new String[] { "BAR", "BAZ", "BOOP" }); |
||||
assertEquals(FooEnum.BAR, binder.getModel().getFoos().get(0)); |
||||
assertEquals(FooEnum.BAZ, binder.getModel().getFoos().get(1)); |
||||
assertEquals(FooEnum.BOOP, binder.getModel().getFoos().get(2)); |
||||
String[] values = b.getFormattedValues(); |
||||
assertEquals(3, values.length); |
||||
assertEquals("BAR", values[0]); |
||||
assertEquals("BAZ", values[1]); |
||||
assertEquals("BOOP", values[2]); |
||||
} |
||||
|
||||
@Test(expected=IllegalArgumentException.class) |
||||
public void getBindingMultiValuedTypeConversionError() { |
||||
Binder<TestBean> binder = new Binder<TestBean>(new TestBean()); |
||||
Binding b = binder.getBinding("foos"); |
||||
assertTrue(b.isCollection()); |
||||
assertEquals(0, b.getFormattedValues().length); |
||||
b.setValues(new String[] { "BAR", "BOGUS", "BOOP" }); |
||||
} |
||||
|
||||
public static enum FooEnum { |
||||
BAR, BAZ, BOOP; |
||||
} |
||||
|
||||
public static class TestBean { |
||||
private String string; |
||||
private int integer; |
||||
private Date date; |
||||
private FooEnum foo; |
||||
private BigDecimal currency; |
||||
private List<FooEnum> foos = new ArrayList<FooEnum>(); |
||||
|
||||
public String getString() { |
||||
return string; |
||||
} |
||||
|
||||
public void setString(String string) { |
||||
this.string = string; |
||||
} |
||||
|
||||
public int getInteger() { |
||||
return integer; |
||||
} |
||||
|
||||
public void setInteger(int integer) { |
||||
this.integer = integer; |
||||
} |
||||
|
||||
public Date getDate() { |
||||
return date; |
||||
} |
||||
|
||||
public void setDate(Date date) { |
||||
this.date = date; |
||||
} |
||||
|
||||
public FooEnum getFoo() { |
||||
return foo; |
||||
} |
||||
|
||||
public void setFoo(FooEnum foo) { |
||||
this.foo = foo; |
||||
} |
||||
|
||||
public BigDecimal getCurrency() { |
||||
return currency; |
||||
} |
||||
|
||||
@Currency |
||||
public void setCurrency(BigDecimal currency) { |
||||
this.currency = currency; |
||||
} |
||||
|
||||
public List<FooEnum> getFoos() { |
||||
return foos; |
||||
} |
||||
|
||||
public void setFoos(List<FooEnum> foos) { |
||||
this.foos = foos; |
||||
} |
||||
|
||||
} |
||||
|
||||
public @interface Currency { |
||||
|
||||
} |
||||
} |
||||
Loading…
Reference in new issue