001: package liquibase.util;
002:
003: import java.math.BigDecimal;
004: import java.math.BigInteger;
005: import java.text.NumberFormat;
006: import java.text.ParseException;
007:
008: /**
009: * Miscellaneous utility methods for number conversion and parsing.
010: * Mainly for internal use within the framework; consider Jakarta's
011: * Commons Lang for a more comprehensive suite of string utilities.
012: *
013: * @author Juergen Hoeller
014: * @author Rob Harrop
015: * @since 1.1.2
016: */
017: public abstract class NumberUtils {
018:
019: /**
020: * Convert the given number into an instance of the given target class.
021: *
022: * @param number the number to convert
023: * @param targetClass the target class to convert to
024: * @return the converted number
025: * @throws IllegalArgumentException if the target class is not supported
026: * (i.e. not a standard Number subclass as included in the JDK)
027: * @see java.lang.Byte
028: * @see java.lang.Short
029: * @see java.lang.Integer
030: * @see java.lang.Long
031: * @see java.math.BigInteger
032: * @see java.lang.Float
033: * @see java.lang.Double
034: * @see java.math.BigDecimal
035: */
036: public static Number convertNumberToTargetClass(Number number,
037: Class targetClass) throws IllegalArgumentException {
038:
039: if (targetClass.isInstance(number)) {
040: return number;
041: } else if (targetClass.equals(Byte.class)) {
042: long value = number.longValue();
043: if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {
044: raiseOverflowException(number, targetClass);
045: }
046: return number.byteValue();
047: } else if (targetClass.equals(Short.class)) {
048: long value = number.longValue();
049: if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
050: raiseOverflowException(number, targetClass);
051: }
052: return number.shortValue();
053: } else if (targetClass.equals(Integer.class)) {
054: long value = number.longValue();
055: if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
056: raiseOverflowException(number, targetClass);
057: }
058: return number.intValue();
059: } else if (targetClass.equals(Long.class)) {
060: return number.longValue();
061: } else if (targetClass.equals(Float.class)) {
062: return number.floatValue();
063: } else if (targetClass.equals(Double.class)) {
064: return number.doubleValue();
065: } else if (targetClass.equals(BigInteger.class)) {
066: return BigInteger.valueOf(number.longValue());
067: } else if (targetClass.equals(BigDecimal.class)) {
068: // using BigDecimal(String) here, to avoid unpredictability of BigDecimal(double)
069: // (see BigDecimal javadoc for details)
070: return new BigDecimal(number.toString());
071: } else {
072: throw new IllegalArgumentException(
073: "Could not convert number [" + number
074: + "] of type ["
075: + number.getClass().getName()
076: + "] to unknown target class ["
077: + targetClass.getName() + "]");
078: }
079: }
080:
081: /**
082: * Raise an overflow exception for the given number and target class.
083: *
084: * @param number the number we tried to convert
085: * @param targetClass the target class we tried to convert to
086: */
087: private static void raiseOverflowException(Number number,
088: Class targetClass) {
089: throw new IllegalArgumentException("Could not convert number ["
090: + number + "] of type [" + number.getClass().getName()
091: + "] to target class [" + targetClass.getName()
092: + "]: overflow");
093: }
094:
095: /**
096: * Parse the given text into a number instance of the given target class,
097: * using the corresponding default <code>decode</code> methods. Trims the
098: * input <code>String</code> before attempting to parse the number. Supports
099: * numbers in hex format (with leading 0x) and in octal format (with leading 0).
100: *
101: * @param text the text to convert
102: * @param targetClass the target class to parse into
103: * @return the parsed number
104: * @throws IllegalArgumentException if the target class is not supported
105: * (i.e. not a standard Number subclass as included in the JDK)
106: * @see java.lang.Byte#decode
107: * @see java.lang.Short#decode
108: * @see java.lang.Integer#decode
109: * @see java.lang.Long#decode
110: * @see #decodeBigInteger(String)
111: * @see java.lang.Float#valueOf
112: * @see java.lang.Double#valueOf
113: * @see java.math.BigDecimal#BigDecimal(String)
114: */
115: public static Number parseNumber(String text, Class targetClass) {
116: String trimmed = text.trim();
117:
118: if (targetClass.equals(Byte.class)) {
119: return Byte.decode(trimmed);
120: } else if (targetClass.equals(Short.class)) {
121: return Short.decode(trimmed);
122: } else if (targetClass.equals(Integer.class)) {
123: return Integer.decode(trimmed);
124: } else if (targetClass.equals(Long.class)) {
125: return Long.decode(trimmed);
126: } else if (targetClass.equals(BigInteger.class)) {
127: return decodeBigInteger(trimmed);
128: } else if (targetClass.equals(Float.class)) {
129: return Float.valueOf(trimmed);
130: } else if (targetClass.equals(Double.class)) {
131: return Double.valueOf(trimmed);
132: } else if (targetClass.equals(BigDecimal.class)
133: || targetClass.equals(Number.class)) {
134: return new BigDecimal(trimmed);
135: } else {
136: throw new IllegalArgumentException(
137: "Cannot convert String [" + text
138: + "] to target class ["
139: + targetClass.getName() + "]");
140: }
141: }
142:
143: /**
144: * Parse the given text into a number instance of the given target class,
145: * using the given NumberFormat. Trims the input <code>String</code>
146: * before attempting to parse the number.
147: *
148: * @param text the text to convert
149: * @param targetClass the target class to parse into
150: * @param numberFormat the NumberFormat to use for parsing (if <code>null</code>,
151: * this method falls back to <code>parseNumber(String, Class)</code>)
152: * @return the parsed number
153: * @throws IllegalArgumentException if the target class is not supported
154: * (i.e. not a standard Number subclass as included in the JDK)
155: * @see java.text.NumberFormat#parse
156: * @see #convertNumberToTargetClass
157: * @see #parseNumber(String,Class)
158: */
159: public static Number parseNumber(String text, Class targetClass,
160: NumberFormat numberFormat) {
161: if (numberFormat != null) {
162: try {
163: Number number = numberFormat.parse(text.trim());
164: return convertNumberToTargetClass(number, targetClass);
165: } catch (ParseException ex) {
166: throw new IllegalArgumentException(ex.getMessage());
167: }
168: } else {
169: return parseNumber(text, targetClass);
170: }
171: }
172:
173: /**
174: * Decode a {@link java.math.BigInteger} from a {@link String} value.
175: * Supports decimal, hex and octal notation.
176: *
177: * @see BigInteger#BigInteger(String,int)
178: */
179: private static BigInteger decodeBigInteger(String value) {
180: int radix = 10;
181: int index = 0;
182: boolean negative = false;
183:
184: // Handle minus sign, if present.
185: if (value.startsWith("-")) {
186: negative = true;
187: index++;
188: }
189:
190: // Handle radix specifier, if present.
191: if (value.startsWith("0x", index)
192: || value.startsWith("0X", index)) {
193: index += 2;
194: radix = 16;
195: } else if (value.startsWith("#", index)) {
196: index++;
197: radix = 16;
198: } else if (value.startsWith("0", index)
199: && value.length() > 1 + index) {
200: index++;
201: radix = 8;
202: }
203:
204: BigInteger result = new BigInteger(value.substring(index),
205: radix);
206: return (negative ? result.negate() : result);
207: }
208:
209: }
|