Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
NumberUtils |
|
| 12.0;12 |
1 | package liquibase.util; | |
2 | ||
3 | import java.math.BigDecimal; | |
4 | import java.math.BigInteger; | |
5 | import java.text.NumberFormat; | |
6 | import java.text.ParseException; | |
7 | ||
8 | /** | |
9 | * Miscellaneous utility methods for number conversion and parsing. Mainly for internal use within the framework; | |
10 | * consider Jakarta's Commons Lang for a more comprehensive suite of string utilities. | |
11 | * | |
12 | * @author Juergen Hoeller | |
13 | * @author Rob Harrop | |
14 | * @since 1.1.2 | |
15 | */ | |
16 | 0 | public abstract class NumberUtils { |
17 | ||
18 | /** | |
19 | * Convert the given number into an instance of the given target class. | |
20 | * | |
21 | * @param number | |
22 | * the number to convert | |
23 | * @param targetClass | |
24 | * the target class to convert to | |
25 | * @return the converted number | |
26 | * @throws IllegalArgumentException | |
27 | * if the target class is not supported (i.e. not a standard Number subclass as included in the JDK) | |
28 | * @see java.lang.Byte | |
29 | * @see java.lang.Short | |
30 | * @see java.lang.Integer | |
31 | * @see java.lang.Long | |
32 | * @see java.math.BigInteger | |
33 | * @see java.lang.Float | |
34 | * @see java.lang.Double | |
35 | * @see java.math.BigDecimal | |
36 | */ | |
37 | public static Number convertNumberToTargetClass(Number number, Class targetClass) throws IllegalArgumentException { | |
38 | ||
39 | 0 | if (targetClass.isInstance(number)) { |
40 | 0 | return number; |
41 | 0 | } else if (targetClass.equals(Byte.class)) { |
42 | 0 | long value = number.longValue(); |
43 | 0 | if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) { |
44 | 0 | raiseOverflowException(number, targetClass); |
45 | } | |
46 | 0 | return number.byteValue(); |
47 | 0 | } else if (targetClass.equals(Short.class)) { |
48 | 0 | long value = number.longValue(); |
49 | 0 | if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) { |
50 | 0 | raiseOverflowException(number, targetClass); |
51 | } | |
52 | 0 | return number.shortValue(); |
53 | 0 | } else if (targetClass.equals(Integer.class)) { |
54 | 0 | long value = number.longValue(); |
55 | 0 | if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) { |
56 | 0 | raiseOverflowException(number, targetClass); |
57 | } | |
58 | 0 | return number.intValue(); |
59 | 0 | } else if (targetClass.equals(Long.class)) { |
60 | 0 | return number.longValue(); |
61 | 0 | } else if (targetClass.equals(Float.class)) { |
62 | 0 | return number.floatValue(); |
63 | 0 | } else if (targetClass.equals(Double.class)) { |
64 | 0 | return number.doubleValue(); |
65 | 0 | } else if (targetClass.equals(BigInteger.class)) { |
66 | 0 | return BigInteger.valueOf(number.longValue()); |
67 | 0 | } else if (targetClass.equals(BigDecimal.class)) { |
68 | // using BigDecimal(String) here, to avoid unpredictability of BigDecimal(double) | |
69 | // (see BigDecimal javadoc for details) | |
70 | 0 | return new BigDecimal(number.toString()); |
71 | } else { | |
72 | 0 | throw new IllegalArgumentException("Could not convert number [" + number + "] of type [" |
73 | + number.getClass().getName() + "] to unknown target class [" + targetClass.getName() + "]"); | |
74 | } | |
75 | } | |
76 | ||
77 | /** | |
78 | * Raise an overflow exception for the given number and target class. | |
79 | * | |
80 | * @param number | |
81 | * the number we tried to convert | |
82 | * @param targetClass | |
83 | * the target class we tried to convert to | |
84 | */ | |
85 | private static void raiseOverflowException(Number number, Class targetClass) { | |
86 | 0 | throw new IllegalArgumentException("Could not convert number [" + number + "] of type [" |
87 | + number.getClass().getName() + "] to target class [" + targetClass.getName() + "]: overflow"); | |
88 | } | |
89 | ||
90 | /** | |
91 | * Parse the given text into a number instance of the given target class, using the corresponding default | |
92 | * <code>decode</code> methods. Trims the input <code>String</code> before attempting to parse the number. Supports | |
93 | * numbers in hex format (with leading 0x) and in octal format (with leading 0). | |
94 | * | |
95 | * @param text | |
96 | * the text to convert | |
97 | * @param targetClass | |
98 | * the target class to parse into | |
99 | * @return the parsed number | |
100 | * @throws IllegalArgumentException | |
101 | * if the target class is not supported (i.e. not a standard Number subclass as included in the JDK) | |
102 | * @see java.lang.Byte#decode | |
103 | * @see java.lang.Short#decode | |
104 | * @see java.lang.Integer#decode | |
105 | * @see java.lang.Long#decode | |
106 | * @see #decodeBigInteger(String) | |
107 | * @see java.lang.Float#valueOf | |
108 | * @see java.lang.Double#valueOf | |
109 | * @see java.math.BigDecimal#BigDecimal(String) | |
110 | */ | |
111 | public static Number parseNumber(String text, Class targetClass) { | |
112 | 0 | String trimmed = text.trim(); |
113 | ||
114 | 0 | if (targetClass.equals(Byte.class)) { |
115 | 0 | return Byte.decode(trimmed); |
116 | 0 | } else if (targetClass.equals(Short.class)) { |
117 | 0 | return Short.decode(trimmed); |
118 | 0 | } else if (targetClass.equals(Integer.class)) { |
119 | 0 | return Integer.decode(trimmed); |
120 | 0 | } else if (targetClass.equals(Long.class)) { |
121 | 0 | return Long.decode(trimmed); |
122 | 0 | } else if (targetClass.equals(BigInteger.class)) { |
123 | 0 | return decodeBigInteger(trimmed); |
124 | 0 | } else if (targetClass.equals(Float.class)) { |
125 | 0 | return Float.valueOf(trimmed); |
126 | 0 | } else if (targetClass.equals(Double.class)) { |
127 | 0 | return Double.valueOf(trimmed); |
128 | 0 | } else if (targetClass.equals(BigDecimal.class) || targetClass.equals(Number.class)) { |
129 | 0 | return new BigDecimal(trimmed); |
130 | } else { | |
131 | 0 | throw new IllegalArgumentException("Cannot convert String [" + text + "] to target class [" |
132 | + targetClass.getName() + "]"); | |
133 | } | |
134 | } | |
135 | ||
136 | /** | |
137 | * Parse the given text into a number instance of the given target class, using the given NumberFormat. Trims the | |
138 | * input <code>String</code> before attempting to parse the number. | |
139 | * | |
140 | * @param text | |
141 | * the text to convert | |
142 | * @param targetClass | |
143 | * the target class to parse into | |
144 | * @param numberFormat | |
145 | * the NumberFormat to use for parsing (if <code>null</code>, this method falls back to | |
146 | * <code>parseNumber(String, Class)</code>) | |
147 | * @return the parsed number | |
148 | * @throws IllegalArgumentException | |
149 | * if the target class is not supported (i.e. not a standard Number subclass as included in the JDK) | |
150 | * @see java.text.NumberFormat#parse | |
151 | * @see #convertNumberToTargetClass | |
152 | * @see #parseNumber(String,Class) | |
153 | */ | |
154 | public static Number parseNumber(String text, Class targetClass, NumberFormat numberFormat) { | |
155 | 0 | if (numberFormat != null) { |
156 | try { | |
157 | 0 | Number number = numberFormat.parse(text.trim()); |
158 | 0 | return convertNumberToTargetClass(number, targetClass); |
159 | 0 | } catch (ParseException ex) { |
160 | 0 | throw new IllegalArgumentException(ex.getMessage()); |
161 | } | |
162 | } else { | |
163 | 0 | return parseNumber(text, targetClass); |
164 | } | |
165 | } | |
166 | ||
167 | /** | |
168 | * Decode a {@link java.math.BigInteger} from a {@link String} value. Supports decimal, hex and octal notation. | |
169 | * | |
170 | * @see BigInteger#BigInteger(String,int) | |
171 | */ | |
172 | private static BigInteger decodeBigInteger(String value) { | |
173 | 0 | int radix = 10; |
174 | 0 | int index = 0; |
175 | 0 | boolean negative = false; |
176 | ||
177 | // Handle minus sign, if present. | |
178 | 0 | if (value.startsWith("-")) { |
179 | 0 | negative = true; |
180 | 0 | index++; |
181 | } | |
182 | ||
183 | // Handle radix specifier, if present. | |
184 | 0 | if (value.startsWith("0x", index) || value.startsWith("0X", index)) { |
185 | 0 | index += 2; |
186 | 0 | radix = 16; |
187 | 0 | } else if (value.startsWith("#", index)) { |
188 | 0 | index++; |
189 | 0 | radix = 16; |
190 | 0 | } else if (value.startsWith("0", index) && value.length() > 1 + index) { |
191 | 0 | index++; |
192 | 0 | radix = 8; |
193 | } | |
194 | ||
195 | 0 | BigInteger result = new BigInteger(value.substring(index), radix); |
196 | 0 | return (negative ? result.negate() : result); |
197 | } | |
198 | ||
199 | } |