001    // Copyright 2004, 2005 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry.form.translator;
016    
017    import org.apache.tapestry.IMarkupWriter;
018    import org.apache.tapestry.IRequestCycle;
019    import org.apache.tapestry.form.FormComponentContributorContext;
020    import org.apache.tapestry.form.FormComponentContributorTestCase;
021    import org.apache.tapestry.form.IFormComponent;
022    import org.apache.tapestry.form.ValidationMessages;
023    import org.apache.tapestry.json.JSONObject;
024    import org.apache.tapestry.valid.ValidationConstraint;
025    import org.apache.tapestry.valid.ValidationStrings;
026    import org.apache.tapestry.valid.ValidatorException;
027    import static org.easymock.EasyMock.expect;
028    import org.testng.annotations.Test;
029    
030    import java.text.DecimalFormatSymbols;
031    import java.util.Locale;
032    
033    /**
034     * Tests for {@link org.apache.tapestry.form.translator.NumberTranslator}
035     * 
036     * @author Howard Lewis Ship
037     * @since 4.0
038     */
039    @Test
040    public class TestNumberTranslator extends FormComponentContributorTestCase
041    {
042        public void test_Default_Format()
043        {
044            NumberTranslator translator = new NumberTranslator();
045    
046            testFormat(translator, new Integer(10), "10");
047        }
048    
049        public void test_Omit_Zero()
050        {
051            NumberTranslator translator = new NumberTranslator("pattern=0.00,omitZero=true");
052            
053            testFormat(translator, new Integer(0), "");
054            
055            assertEquals(translator.getValueForEmptyInput(), null);
056        }
057    
058        public void test_Omit_Zero_Off()
059        {
060            NumberTranslator translator = new NumberTranslator("!omitZero,pattern=0.00");
061            
062            testFormat(translator, new Integer(0), "0.00");
063        }
064    
065        public void test_Custom_Format()
066        {
067            NumberTranslator translator = new NumberTranslator();
068    
069            translator.setPattern("$#0.00");
070    
071            testFormat(translator, new Integer(10), "$10.00");
072        }
073    
074        public void test_Initializer_Format()
075        {
076            NumberTranslator translator = new NumberTranslator("pattern=#0%");
077    
078            testFormat(translator, new Double(0.10), "10%");
079        }
080    
081        private void testFormat(Translator translator, Number number, String expected)
082        {
083            IFormComponent field = newField();
084    
085            String result = translator.format(field, Locale.ENGLISH, number);
086    
087            assertEquals(result, expected);
088        }
089    
090        public void test_Null_Format()
091        throws Exception
092        {
093            NumberTranslator translator = new NumberTranslator("pattern=0.00,omitZero");
094            IFormComponent field = newField();
095            ValidationMessages messages = newMock(ValidationMessages.class);
096            
097            replay();
098            
099            Number num = (Number)translator.parse(field, messages, "");
100            
101            assert num == null;
102            
103            verify();
104        }
105    
106        public void test_Null_Format_Pattern()
107        {
108            NumberTranslator translator = new NumberTranslator();
109    
110            replay();
111    
112            String result = translator.format(_component, null, null);
113    
114            assertEquals("", result);
115    
116            verify();
117        }
118        
119        public void test_Localized_Format()
120        {
121            IFormComponent field = newField();
122            NumberTranslator translator = new NumberTranslator();
123            translator.setPattern("#,###");
124            
125            replay();
126            
127            Integer input = new Integer(10999999);
128            String result = translator.format(field, Locale.FRENCH, input);
129            DecimalFormatSymbols sym = new DecimalFormatSymbols(Locale.FRENCH);
130            
131            verify();
132            
133            assertEquals(result, "10" + sym.getGroupingSeparator() + "999" + sym.getGroupingSeparator() + "999");
134        }
135        
136        public void test_Default_Parse() throws Exception
137        {
138            NumberTranslator translator = new NumberTranslator();
139    
140            testParse(translator, "0.1", new Double(0.1));
141        }
142    
143        public void test_Custom_Parse() throws Exception
144        {
145            NumberTranslator translator = new NumberTranslator();
146    
147            translator.setPattern("#%");
148    
149            testParse(translator, "10%", new Double(0.1));
150        }
151    
152        public void test_Trimmed_Parse() throws Exception
153        {
154            NumberTranslator translator = new NumberTranslator();
155    
156            translator.setTrim(true);
157    
158            testParse(translator, " 100 ", new Long(100));
159        }
160    
161        private void testParse(Translator translator, String number, Number expected) throws Exception
162        {
163            IFormComponent field = newField();
164            
165            ValidationMessages messages = newMock(ValidationMessages.class);
166    
167            trainGetLocale(messages, Locale.ENGLISH);
168    
169            replay();
170    
171            Number result = (Number) translator.parse(field, messages, number);
172    
173            assertEquals(expected, result);
174    
175            verify();
176    
177        }
178    
179        public void test_Failed_Parse_Default_Message()
180        {
181            NumberTranslator translator = new NumberTranslator();
182            
183            testFailedParse(translator, null);
184        }
185    
186        public void test_Failed_Parse_Custom_Message()
187        {
188            NumberTranslator translator = new NumberTranslator();
189    
190            String message = "Field Name is an invalid number.";
191    
192            translator.setMessage(message);
193    
194            testFailedParse(translator, message);
195        }
196    
197        private void testFailedParse(Translator translator, String messageOverride)
198        {
199            IFormComponent field = newField("Number Field");
200            
201            ValidationMessages messages = newMock(ValidationMessages.class);
202    
203            trainGetLocale(messages, Locale.ENGLISH);
204    
205            trainBuildMessage(
206                    messages,
207                    messageOverride,
208                    ValidationStrings.INVALID_NUMBER,
209                    new Object[]
210                    { "Number Field", "#" },
211                    "invalid number");
212    
213            replay();
214    
215            try
216            {
217                System.out.println(translator.parse(field, messages, "Bad-Number"));
218    
219                unreachable();
220            }
221            catch (ValidatorException e)
222            {
223                assertEquals("invalid number", e.getMessage());
224                assertEquals(ValidationConstraint.NUMBER_FORMAT, e.getConstraint());
225            }
226    
227            verify();
228        }
229    
230        public void test_Render_Contribution()
231        {
232            NumberTranslator translator = new NumberTranslator();
233            IFormComponent field = newField("Number Field", "numberField", 1);
234            
235            JSONObject json = new JSONObject();
236            
237            IMarkupWriter writer = newWriter();
238            IRequestCycle cycle = newCycle();
239            
240            FormComponentContributorContext context = newMock(FormComponentContributorContext.class);
241            
242            expect(context.getProfile()).andReturn(json);
243            
244            trainGetLocale(context, Locale.ENGLISH);
245            
246            trainBuildMessage(context, null, ValidationStrings.INVALID_NUMBER, 
247                    new Object[] { "Number Field", "#" }, "invalid number message");
248            
249            replay();
250            
251            translator.renderContribution(writer, cycle, context, field);
252            
253            verify();
254            
255            assertEquals(json.toString(),
256                    "{\"constraints\":{\"numberField\":" +
257                    "[[tapestry.form.validation.isReal,null,{places:0,decimal:\".\",separator:\"\"}]]}," +
258                    "\"numberField\":{\"constraints\":[\"invalid number message\"]}}");
259        }
260    
261        public void test_Render_Grouping_Separator()
262        {
263            NumberTranslator translator = new NumberTranslator();
264            translator.setPattern("###,##");
265            IFormComponent field = newField("Number Field", "numberField", 1);
266    
267            JSONObject json = new JSONObject();
268    
269            IMarkupWriter writer = newWriter();
270            IRequestCycle cycle = newCycle();
271    
272            FormComponentContributorContext context = newMock(FormComponentContributorContext.class);
273    
274            expect(context.getProfile()).andReturn(json);
275    
276            trainGetLocale(context, Locale.US);
277    
278            trainBuildMessage(context, null, ValidationStrings.INVALID_NUMBER, new String[] { "Number Field", "#,##" },
279                              "invalid number message");
280    
281            replay();
282    
283            translator.renderContribution(writer, cycle, context, field);
284    
285            verify();
286    
287            assertEquals(json.toString(),
288                    "{\"constraints\":{\"numberField\":" +
289                    "[[tapestry.form.validation.isReal,null," +
290                    "{places:0,decimal:\".\",separator:\",\",groupSize:2}]]}," +
291                    "\"numberField\":{\"constraints\":[\"invalid number message\"]}}");
292        }
293    
294        public void test_Message_Render_Contribution()
295        {
296            NumberTranslator translator = new NumberTranslator();
297            IFormComponent field = newField("Number Field", "myfield", 1);
298            
299            String messageOverride = "You entered a bunk value for {0}. I should look like {1}. Watch out for ''this''!";
300    
301            IMarkupWriter writer = newWriter();
302            IRequestCycle cycle = newCycle();
303            
304            JSONObject json = new JSONObject();
305            
306            FormComponentContributorContext context = newMock(FormComponentContributorContext.class);
307    
308            expect(context.getProfile()).andReturn(json);
309            
310            trainGetLocale(context, Locale.ENGLISH);
311            
312            trainBuildMessage(
313                    context,
314                    messageOverride,
315                    ValidationStrings.INVALID_NUMBER,
316                    new Object[] { "Number Field", "#" },
317                    "Blah Blah 'Field Name' Blah.");
318            
319            replay();
320            
321            translator.setMessage(messageOverride);
322    
323            translator.renderContribution(writer, cycle, context, field);
324            
325            verify();
326            
327            assertEquals(json.toString(),
328                    "{\"constraints\":{\"myfield\":[[tapestry.form.validation.isReal,null," +
329                    "{places:0,decimal:\".\",separator:\"\"}]]}," +
330                    "\"myfield\":{\"constraints\":[\"Blah Blah 'Field Name' Blah.\"]}}");
331        }
332        
333        public void test_Trim_Render_Contribution()
334        {
335            IFormComponent field = newField("Number Field", "myfield", 2);
336            
337            NumberTranslator translator = new NumberTranslator();
338            JSONObject json = new JSONObject();
339            
340            IMarkupWriter writer = newWriter();
341            IRequestCycle cycle = newCycle();
342            
343            FormComponentContributorContext context = newMock(FormComponentContributorContext.class);
344            
345            expect(context.getProfile()).andReturn(json);
346            
347            trainGetLocale(context, Locale.ENGLISH);
348            
349            trainBuildMessage(context, null, ValidationStrings.INVALID_NUMBER, new Object[]
350            { "Number Field", "#" }, "invalid number message");
351            
352            expect(context.getProfile()).andReturn(json);
353            
354            replay();
355    
356            translator.setTrim(true);
357            
358            translator.renderContribution(writer, cycle, context, field);
359            
360            verify();
361            
362            assertEquals(json.toString(),
363                    "{\"trim\":[\"myfield\"]," +
364                    "\"constraints\":{\"myfield\":[[tapestry.form.validation.isReal,null," +
365                    "{places:0,decimal:\".\",separator:\"\"}]]}," +
366                    "\"myfield\":{\"constraints\":[\"invalid number message\"]}}");
367        }
368    }