- Revision
- 753
- Author
- Date
- 2008-06-21 12:46:54 -0500 (Sat, 21 Jun 2008)
Log Message
WAFFLE-90: Added NumberValueConverter to override OGNL number conversion.
Modified Paths
- trunk/examples/freemarker-example/src/main/java/org/codehaus/waffle/example/freemarker/controller/PersonController.java
- trunk/examples/freemarker-example/src/main/java/org/codehaus/waffle/example/freemarker/model/Person.java
- trunk/examples/freemarker-example/src/main/java/org/codehaus/waffle/example/freemarker/persister/PersistablePerson.java
- trunk/examples/freemarker-example/src/main/webapp/WEB-INF/web.xml
- trunk/examples/freemarker-example/src/main/webapp/people/edit.ftl
- trunk/examples/freemarker-example/src/main/webapp/people/manage.ftl
- trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/ognl/OgnlControllerDataBinderTest.java
- trunk/waffle-core/src/test/java/org/codehaus/waffle/testmodel/FakeController.java
Added Paths
Diff
Modified: trunk/examples/freemarker-example/src/main/java/org/codehaus/waffle/example/freemarker/controller/PersonController.java (752 => 753)
--- trunk/examples/freemarker-example/src/main/java/org/codehaus/waffle/example/freemarker/controller/PersonController.java 2008-06-21 15:49:43 UTC (rev 752) +++ trunk/examples/freemarker-example/src/main/java/org/codehaus/waffle/example/freemarker/controller/PersonController.java 2008-06-21 17:46:54 UTC (rev 753) @@ -8,6 +8,7 @@ import java.util.Collection; import java.util.List; +import org.codehaus.waffle.action.annotation.ActionMethod; import org.codehaus.waffle.example.freemarker.model.Person; import org.codehaus.waffle.example.freemarker.model.Person.Type; import org.codehaus.waffle.example.freemarker.persister.PersistablePerson; @@ -22,6 +23,7 @@ private Person person; private List<Long> selectedIds = new ArrayList<Long>(); private List<String> skills = Arrays.asList("Magician", "Apprentice"); + private Long id; public PersonController(PersonPersister persister, DateProvider dateProvider) { this.persister = persister; @@ -36,10 +38,10 @@ return persister.findAll(); } - public List<Type> getTypes(){ + public List<Type> getTypes() { return asList(Type.values()); } - + public List<Long> getSelectedIds() { return selectedIds; } @@ -56,6 +58,14 @@ return selected; } + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + public Person getPerson() { return person; } @@ -72,10 +82,15 @@ persister.delete(personId); } + @ActionMethod(parameters = { "id" }) public void select(Long id) { this.person = persister.findById(id); } + public void select() { + this.person = persister.findById(id); + } + public void show() { // do nothing: the selected Ids and people are automatically populated }
Modified: trunk/examples/freemarker-example/src/main/java/org/codehaus/waffle/example/freemarker/model/Person.java (752 => 753)
--- trunk/examples/freemarker-example/src/main/java/org/codehaus/waffle/example/freemarker/model/Person.java 2008-06-21 15:49:43 UTC (rev 752) +++ trunk/examples/freemarker-example/src/main/java/org/codehaus/waffle/example/freemarker/model/Person.java 2008-06-21 17:46:54 UTC (rev 753) @@ -37,6 +37,8 @@ boolean isWizard(); + double getMagicNumber(); + String getNotes(); }
Modified: trunk/examples/freemarker-example/src/main/java/org/codehaus/waffle/example/freemarker/persister/PersistablePerson.java (752 => 753)
--- trunk/examples/freemarker-example/src/main/java/org/codehaus/waffle/example/freemarker/persister/PersistablePerson.java 2008-06-21 15:49:43 UTC (rev 752) +++ trunk/examples/freemarker-example/src/main/java/org/codehaus/waffle/example/freemarker/persister/PersistablePerson.java 2008-06-21 17:46:54 UTC (rev 753) @@ -23,6 +23,7 @@ private List<Double> grades; private String notes; private boolean wizard; + private double magicNumber; private Type type; private Person bestFriend; private List<Person> friends; @@ -43,6 +44,7 @@ notes = ""; type = Type.APPRENTICE; wizard = false; + magicNumber = 0.d; } public PersistablePerson(Person person) { @@ -61,6 +63,7 @@ this.notes = person.getNotes(); this.type = person.getType(); this.wizard = person.isWizard(); + this.magicNumber = person.getMagicNumber(); } public Long getId() { @@ -187,6 +190,14 @@ this.wizard = wizard; } + public double getMagicNumber() { + return magicNumber; + } + + public void setMagicNumber(double magicNumber) { + this.magicNumber = magicNumber; + } + @Override public String toString() { return ToStringBuilder.reflectionToString(this, SHORT_PREFIX_STYLE);
Modified: trunk/examples/freemarker-example/src/main/webapp/WEB-INF/web.xml (752 => 753)
--- trunk/examples/freemarker-example/src/main/webapp/WEB-INF/web.xml 2008-06-21 15:49:43 UTC (rev 752) +++ trunk/examples/freemarker-example/src/main/webapp/WEB-INF/web.xml 2008-06-21 17:46:54 UTC (rev 753) @@ -16,6 +16,10 @@ <param-value>org.codehaus.waffle.bind.converters.DateValueConverter</param-value> </context-param> <context-param> + <param-name>register:NumberValueConverter</param-name> + <param-value>org.codehaus.waffle.bind.converters.NumberValueConverter</param-value> + </context-param> + <context-param> <param-name>register:StringListValueConverter</param-name> <param-value>org.codehaus.waffle.bind.converters.StringListValueConverter</param-value> </context-param>
Modified: trunk/examples/freemarker-example/src/main/webapp/people/edit.ftl (752 => 753)
--- trunk/examples/freemarker-example/src/main/webapp/people/edit.ftl 2008-06-21 15:49:43 UTC (rev 752) +++ trunk/examples/freemarker-example/src/main/webapp/people/edit.ftl 2008-06-21 17:46:54 UTC (rev 753) @@ -81,6 +81,10 @@ <@w.text "person.wizard" "${person.wizard?string}"/> </p> <p class="fieldRow"> + <label for="" Number:</label> + <@w.text "person.magicNumber" "${person.magicNumber}"/> + </p> + <p class="fieldRow"> <label for="" <@w.textarea "person.notes" "${person.notes}" /> </p>
Modified: trunk/examples/freemarker-example/src/main/webapp/people/manage.ftl (752 => 753)
--- trunk/examples/freemarker-example/src/main/webapp/people/manage.ftl 2008-06-21 15:49:43 UTC (rev 752) +++ trunk/examples/freemarker-example/src/main/webapp/people/manage.ftl 2008-06-21 17:46:54 UTC (rev 753) @@ -23,7 +23,7 @@ <#list people as person> <tr class="odd"> <td> - <a href="" + <a href="" </td> <td>${person.firstName}</td> <td>${person.lastName}</td>
Added: trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/NumberValueConverter.java (0 => 753)
--- trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/NumberValueConverter.java (rev 0) +++ trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/NumberValueConverter.java 2008-06-21 17:46:54 UTC (rev 753) @@ -0,0 +1,69 @@ +/* + * Copyright (c) terms as published in http://waffle.codehaus.org/license.html + */ +package org.codehaus.waffle.bind.converters; + +import java.lang.reflect.Type; +import java.text.NumberFormat; +import java.text.ParseException; +import java.util.Properties; + +import org.codehaus.waffle.i18n.MessageResources; + +/** + * <p> + * <code>ValueConverter</code> that converts a value to a Numbers using the <code>NumberFormat</code> instance + * provided (which defaults to <code>NumberFormat.getInstance()</code>). + * </p> + * The message keys and default values used are: + * <ul> + * <li>"bind.error.number" ([EMAIL PROTECTED] #BIND_ERROR_NUMBER_KEY}): bind error in number parsing (message defaults to + * [EMAIL PROTECTED] #DEFAULT_NUMBER_MESSAGE})</li> + * <li>"bind.error.number.missing" ([EMAIL PROTECTED] #BIND_ERROR_NUMBER_MISSING_KEY}): number is <code>null</code> or empty + * (message defaults to [EMAIL PROTECTED] #DEFAULT_NUMBER_MISSING_MESSAGE})</li> + * </ul> + * + * @author Mauro Talevi + */ +public class NumberValueConverter extends AbstractValueConverter { + public static final String BIND_ERROR_NUMBER_KEY = "bind.error.number"; + public static final String BIND_ERROR_NUMBER_MISSING_KEY = "bind.error.number.missing"; + public static final String DEFAULT_NUMBER_MISSING_MESSAGE = "Missing number value for field {0}"; + public static final String DEFAULT_NUMBER_MESSAGE = "Invalid number {1} (using format {2}) for field {0}"; + + private NumberFormat numberFormat; + + public NumberValueConverter(MessageResources messageResources) { + this(messageResources, new Properties(), NumberFormat.getInstance()); + } + + public NumberValueConverter(MessageResources messageResources, Properties patterns, NumberFormat numberFormat) { + super(messageResources, patterns); + this.numberFormat = numberFormat; + } + + public boolean accept(Type type) { + if (type instanceof Class) { + Class<?> rawType = (Class<?>) type; + return Number.class.isAssignableFrom(rawType) || double.class.equals(rawType) + || float.class.equals(rawType) || long.class.equals(rawType) || int.class.equals(rawType); + } + return false; + } + + @SuppressWarnings( { "unchecked" }) + public Object convertValue(String propertyName, String value, Type toType) { + String fieldName = messageFor(propertyName, propertyName); + if (missingValue(value)) { + return convertMissingValue(BIND_ERROR_NUMBER_MISSING_KEY, DEFAULT_NUMBER_MISSING_MESSAGE, fieldName); + } + + try { + return numberFormat.parse(value); + } catch (ParseException e) { + e.printStackTrace(); + throw newBindException(BIND_ERROR_NUMBER_KEY, DEFAULT_NUMBER_MESSAGE, fieldName, value, numberFormat); + } + } + +}
Added: trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/converters/NumberValueConverterTest.java (0 => 753)
--- trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/converters/NumberValueConverterTest.java (rev 0) +++ trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/converters/NumberValueConverterTest.java 2008-06-21 17:46:54 UTC (rev 753) @@ -0,0 +1,62 @@ +package org.codehaus.waffle.bind.converters; + +import static org.codehaus.waffle.testmodel.FakeControllerWithNumberMethods.methodParameterType; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.beans.IntrospectionException; + +import ognl.OgnlException; + +import org.codehaus.waffle.bind.BindException; +import org.codehaus.waffle.i18n.DefaultMessageResources; +import org.junit.Test; + +/** + * @author Mauro Talevi + */ +public class NumberValueConverterTest extends AbstractValueConverterTest { + + @Test + public void canAccept() throws IntrospectionException { + NumberValueConverter converter = new NumberValueConverter(new DefaultMessageResources()); + assertTrue(converter.accept(Number.class)); + assertTrue(converter.accept(Double.class)); + assertTrue(converter.accept(methodParameterType("primitiveDouble"))); + assertTrue(converter.accept(Float.class)); + assertTrue(converter.accept(methodParameterType("primitiveFloat"))); + assertTrue(converter.accept(Long.class)); + assertTrue(converter.accept(methodParameterType("primitiveLong"))); + assertTrue(converter.accept(Integer.class)); + assertTrue(converter.accept(methodParameterType("primitiveInteger"))); + assertFalse(converter.accept(Object.class)); + } + + @Test + public void canConvertNumbers() throws OgnlException, IntrospectionException { + NumberValueConverter converter = new NumberValueConverter(new DefaultMessageResources()); + assertCanConvertValueToNumber(converter, -1, "-1", Long.class); + assertCanConvertValueToNumber(converter, -1E3, "-1,000", Long.class); + assertCanConvertValueToNumber(converter, 1E3, "1000", Long.class); + assertCanConvertValueToNumber(converter, 1E3, "1,000", Long.class); + assertCanConvertValueToNumber(converter, 1E6, "1,000,000", Long.class); + assertCanConvertValueToNumber(converter, 0.1, "0.1", Double.class); + assertCanConvertValueToNumber(converter, 0.1E-3, ".1E-3", Double.class); + } + + @Test(expected=BindException.class) + public void cannotParseInvalidNumber() throws OgnlException, IntrospectionException { + NumberValueConverter converter = new NumberValueConverter(new DefaultMessageResources()); + converter.convertValue("property-name", "invalid-number", Number.class); + } + + @SuppressWarnings("unchecked") + private void assertCanConvertValueToNumber(NumberValueConverter converter, Number expected, String value, + Class<?> expectedType) throws IntrospectionException { + Number actual = (Number)converter.convertValue("property-name", value, Number.class); + assertEquals(expected.doubleValue(), actual.doubleValue(),1E-6); + } + + +}
Modified: trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/ognl/OgnlControllerDataBinderTest.java (752 => 753)
--- trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/ognl/OgnlControllerDataBinderTest.java 2008-06-21 15:49:43 UTC (rev 752) +++ trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/ognl/OgnlControllerDataBinderTest.java 2008-06-21 17:46:54 UTC (rev 753) @@ -16,9 +16,11 @@ import org.codehaus.waffle.bind.BindErrorMessageResolver; import org.codehaus.waffle.bind.BindException; import org.codehaus.waffle.bind.ControllerDataBinder; +import org.codehaus.waffle.bind.converters.NumberValueConverter; import org.codehaus.waffle.bind.converters.StringListValueConverter; import org.codehaus.waffle.context.ContextLevel; import org.codehaus.waffle.i18n.DefaultMessageResources; +import org.codehaus.waffle.i18n.MessageResources; import org.codehaus.waffle.monitor.SilentMonitor; import org.codehaus.waffle.testmodel.FakeBean; import org.codehaus.waffle.testmodel.FakeController; @@ -40,6 +42,8 @@ @RunWith(JMock.class) public class OgnlControllerDataBinderTest { + private static final MessageResources MESSAGE_RESOURCES = new DefaultMessageResources(); + private static final SilentMonitor MONITOR = new SilentMonitor(); private Mockery mockery = new Mockery(); @Test @@ -63,7 +67,7 @@ }); FakeController fakeController = new FakeController(); - ControllerDataBinder binder = new OgnlControllerDataBinder(new OgnlValueConverterFinder(), null, new SilentMonitor()); + ControllerDataBinder binder = new OgnlControllerDataBinder(new OgnlValueConverterFinder(), null, MONITOR); ErrorsContext errorsContext = new DefaultErrorsContext(null); binder.bind(request, null, errorsContext, fakeController); @@ -73,8 +77,36 @@ } @Test - public void canBindListValues() { + public void canBindNumberValues() { List<String> parameters = new ArrayList<String>(); + parameters.add("number"); + final Enumeration<String> enumeration = Collections.enumeration(parameters); + + // Mock HttpServletRequest + final String[] values = new String[]{"1,000"}; + final HttpServletRequest request = mockery.mock(HttpServletRequest.class); + mockery.checking(new Expectations() { + { + one(request).getParameterNames(); + will(returnValue(enumeration)); + one(request).getParameterValues("number"); + will(returnValue(values)); + } + }); + + FakeController fakeController = new FakeController(); + OgnlValueConverterFinder finder = new OgnlValueConverterFinder(new NumberValueConverter(MESSAGE_RESOURCES)); + ControllerDataBinder binder = new OgnlControllerDataBinder(finder, null, MONITOR); + ErrorsContext errorsContext = new DefaultErrorsContext(null); + binder.bind(request, null, errorsContext, fakeController); + + assertEquals(1000L, fakeController.getNumber()); + assertFalse(errorsContext.hasErrorMessages()); + } + + @Test + public void canBindStringListValues() { + List<String> parameters = new ArrayList<String>(); parameters.add("list"); final Enumeration<String> enumeration = Collections.enumeration(parameters); @@ -91,13 +123,15 @@ }); FakeController fakeController = new FakeController(); - ControllerDataBinder binder = new OgnlControllerDataBinder(new OgnlValueConverterFinder(new StringListValueConverter(new DefaultMessageResources())), null, new SilentMonitor()); + OgnlValueConverterFinder finder = new OgnlValueConverterFinder(new StringListValueConverter(MESSAGE_RESOURCES)); + ControllerDataBinder binder = new OgnlControllerDataBinder(finder, null, MONITOR); ErrorsContext errorsContext = new DefaultErrorsContext(null); binder.bind(request, null, errorsContext, fakeController); assertEquals(asList(values), fakeController.getList()); assertFalse(errorsContext.hasErrorMessages()); } + @Test public void canBindEmptyValueForEnum() { @@ -117,7 +151,7 @@ }); FakeController fakeController = new FakeController(); - ControllerDataBinder binder = new OgnlControllerDataBinder(new OgnlValueConverterFinder(), null, new SilentMonitor()); + ControllerDataBinder binder = new OgnlControllerDataBinder(new OgnlValueConverterFinder(), null, MONITOR); ErrorsContext errorsContext = new DefaultErrorsContext(null); binder.bind(request, null, errorsContext, fakeController); @@ -143,7 +177,7 @@ }); FakeController fakeController = new FakeController(); - ControllerDataBinder binder = new OgnlControllerDataBinder(new OgnlValueConverterFinder(), null, new SilentMonitor()); + ControllerDataBinder binder = new OgnlControllerDataBinder(new OgnlValueConverterFinder(), null, MONITOR); ErrorsContext errorsContext = new DefaultErrorsContext(null); binder.bind(request, null, errorsContext, fakeController); @@ -172,7 +206,7 @@ }); FakeController fakeController = new FakeController(); - ControllerDataBinder binder = new OgnlControllerDataBinder(new OgnlValueConverterFinder(), null, new SilentMonitor()); + ControllerDataBinder binder = new OgnlControllerDataBinder(new OgnlValueConverterFinder(), null, MONITOR); ErrorsContext errorsContext = new DefaultErrorsContext(null); binder.bind(request, null, errorsContext, fakeController); @@ -204,7 +238,7 @@ } }); - ControllerDataBinder binder = new OgnlControllerDataBinder(new OgnlValueConverterFinder(), resolver, new SilentMonitor()); + ControllerDataBinder binder = new OgnlControllerDataBinder(new OgnlValueConverterFinder(), resolver, MONITOR); ErrorsContext errorsContext = new DefaultErrorsContext(null); binder.bind(request, null, errorsContext, new FakeBean()); @@ -235,7 +269,7 @@ } }); - ControllerDataBinder binder = new OgnlControllerDataBinder(new OgnlValueConverterFinder(), null, new SilentMonitor()) { + ControllerDataBinder binder = new OgnlControllerDataBinder(new OgnlValueConverterFinder(), null, MONITOR) { protected Object handleConvert(String parameterName, String parameterValue, Object model) { throw new BindException("fake from test"); }
Modified: trunk/waffle-core/src/test/java/org/codehaus/waffle/testmodel/FakeController.java (752 => 753)
--- trunk/waffle-core/src/test/java/org/codehaus/waffle/testmodel/FakeController.java 2008-06-21 15:49:43 UTC (rev 752) +++ trunk/waffle-core/src/test/java/org/codehaus/waffle/testmodel/FakeController.java 2008-06-21 17:46:54 UTC (rev 753) @@ -18,7 +18,7 @@ private String name; private String[] values; private List<String> list; - private Long numericValue; + private Number number; private ContextLevel contextLevel; private HttpServletRequest request; private HttpServletResponse response; @@ -31,15 +31,15 @@ public void setName(String name) { this.name = name; } + + public Number getNumber() { + return number; + } - public Long getNumericValue() { - return numericValue; + public void setNumber(Number number) { + this.number = number; } - public void setNumericValue(Long numericValue) { - this.numericValue = numericValue; - }// used to prove we can set an enum with ognl - public ContextLevel getContextLevel() { return contextLevel; }
Added: trunk/waffle-core/src/test/java/org/codehaus/waffle/testmodel/FakeControllerWithNumberMethods.java (0 => 753)
--- trunk/waffle-core/src/test/java/org/codehaus/waffle/testmodel/FakeControllerWithNumberMethods.java (rev 0) +++ trunk/waffle-core/src/test/java/org/codehaus/waffle/testmodel/FakeControllerWithNumberMethods.java 2008-06-21 17:46:54 UTC (rev 753) @@ -0,0 +1,26 @@ +package org.codehaus.waffle.testmodel; + +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.MethodDescriptor; +import java.lang.reflect.Type; + +public class FakeControllerWithNumberMethods { + public static Type methodParameterType(String methodName) throws IntrospectionException { + BeanInfo beanInfo = Introspector.getBeanInfo(FakeControllerWithNumberMethods.class); + for (MethodDescriptor md : beanInfo.getMethodDescriptors()) { + if (md.getMethod().getName().equals(methodName)) { + return md.getMethod().getGenericParameterTypes()[0]; + } + } + return null; + } + + @SuppressWarnings("unchecked") + public void primitiveDouble(double number){}; + public void primitiveFloat(float number){}; + public void primitiveLong(long number){}; + public void primitiveInteger(int number){}; + public void object(Object object){}; +} \ No newline at end of file
To unsubscribe from this list please visit:
