1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    * 
10   *  http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   */
19  package org.apache.myfaces.trinidad.convert;
20  
21  import java.util.Locale;
22  
23  import javax.faces.convert.ConverterException;
24  import javax.faces.convert.NumberConverter;
25  import javax.faces.component.UIComponent;
26  
27  import org.apache.myfaces.trinidad.convert.ConverterTestCase;
28  import org.apache.myfaces.trinidadbuild.test.MockUIComponentWrapper;
29  import org.apache.shale.test.mock.MockFacesContext;
30  import org.jmock.Mock;
31  
32  /**
33   * Test NumberConverter
34   * @version $Name: $ ($version: $) $Date: 16-aug-2005.15:12:23 $
35   */
36  public abstract class NumberConverterTestCase extends ConverterTestCase
37  {
38    public NumberConverterTestCase(String name)
39    {
40      super(name);
41    }
42  
43    /**
44     * Test when context is set to null
45     */
46    public void testNullContext()
47    {
48      Mock mock = mock(UIComponent.class);
49      UIComponent component = (UIComponent) mock.proxy();
50      MockUIComponentWrapper wrapper = new MockUIComponentWrapper(mock, component);
51      NumberConverter converter = getNumberConverter();
52  
53      doTestNullContext(wrapper, converter);
54    }
55  
56    public void testNullComponent()
57    {
58      NumberConverter converter  = getNumberConverter();
59  
60      doTestNullComponent(facesContext, converter);
61    }
62  
63    /**
64     * Tests that null returns immediately.
65     *
66     * @throws ConverterException  when test fails
67     */
68    public void testNullInputValue() throws ConverterException
69    {
70      Mock mock = mock(UIComponent.class);
71      UIComponent component = (UIComponent) mock.proxy();
72      MockUIComponentWrapper wrapper = new MockUIComponentWrapper(mock, component);
73      NumberConverter converter  = getNumberConverter();
74  
75      doTestNull(facesContext, wrapper, converter);
76    }
77  
78    public void testEmptyValueConversion()
79    {
80      super.doTestBlankValue(getNumberConverter());
81    }
82  
83    public void testValueType()
84    {
85      Mock mock = mock(UIComponent.class);
86      UIComponent component = (UIComponent) mock.proxy();
87      setFacesContext(facesContext);
88      try
89      {
90        String input = "123";
91        NumberConverter converter = getNumberConverter();
92        Object number = converter.getAsObject(facesContext, component, input);
93        assertEquals(true, number instanceof Number);
94        assertEquals(true, (((Number)number).intValue() == 123));
95  
96        String outVal = converter.getAsString(facesContext, component, number);
97        assertEquals(input, outVal);
98      }
99      finally
100     {
101       setFacesContext(null);
102     }
103     mock.verify();
104   }
105 
106   public void testAppropriateFormatsArePicked()
107   {
108     // check if appropriate formtas based on the types are chosen.
109     // like numeric, currency, percent are picked. Chose pattern if set avoiding
110     // types
111 
112     String[] patterns = {"##,##",null, null, null,null};
113 
114     //pick pattern, numeric, percent, currency
115     String[] types = {null,"number", "percent", "currency", "currency"};
116     String[] inputValues = {"99,99","99", "99%","$99", "$99.00"} ;
117     Number[] expectedValues = {new Long(9999), new Long(99), new Double(0.99), new Long(99), new Long(99)};
118     String[] expectedStringValues = {"99,99","99", "99%","$99.00", "$99.00"} ;
119     Locale usLocl = Locale.US;
120     Locale[] locales = {usLocl, usLocl, usLocl, usLocl,Locale.CANADA};
121 
122     NumberConverter nconv = getNumberConverter();
123 
124     for (int i = 0; i < patterns.length; i++)
125     {
126       Mock mock = mock(UIComponent.class);
127       UIComponent component = (UIComponent) mock.proxy();
128 
129       setFacesContext(facesContext);
130       try
131       {
132         nconv.setPattern(patterns[i]);
133         nconv.setType(types[i]);
134         nconv.setLocale(locales[i]);
135         
136         Object convValue = nconv.getAsObject(facesContext, component, inputValues[i]);
137         assertEquals(expectedValues[i], convValue);
138         
139         String outValue = nconv.getAsString(facesContext, component, expectedValues[i]);
140         
141         assertEquals(expectedStringValues[i], outValue);
142       }
143       finally
144       {
145         setFacesContext(null);
146       }
147       mock.verify();
148     }
149   }
150 
151   public void testStateHolderSaveRestore()
152   {
153     NumberConverter converter = getNumberConverter();
154 
155     NumberConverter restoreConverter = getNumberConverter();
156     Mock mock = mock(UIComponent.class);
157     UIComponent component = (UIComponent) mock.proxy();
158     MockUIComponentWrapper wrapper = new MockUIComponentWrapper(mock, component);
159 
160     for (int i = 0; i < _LOCALES.length; i++)
161     {
162       converter.setLocale(_LOCALES[i]);
163       restoreConverter.setLocale(_LOCALES[i]);
164       doTestStateHolderSaveRestore(converter, restoreConverter, facesContext, wrapper);
165 
166       converter.setCurrencyCode( _CURRENCY_CODES[i]);
167       restoreConverter.setCurrencyCode( _CURRENCY_CODES[i]);
168       doTestStateHolderSaveRestore(converter, restoreConverter, facesContext, wrapper);
169 
170       converter.setCurrencySymbol(_CURRENCY_SYMBOLS[i]);
171       restoreConverter.setCurrencySymbol(_CURRENCY_SYMBOLS[i]);
172       doTestStateHolderSaveRestore(converter, restoreConverter, facesContext, wrapper);
173 
174       converter.setIntegerOnly(_INTEGER_ONLY[1]);
175       restoreConverter.setIntegerOnly(_INTEGER_ONLY[1]);
176       doTestStateHolderSaveRestore(converter, restoreConverter, facesContext, wrapper);
177 
178       converter.setMaxFractionDigits(_MAX_FRACTION_DIGITS[i]);
179       restoreConverter.setMaxFractionDigits(_MAX_FRACTION_DIGITS[i]);
180       doTestStateHolderSaveRestore(converter, restoreConverter, facesContext, wrapper);
181 
182       converter.setMaxIntegerDigits(_MAX_INT_DIGITS[i]);
183       restoreConverter.setMaxIntegerDigits(_MAX_INT_DIGITS[i]);
184       doTestStateHolderSaveRestore(converter, restoreConverter, facesContext, wrapper);
185 
186       converter.setMinFractionDigits(_MIN_FRACT_DIGITS[i]);
187       restoreConverter.setMinFractionDigits(_MIN_FRACT_DIGITS[i]);
188       doTestStateHolderSaveRestore(converter, restoreConverter, facesContext, wrapper);
189 
190       converter.setMinIntegerDigits(_MIN_INT_DIGITS[i]);
191       restoreConverter.setMinIntegerDigits(_MIN_INT_DIGITS[i]);
192       doTestStateHolderSaveRestore(converter, restoreConverter, facesContext, wrapper);
193 
194       converter.setPattern( _PATTTERNS[i]);
195       restoreConverter.setPattern(_PATTTERNS[i]);
196       doTestStateHolderSaveRestore(converter, restoreConverter, facesContext, wrapper);
197 
198       converter.setTransient(_TRANSIENT[i]);
199       restoreConverter.setTransient(_TRANSIENT[i]);
200       doTestStateHolderSaveRestore(converter, restoreConverter, facesContext, wrapper);
201 
202       converter.setType(_TYPES[i]);
203       doTestStateHolderSaveRestore(converter, restoreConverter, facesContext, wrapper);
204 
205     }
206   }
207 
208 //  public void testFranceLocale()
209 //  {
210 //    NumberConverter converter = getNumberConverter();
211 //    Mock mock = mock(UIComponent.class);
212 //    UIComponent comp = (UIComponent) mock.proxy();
213 //    
214 //    converter.setLocale(Locale.FRANCE);
215 //    converter.setType("currency");
216 //    Double d = new Double(12345.68d);
217 //    
218 //    setFacesContext(facesContext);
219 //    try
220 //    {
221 //      String convertedString = converter.getAsString(facesContext, comp, d);
222 //      assertEquals("12 345,68 €", convertedString);
223 //    }
224 //    finally
225 //    {
226 //      setFacesContext(null);
227 //    }
228 //    mock.verify();
229 //  }
230   
231   public void testCurrencyCodeIsHonoured()
232   {
233     NumberConverter converter = getNumberConverter();
234     Mock mock = mock(UIComponent.class);
235     UIComponent component = (UIComponent) mock.proxy();
236 
237     converter.setLocale(Locale.US);
238     converter.setType("currency");
239     Double  value = new Double(99);
240 
241     setFacesContext(facesContext);
242     try
243     {
244       String outPut = converter.getAsString(facesContext, component, value);
245       assertEquals("$99.00", outPut);
246       //Locale is US. By general convention the output prefix would be '$'
247       // since we set the currency code to 'DEM' value should be DEM[value]
248       converter.setCurrencyCode("DEM");
249       
250       outPut = converter.getAsString(facesContext, component, value);
251       assertEquals("DEM99.00", outPut);
252     }
253     finally
254     {
255       setFacesContext(null);
256     }
257     mock.verify();
258   }
259 
260   public void testCurrencyCodeIsHonouredWhenCurrencyCodeAndCurrencySymbolIsSet()
261   {
262     NumberConverter converter   = getNumberConverter();
263     Mock mock = buildMockUIComponent(2);
264     UIComponent component = (UIComponent) mock.proxy();
265 
266     converter.setLocale(Locale.US);
267     converter.setType("currency");
268     Double value = new Double(99);
269 
270     setFacesContext(facesContext);
271     try
272     {
273       String outPut = converter.getAsString(facesContext, component, value);
274       assertEquals("$99.00", outPut);
275       //Locale is US. By general convention the output prefix would be '$'
276       // since we set the currency code to 'DEM' value should be DEM[value]
277       converter.setCurrencyCode("DEM");
278       
279       // Let us set the symbol to '*'. This should not take effect, since currency
280       // code is set.
281       converter.setCurrencySymbol("*");
282       
283       outPut = converter.getAsString(facesContext, component, value);
284       assertEquals("DEM99.00", outPut);
285       try
286       {
287         if(converter.getAsObject(facesContext, component, "DEM99.00") instanceof Number)
288         {
289           fail("Exception should occur - since currency should not be considered while formatting");
290         }
291       }
292       catch(Exception e)
293       {
294         ;//Expected to fail.
295       }
296     }
297     finally
298     {
299       setFacesContext(null);
300     }
301     mock.verify();
302   }
303 
304   public void testCurrencySymbolIsHonoured()
305   {
306     NumberConverter converter = getNumberConverter();
307     Mock mock = mock(UIComponent.class);
308     UIComponent component = (UIComponent) mock.proxy();
309     converter.setLocale(Locale.US);
310     converter.setType("currency");
311     Double  value = new Double(99);
312     //Locale is US. By general convention the output prefix would be '$'
313     // since we set currency symbol to '*' we should get the value to be *99.00
314     converter.setCurrencySymbol("*");
315     
316     setFacesContext(facesContext);
317     try
318     {
319       String outPut = converter.getAsString(facesContext, component, value);
320       assertEquals("*99.00", outPut);
321     }
322     finally
323     {
324       setFacesContext(null);
325     }
326     mock.verify();
327   }
328 
329   public void testIntegerOnlyIsHonoured()
330   {
331     // integerOnly is used only while parsing to create number objects
332     NumberConverter converter = getNumberConverter();
333     Mock mock = mock(UIComponent.class);
334     UIComponent component = (UIComponent) mock.proxy();
335     converter.setLocale(Locale.US);
336 
337     String[] inputs = {"23.10", "44.90876", "11111", "67859.0001"};
338     Number[] expectedValues = {new Long(23), new Long(44), new Long(11111), new Long(67859)};
339 
340     setFacesContext(facesContext);
341     try
342     {
343       for (int i = 0; i < inputs.length; i++)
344       {
345         converter.setIntegerOnly(true);
346         Number num = (Number) converter.getAsObject(facesContext, component, inputs[i]);
347         assertEquals(expectedValues[i], num);
348       }
349     }
350     finally
351     {
352       setFacesContext(null);
353     }
354     mock.verify();
355   }
356 
357 
358   public void testSettingFractDigitsAndSettingMinDigitsDoesNotAffectParsing()
359   {
360     // integerOnly is used only while parsing to create number objects
361     NumberConverter converter = getNumberConverter();
362     Mock mock = mock(UIComponent.class);
363     UIComponent component = (UIComponent) mock.proxy();
364     converter.setLocale(Locale.US);
365 
366     String[] inputs = {"23.10", "44.90876", "11111", "67859.0001"};
367     Number[] expectedValues = {new Long(23), new Long(44), new Long(11111), new Long(67859)};
368 
369     setFacesContext(facesContext);
370     try
371     {
372       for (int i = 0; i < inputs.length; i++)
373       {
374         // setting these values should not affect parsing.
375         converter.setMaxFractionDigits(10);
376         converter.setMaxIntegerDigits(1);
377         converter.setMinFractionDigits(1);
378         converter.setMinFractionDigits(0);
379         
380         // this should be taken care by the parsing code
381         converter.setIntegerOnly(true);
382         Number num = (Number) converter.getAsObject(facesContext, component, inputs[i]);
383         assertEquals(expectedValues[i], num);
384       }
385     }
386     finally
387     {
388       setFacesContext(null);
389     }
390     mock.verify();
391   }
392 
393   public void testLocaleIsPickedUpFromViewRoot()
394   {
395 
396     NumberConverter converter = getNumberConverter();
397     Mock mock = mock(UIComponent.class);
398     UIComponent component = (UIComponent) mock.proxy();
399 
400     facesContext.getViewRoot().setLocale(Locale.US);
401 
402     String input = "1234.56";
403 
404     setFacesContext(facesContext);
405     try
406     {
407       // if we get a valid object, implies locale was indeed picked up.
408       // otherwise we would have got a null pointer exception or other exception
409       Object value = converter.getAsObject(facesContext, component, input);
410       assertEquals(new Double(1234.56), value);
411     }
412     finally
413     {
414       setFacesContext(null);
415     }
416   }
417 
418   public void testGroupingIsHonoured()
419   {
420     Number[] inputValues = {new Long(9999), new Long(99), new Double(0.99), new Double(99999.567), new Long(9999)};
421     boolean [] isGroupingUsed = {true, true, true, false, false };
422     String[] expectedValues = {"9,999", "99", "0.99", "99999.567", "9999"};
423 
424     NumberConverter converter = getNumberConverter();
425     Mock mock = mock(UIComponent.class);
426     UIComponent component = (UIComponent) mock.proxy();
427     converter.setLocale(Locale.US);
428     setFacesContext(facesContext);
429     try
430     {
431       for (int i = 0; i < inputValues.length; i++)
432       {
433         converter.setGroupingUsed(isGroupingUsed[i]);
434         String out = converter.getAsString(facesContext, component, inputValues[i]);
435         assertEquals(expectedValues[i], out);
436       }
437     }
438     finally
439     {
440       setFacesContext(null);
441     }
442     mock.verify();
443   }
444 
445   public void testStrictnessOfConversion()
446   {
447     String[] inputValues = {"123ABC", "22.22.2" };
448     Mock mock = buildMockUIComponent(inputValues.length * 3);
449     UIComponent component = (UIComponent) mock.proxy();
450     MockUIComponentWrapper wrapper = new MockUIComponentWrapper(mock, component);
451 
452     for (int i = 0; i < inputValues.length; i++)
453     {
454       doTestStrictNess(facesContext, wrapper, Locale.US, inputValues[i]);
455     }
456     mock.verify();
457   }
458 
459   public void testSettingFractDigitsAndSettingMinDigitsAreHononured()
460   {
461     Number[] inputValues = {new Long(1234), new Double(1234.5678), new Double(1234), new Double(10.00)};
462     String[] expectedValues = {"1,234", "34.57", "1,234", "10.00"};
463 
464     int[] maxFractDigits = {0, 2, 2, 2};
465     int[] maxIntDigits   = {4, 2, 4, 3};
466     int[] minIntDigits   = {4, 1, 2, 1};
467     int[] minFractDigits = {0, 2, 0, 2};
468 
469     NumberConverter converter   = getNumberConverter();
470     Mock mock = mock(UIComponent.class);
471     UIComponent component = (UIComponent) mock.proxy();
472 
473     setFacesContext(facesContext);
474     try
475     {
476       converter.setLocale(Locale.US);
477       for (int i = 0; i < maxFractDigits.length; i++)
478       {
479         converter.setMaxFractionDigits(maxFractDigits[i]);
480         converter.setMaxIntegerDigits(maxIntDigits[i]);
481         converter.setMinFractionDigits(minFractDigits[i]);
482         converter.setMinIntegerDigits(minIntDigits[i]);
483         
484         String out = converter.getAsString(facesContext, component, inputValues[i]);
485         assertEquals(expectedValues[i], out);   
486       }
487     }
488     finally
489     {
490       setFacesContext(null);
491     }
492     mock.verify();
493   }
494 
495   protected abstract NumberConverter getNumberConverter();
496 
497   protected abstract void doTestStrictNess(
498     MockFacesContext context,
499     MockUIComponentWrapper wrapper,
500     Locale locale,
501     String inputValue);
502 
503   private static final String[] _CURRENCY_CODES = {"USD", "DEM" };
504 
505   private static final String[] _CURRENCY_SYMBOLS = {"*", "!"};
506 
507   private static final Locale[] _LOCALES = {Locale.US, Locale.GERMAN};
508 
509   private static final int[] _MAX_FRACTION_DIGITS = {2, 3};
510 
511   private static final int[] _MAX_INT_DIGITS = {5, 6};
512 
513   private static final int[] _MIN_FRACT_DIGITS = {2, 3};
514 
515   private static final int[] _MIN_INT_DIGITS = {2, 3};
516 
517   private static final String[] _PATTTERNS = {"##,##", null};
518 
519   private static final String[] _TYPES = {"currency","percent"};
520 
521   // -= Simon Lessard =-
522   // TODO: Never read locally as of 2006-08-09. Remove whenever possible
523   //       or implements a grouping test using this constant.
524   //private static final boolean[] _GROUPING = {true, false};
525 
526   private static final boolean[] _INTEGER_ONLY = {true, false};
527 
528   private static final boolean[] _TRANSIENT = {true, false};
529 
530 }
531 // DONOT DELETE LET THESE STAY HERE.
532 // CurrencyCode
533 // CurrencySymbol
534 // Locale
535 // MaxFractionDigits
536 // MaxIntegerDigits
537 // MinFractionDigits
538 // MinIntegerDigits
539 // Pattern
540 // Type
541 // GroupingUsed
542 // IntegerOnly
543 
544 //Currency Code | Country Currency
545 //USD - United States Dollar
546 //
547 //ITL - Italian Lira
548 //
549 //DEM - German Mark
550 //
551 //HKG - Hong Kong Dollar
552 //
553 //MXN - Mexican Peso
554 //
555 //EUR - Euro
556 
557 // CurrencyCode        tested
558 // CurrencySymbol      tested
559 // Locale              tested  to pick up from viewRoot
560 
561 
562 // MaxFractionDigits      only while formatting
563 // MaxIntegerDigits       only while formatting
564 // MinFractionDigits      only while formatting
565 // MinIntegerDigits       only while formatting
566 
567 // Pattern             tested
568 // Type                tested
569 // GroupingUsed        tested
570 // IntegerOnly         tested   // only while parsing