Mercurial > projects > dwt2
comparison org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToNumberParser.d @ 78:0a55d2d5a946
Added file for databinding
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Tue, 14 Apr 2009 11:35:29 +0200 |
parents | |
children | 6be48cf9f95c |
comparison
equal
deleted
inserted
replaced
76:f05e6e8b2f2d | 78:0a55d2d5a946 |
---|---|
1 /******************************************************************************* | |
2 * Copyright (c) 2007, 2008 IBM Corporation and others. | |
3 * All rights reserved. This program and the accompanying materials | |
4 * are made available under the terms of the Eclipse Public License v1.0 | |
5 * which accompanies this distribution, and is available at | |
6 * http://www.eclipse.org/legal/epl-v10.html | |
7 * | |
8 * Contributors: | |
9 * IBM Corporation - initial API and implementation | |
10 *******************************************************************************/ | |
11 | |
12 module org.eclipse.core.internal.databinding.conversion.StringToNumberParser; | |
13 | |
14 import java.lang.all; | |
15 | |
16 import java.math.BigDecimal; | |
17 import java.math.BigInteger; | |
18 import java.text.ParsePosition; | |
19 | |
20 import org.eclipse.core.internal.databinding.BindingMessages; | |
21 | |
22 import com.ibm.icu.text.NumberFormat; | |
23 | |
24 /** | |
25 * Utility class for the parsing of strings to numbers. | |
26 * | |
27 * @since 1.0 | |
28 */ | |
29 public class StringToNumberParser { | |
30 private static final BigDecimal FLOAT_MAX_BIG_DECIMAL = new BigDecimal( | |
31 Float.MAX_VALUE); | |
32 private static final BigDecimal FLOAT_MIN_BIG_DECIMAL = new BigDecimal( | |
33 -Float.MAX_VALUE); | |
34 | |
35 private static final BigDecimal DOUBLE_MAX_BIG_DECIMAL = new BigDecimal( | |
36 Double.MAX_VALUE); | |
37 private static final BigDecimal DOUBLE_MIN_BIG_DECIMAL = new BigDecimal( | |
38 -Double.MAX_VALUE); | |
39 | |
40 /** | |
41 * @param value | |
42 * @param numberFormat | |
43 * @param primitive | |
44 * @return result | |
45 */ | |
46 public static ParseResult parse(Object value, NumberFormat numberFormat, | |
47 bool primitive) { | |
48 if (!( null !is cast(String)value )) { | |
49 throw new IllegalArgumentException( | |
50 "Value to convert is not a String"); //$NON-NLS-1$ | |
51 } | |
52 | |
53 String source = cast(String) value; | |
54 ParseResult result = new ParseResult(); | |
55 if (!primitive && source.trim().length() is 0) { | |
56 return result; | |
57 } | |
58 | |
59 synchronized (numberFormat) { | |
60 ParsePosition position = new ParsePosition(0); | |
61 Number parseResult = null; | |
62 parseResult = numberFormat.parse(source, position); | |
63 | |
64 if (position.getIndex() !is source.length() | |
65 || position.getErrorIndex() > -1) { | |
66 | |
67 result.position = position; | |
68 } else { | |
69 result.number = parseResult; | |
70 } | |
71 } | |
72 | |
73 return result; | |
74 } | |
75 | |
76 /** | |
77 * The result of a parse operation. | |
78 * | |
79 * @since 1.0 | |
80 */ | |
81 public static class ParseResult { | |
82 /* package */Number number; | |
83 /* package */ParsePosition position; | |
84 | |
85 /** | |
86 * The number as a result of the conversion. <code>null</code> if the | |
87 * value could not be converted or if the type is not a primitive and | |
88 * the value was an empty string. | |
89 * | |
90 * @return number | |
91 */ | |
92 public Number getNumber() { | |
93 return number; | |
94 } | |
95 | |
96 /** | |
97 * ParsePosition if an error occurred while parsing. <code>null</code> | |
98 * if no error occurred. | |
99 * | |
100 * @return parse position | |
101 */ | |
102 public ParsePosition getPosition() { | |
103 return position; | |
104 } | |
105 } | |
106 | |
107 /** | |
108 * Formats an appropriate message for a parsing error. | |
109 * | |
110 * @param value | |
111 * @param position | |
112 * @return message | |
113 */ | |
114 public static String createParseErrorMessage(String value, | |
115 ParsePosition position) { | |
116 int errorIndex = (position.getErrorIndex() > -1) ? position | |
117 .getErrorIndex() : position.getIndex(); | |
118 | |
119 if (errorIndex < value.length()) { | |
120 return BindingMessages.formatString(BindingMessages.VALIDATE_NUMBER_PARSE_ERROR, | |
121 [cast(Object) stringcast(value), new Integer(errorIndex + 1), | |
122 new Character(value.charAt(errorIndex)) ]); | |
123 } | |
124 return BindingMessages.formatString(BindingMessages.VALIDATE_NUMBER_PARSE_ERROR_NO_CHARACTER, | |
125 [cast(Object) stringcast(value), new Integer(errorIndex + 1) ]); | |
126 } | |
127 | |
128 /** | |
129 * Formats an appropriate message for an out of range error. | |
130 * | |
131 * @param minValue | |
132 * @param maxValue | |
133 * @param numberFormat when accessed method synchronizes on instance | |
134 * @return message | |
135 */ | |
136 public static String createOutOfRangeMessage(Number minValue, | |
137 Number maxValue, NumberFormat numberFormat) { | |
138 String min = null; | |
139 String max = null; | |
140 | |
141 synchronized (numberFormat) { | |
142 min = numberFormat.format(minValue); | |
143 max = numberFormat.format(maxValue); | |
144 } | |
145 | |
146 return BindingMessages.formatString( | |
147 "Validate_NumberOutOfRangeError", [ cast(Object)min, max ]); //$NON-NLS-1$ | |
148 } | |
149 | |
150 /** | |
151 * Returns <code>true</code> if the provided <code>number</code> is in | |
152 * the range of a integer. | |
153 * | |
154 * @param number | |
155 * @return <code>true</code> if a valid integer | |
156 * @throws IllegalArgumentException | |
157 * if the number type is unsupported | |
158 */ | |
159 public static bool inIntegerRange(Number number) { | |
160 return checkInteger(number, 31); | |
161 } | |
162 | |
163 /** | |
164 * Validates the range of the provided <code>number</code>. | |
165 * | |
166 * @param number | |
167 * @param bitLength number of bits allowed to be in range | |
168 * @return <code>true</code> if in range | |
169 */ | |
170 private static bool checkInteger(Number number, int bitLength) { | |
171 BigInteger bigInteger = null; | |
172 | |
173 if ( null !is cast(Integer )number || null !is cast(Long)number ) { | |
174 bigInteger = BigInteger.valueOf(number.longValue()); | |
175 } else if ( null !is cast(Float )number || null !is cast(Double)number ) { | |
176 double doubleValue = number.doubleValue(); | |
177 /* | |
178 * doubleValue is doubleValue is used to check for NaN because NaN !is | |
179 * NaN. The only way to check for NaN is to compare that the value | |
180 * is equal to itself. | |
181 */ | |
182 if (doubleValue is doubleValue | |
183 && doubleValue !is Double.NEGATIVE_INFINITY | |
184 && doubleValue !is Double.POSITIVE_INFINITY) { | |
185 bigInteger = (new BigDecimal(doubleValue)).toBigInteger(); | |
186 } else { | |
187 return false; | |
188 } | |
189 } else if ( null !is cast(BigInteger)number ) { | |
190 bigInteger = cast(BigInteger) number; | |
191 } else if ( null !is cast(BigDecimal)number ) { | |
192 bigInteger = (cast(BigDecimal) number).toBigInteger(); | |
193 } else { | |
194 /* | |
195 * The else is necessary as the ICU4J plugin has it's own BigDecimal | |
196 * implementation which isn't part of the replacement plugin. So | |
197 * that this will work we fall back on the double value of the | |
198 * number. | |
199 */ | |
200 bigInteger = (new BigDecimal(number.doubleValue())).toBigInteger(); | |
201 } | |
202 | |
203 if (bigInteger !is null) { | |
204 return bigInteger.bitLength() <= bitLength; | |
205 } | |
206 | |
207 throw new IllegalArgumentException( | |
208 "Number of type [" + number.getClass().getName() + "] is not supported."); //$NON-NLS-1$ //$NON-NLS-2$ | |
209 } | |
210 | |
211 /** | |
212 * Returns <code>true</code> if the provided <code>number</code> is in | |
213 * the range of a long. | |
214 * | |
215 * @param number | |
216 * @return <code>true</code> if in range | |
217 * @throws IllegalArgumentException | |
218 * if the number type is unsupported | |
219 */ | |
220 public static bool inLongRange(Number number) { | |
221 return checkInteger(number, 63); | |
222 } | |
223 | |
224 /** | |
225 * Returns <code>true</code> if the provided <code>number</code> is in | |
226 * the range of a float. | |
227 * | |
228 * @param number | |
229 * @return <code>true</code> if in range | |
230 * @throws IllegalArgumentException | |
231 * if the number type is unsupported | |
232 */ | |
233 public static bool inFloatRange(Number number) { | |
234 return checkDecimal(number, FLOAT_MIN_BIG_DECIMAL, FLOAT_MAX_BIG_DECIMAL); | |
235 } | |
236 | |
237 private static bool checkDecimal(Number number, BigDecimal min, BigDecimal max) { | |
238 BigDecimal bigDecimal = null; | |
239 if ( null !is cast(Integer )number || null !is cast(Long)number ) { | |
240 bigDecimal = new BigDecimal(number.doubleValue()); | |
241 } else if ( null !is cast(Float )number || null !is cast(Double)number ) { | |
242 double doubleValue = number.doubleValue(); | |
243 | |
244 /* | |
245 * doubleValue is doubleValue is used to check for NaN because NaN !is | |
246 * NaN. The only way to check for NaN is to compare that the value | |
247 * is equal to itself. | |
248 */ | |
249 if (doubleValue is doubleValue | |
250 && doubleValue !is Double.NEGATIVE_INFINITY | |
251 && doubleValue !is Double.POSITIVE_INFINITY) { | |
252 bigDecimal = new BigDecimal(doubleValue); | |
253 } else { | |
254 return false; | |
255 } | |
256 } else if ( null !is cast(BigInteger)number ) { | |
257 bigDecimal = new BigDecimal(cast(BigInteger) number); | |
258 } else if ( null !is cast(BigDecimal)number ) { | |
259 bigDecimal = cast(BigDecimal) number; | |
260 } else { | |
261 /* | |
262 * The else is necessary as the ICU4J plugin has it's own BigDecimal | |
263 * implementation which isn't part of the replacement plugin. So | |
264 * that this will work we fall back on the double value of the | |
265 * number. | |
266 */ | |
267 bigDecimal = new BigDecimal(number.doubleValue()); | |
268 } | |
269 | |
270 if (bigDecimal !is null) { | |
271 return max.compareTo(bigDecimal) >= 0 | |
272 && min.compareTo(bigDecimal) <= 0; | |
273 } | |
274 | |
275 throw new IllegalArgumentException( | |
276 "Number of type [" + number.getClass().getName() + "] is not supported."); //$NON-NLS-1$ //$NON-NLS-2$ | |
277 } | |
278 | |
279 /** | |
280 * Returns <code>true</code> if the provided <code>number</code> is in | |
281 * the range of a double. | |
282 * | |
283 * @param number | |
284 * @return <code>true</code> if in range | |
285 * @throws IllegalArgumentException | |
286 * if the number type is unsupported | |
287 */ | |
288 public static bool inDoubleRange(Number number) { | |
289 return checkDecimal(number, DOUBLE_MIN_BIG_DECIMAL, DOUBLE_MAX_BIG_DECIMAL); | |
290 } | |
291 | |
292 /** | |
293 * Returns <code>true</code> if the provided <code>number</code> is in | |
294 * the range of a short. | |
295 * | |
296 * @param number | |
297 * @return <code>true</code> if in range | |
298 */ | |
299 public static bool inShortRange(Number number) { | |
300 return checkInteger(number, 15); | |
301 } | |
302 | |
303 /** | |
304 * Returns <code>true</code> if the provided <code>number</code> is in | |
305 * the range of a byte. | |
306 * | |
307 * @param number | |
308 * @return <code>true</code> if in range | |
309 */ | |
310 public static bool inByteRange(Number number) { | |
311 return checkInteger(number, 7); | |
312 } | |
313 } |