View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.beanutils2.converters;
19  
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertNotNull;
22  import static org.junit.jupiter.api.Assertions.assertThrows;
23  import static org.junit.jupiter.api.Assertions.assertTrue;
24  
25  import java.math.BigDecimal;
26  import java.math.BigInteger;
27  import java.util.Calendar;
28  import java.util.Date;
29  import java.util.Locale;
30  
31  import org.apache.commons.beanutils2.ConversionException;
32  import org.junit.jupiter.api.Test;
33  
34  /**
35   * Abstract base for <Number>Converter classes.
36   *
37   * @param <T> Number type.
38   */
39  abstract class AbstractNumberConverterTest<T extends Number> {
40  
41      /** Test Number values */
42      protected Number[] numbers = new Number[4];
43  
44      protected abstract Class<T> getExpectedType();
45  
46      protected abstract NumberConverter<T> makeConverter();
47  
48      protected abstract NumberConverter<T> makeConverter(T defaultValue);
49  
50      /**
51       * Convert Boolean --> Number (default conversion)
52       */
53      @Test
54      public void testBooleanToNumberDefault() {
55          final NumberConverter<T> converter = makeConverter();
56  
57          // Other type --> String conversion
58          assertEquals(0, ((Number) converter.convert(getExpectedType(), Boolean.FALSE)).intValue(), "Boolean.FALSE to Number ");
59          assertEquals(1, ((Number) converter.convert(getExpectedType(), Boolean.TRUE)).intValue(), "Boolean.TRUE to Number ");
60      }
61  
62      /**
63       * Convert Calendar --> Long
64       */
65      @Test
66      public void testCalendarToNumber() {
67          final NumberConverter<T> converter = makeConverter();
68  
69          final Calendar calendarValue = Calendar.getInstance();
70          final long longValue = calendarValue.getTime().getTime();
71  
72          // Calendar --> Long conversion
73          assertEquals(Long.valueOf(longValue), converter.convert(Long.class, calendarValue), "Calendar to Long");
74  
75          // Calendar --> Integer
76          assertThrows(ConversionException.class, () -> converter.convert(Integer.class, calendarValue), "Calendar to Integer - expected a ConversionException");
77      }
78  
79      /**
80       * Assumes ConversionException in response to covert(getExpectedType(),null).
81       */
82      @Test
83      public void testConvertNull() {
84          assertThrows(ConversionException.class, () -> makeConverter().convert(getExpectedType(), null), "Expected ConversionException");
85      }
86  
87      /**
88       * Assumes convert(getExpectedType(),Number) returns some non-null instance of getExpectedType().
89       */
90      @Test
91      public void testConvertNumber() {
92          final String[] message = { "from Byte", "from Short", "from Integer", "from Long", "from Float", "from Double", "from BigDecimal", "from BigInteger",
93                  "from Integer array", };
94  
95          final Object[] number = { Byte.valueOf((byte) 7), Short.valueOf((short) 8), Integer.valueOf(9), Long.valueOf(10), Float.valueOf((float) 11.1),
96                  Double.valueOf(12.2), new BigDecimal("17.2"), new BigInteger("33"),
97                  new Integer[] { Integer.valueOf(3), Integer.valueOf(2), Integer.valueOf(1) } };
98  
99          for (int i = 0; i < number.length; i++) {
100             final Object val = makeConverter().convert(getExpectedType(), number[i]);
101             assertNotNull(val, "Convert " + message[i] + " should not be null");
102             assertTrue(getExpectedType().isInstance(val), "Convert " + message[i] + " should return a " + getExpectedType().getName());
103         }
104     }
105 
106     /**
107      * Convert Date --> Long
108      */
109     @Test
110     public void testDateToNumber() {
111         final NumberConverter<T> converter = makeConverter();
112 
113         final Date dateValue = new Date();
114         final long longValue = dateValue.getTime();
115 
116         // Date --> Long conversion
117         assertEquals(Long.valueOf(longValue), converter.convert(Long.class, dateValue), "Date to Long");
118 
119         // Date --> Integer
120         assertThrows(ConversionException.class, () -> converter.convert(Integer.class, dateValue), "Date to Integer - expected a ConversionException");
121     }
122 
123     /**
124      * Convert Number --> String (using default and specified Locales)
125      */
126     @Test
127     public void testInvalidDefault() {
128         final T defaultvalue = (T) numbers[0];
129         final NumberConverter<T> converter = makeConverter(defaultvalue);
130 
131         // Default String --> Number conversion
132         assertEquals(defaultvalue, converter.convert(getExpectedType(), null), "Invalid null ");
133         assertEquals(defaultvalue, converter.convert(getExpectedType(), "XXXX"), "Default XXXX ");
134     }
135 
136     /**
137      * Convert Number --> String (using default and specified Locales)
138      */
139     @Test
140     public void testInvalidException() {
141         final NumberConverter<T> converter = makeConverter();
142 
143         assertThrows(ConversionException.class, () -> converter.convert(getExpectedType(), null), "Null test, expected ConversionException");
144 
145         assertThrows(ConversionException.class, () -> converter.convert(getExpectedType(), "XXXX"), "Invalid test, expected ConversionException");
146     }
147 
148     /**
149      * Test specifying an invalid type.
150      */
151     @Test
152     public void testInvalidType() {
153         final NumberConverter<T> converter = makeConverter();
154 
155         assertThrows(ConversionException.class, () -> converter.convert(Object.class, numbers[0]), "Invalid type test, expected ConversionException");
156     }
157 
158     /**
159      * Tests a conversion to an unsupported type if a default value is set.
160      */
161     @Test
162     public void testInvalidTypeWithDefault() {
163         final NumberConverter<T> converter = makeConverter((T) numbers[0]);
164 
165         assertThrows(ConversionException.class, () -> converter.convert(Object.class, numbers[0]),
166                 "Invalid type with default test, expected ConversionException");
167     }
168 
169     /**
170      * Convert Number --> String (default conversion)
171      */
172     @Test
173     public void testNumberToStringDefault() {
174         final NumberConverter<T> converter = makeConverter();
175 
176         // Default Number --> String conversion
177         assertEquals(numbers[0].toString(), converter.convert(String.class, numbers[0]), () -> "Default Convert " + numbers[0]);
178         assertEquals(numbers[1].toString(), converter.convert(String.class, numbers[1]), () -> "Default Convert " + numbers[1]);
179     }
180 
181     /**
182      * Convert Number --> String (using default and specified Locales)
183      */
184     @Test
185     public void testNumberToStringLocale() {
186         // Re-set the default Locale to Locale.US
187         final Locale defaultLocale = Locale.getDefault();
188         Locale.setDefault(Locale.US);
189 
190         final NumberConverter<T> converter = makeConverter();
191         converter.setUseLocaleFormat(true);
192 
193         // Default Locale
194         assertEquals("-12", converter.convert(String.class, numbers[0]), () -> "Default Locale " + numbers[0]);
195         assertEquals("13", converter.convert(String.class, numbers[1]), () -> "Default Locale " + numbers[1]);
196 
197         // Locale.GERMAN
198         converter.setLocale(Locale.GERMAN);
199         assertEquals("-22", converter.convert(String.class, numbers[2]), () -> "Locale.GERMAN " + numbers[2]);
200         assertEquals("23", converter.convert(String.class, numbers[3]), () -> "Locale.GERMAN " + numbers[3]);
201 
202         // Restore the default Locale
203         Locale.setDefault(defaultLocale);
204     }
205 
206     /**
207      * Convert Number --> String (using a Pattern, with default and specified Locales)
208      */
209     @Test
210     public void testNumberToStringPattern() {
211         // Re-set the default Locale to Locale.US
212         final Locale defaultLocale = Locale.getDefault();
213         Locale.setDefault(Locale.US);
214 
215         final NumberConverter<T> converter = makeConverter();
216         converter.setPattern("[0,0.0];(0,0.0)");
217 
218         // Default Locale
219         assertEquals("(12.0)", converter.convert(String.class, numbers[0]), () -> "Default Locale " + numbers[0]);
220         assertEquals("[13.0]", converter.convert(String.class, numbers[1]), () -> "Default Locale " + numbers[1]);
221 
222         // Locale.GERMAN
223         converter.setLocale(Locale.GERMAN);
224         assertEquals("(22,0)", converter.convert(String.class, numbers[2]), () -> "Locale.GERMAN " + numbers[2]);
225         assertEquals("[23,0]", converter.convert(String.class, numbers[3]), () -> "Locale.GERMAN " + numbers[3]);
226 
227         // Restore the default Locale
228         Locale.setDefault(defaultLocale);
229     }
230 
231     /**
232      * Convert Other --> String (default conversion)
233      */
234     @Test
235     public void testOtherToStringDefault() {
236         final NumberConverter<T> converter = makeConverter();
237 
238         // Other type --> String conversion
239         assertEquals("ABC", converter.convert(String.class, new StringBuilder("ABC")), "Default Convert ");
240     }
241 
242     /**
243      * Convert Array --> Number
244      */
245     @Test
246     public void testStringArrayToInteger() {
247         final Integer defaultValue = Integer.valueOf(-1);
248         final NumberConverter<Integer> converter = new IntegerConverterTest().makeConverter(defaultValue);
249 
250         // Default Locale
251         assertEquals(Integer.valueOf(5), converter.convert(Integer.class, new String[] { "5", "4", "3" }), "Valid First");
252         assertEquals(defaultValue, converter.convert(Integer.class, new String[] { "FOO", "1", "2" }), "Invalid First");
253         assertEquals(defaultValue, converter.convert(Integer.class, new String[] { null, "1", "2" }), "Null First");
254         assertEquals(Integer.valueOf(9), converter.convert(Integer.class, new long[] { 9, 2, 6 }), "Long Array");
255     }
256 
257     /**
258      * Convert String --> Number (default conversion)
259      */
260     @Test
261     public void testStringToNumberDefault() {
262         final NumberConverter<T> converter = makeConverter();
263         converter.setUseLocaleFormat(false);
264 
265         // Default String --> Number conversion
266         assertEquals(numbers[0], converter.convert(getExpectedType(), numbers[0].toString()), () -> "Default Convert " + numbers[0]);
267 
268         // Invalid
269         assertThrows(ConversionException.class, () -> converter.convert(getExpectedType(), "12x"), "Expected invalid value to cause ConversionException");
270     }
271 
272     /**
273      * Convert String --> Number if the target type is not defined. Then the default type should be used.
274      */
275     @Test
276     public void testStringToNumberDefaultType() {
277         final NumberConverter<T> converter = makeConverter();
278         converter.setUseLocaleFormat(false);
279 
280         assertEquals(numbers[0], converter.convert(null, numbers[0].toString()), () -> "Default Convert " + numbers[0]);
281     }
282 
283     /**
284      * Convert String --> Number (using default and specified Locales)
285      */
286     @Test
287     public void testStringToNumberLocale() {
288         // Re-set the default Locale to Locale.US
289         final Locale defaultLocale = Locale.getDefault();
290         Locale.setDefault(Locale.US);
291 
292         final NumberConverter<T> converter = makeConverter();
293         converter.setUseLocaleFormat(true);
294 
295         // Default Locale
296         assertEquals(numbers[0], converter.convert(getExpectedType(), "-0,012"), () -> "Default Locale " + numbers[0]);
297         assertEquals(numbers[1], converter.convert(getExpectedType(), "0,013"), () -> "Default Locale " + numbers[1]);
298 
299         // Invalid Value
300         assertThrows(ConversionException.class, () -> converter.convert(getExpectedType(), "0,02x"), "Expected invalid value to cause ConversionException");
301 
302         // Locale.GERMAN
303         converter.setLocale(Locale.GERMAN);
304         assertEquals(numbers[2], converter.convert(getExpectedType(), "-0.022"), () -> "Locale.GERMAN " + numbers[2]);
305         assertEquals(numbers[3], converter.convert(getExpectedType(), "0.023"), () -> "Locale.GERMAN " + numbers[3]);
306 
307         // Invalid Value
308         assertThrows(ConversionException.class, () -> converter.convert(getExpectedType(), "0.02x"), "Expected invalid value to cause ConversionException");
309 
310         // Restore the default Locale
311         Locale.setDefault(defaultLocale);
312     }
313 
314     /**
315      * Convert String --> Number (using a Pattern, with default and specified Locales)
316      */
317     @Test
318     public void testStringToNumberPattern() {
319 
320         // Re-set the default Locale to Locale.US
321         final Locale defaultLocale = Locale.getDefault();
322         Locale.setDefault(Locale.US);
323 
324         final NumberConverter<T> converter = makeConverter();
325         converter.setPattern("[0,0];(0,0)");
326 
327         // Default Locale
328         assertEquals(numbers[0], converter.convert(getExpectedType(), "(1,2)"), () -> "Default Locale " + numbers[0]);
329         assertEquals(numbers[1], converter.convert(getExpectedType(), "[1,3]"), () -> "Default Locale " + numbers[1]);
330 
331         // Locale.GERMAN
332         converter.setLocale(Locale.GERMAN);
333         assertEquals(numbers[2], converter.convert(getExpectedType(), "(2.2)"), () -> "Locale.GERMAN " + numbers[2]);
334         assertEquals(numbers[3], converter.convert(getExpectedType(), "[2.3]"), () -> "Locale.GERMAN " + numbers[3]);
335 
336         // Invalid Value
337         assertThrows(ConversionException.class, () -> converter.convert(getExpectedType(), "1,2"), "Expected invalid value to cause ConversionException");
338 
339         // Invalid Type (will try via String)
340         final Object obj = new Object() {
341             @Override
342             public String toString() {
343                 return "dsdgsdsdg";
344             }
345         };
346         assertThrows(ConversionException.class, () -> converter.convert(getExpectedType(), obj), "Expected invalid value to cause ConversionException");
347 
348         // Restore the default Locale
349         Locale.setDefault(defaultLocale);
350     }
351 
352     @Test
353     public void testToString() {
354         assertNotNull(makeConverter().toString());
355     }
356 
357 }