View Javadoc

1   /*
2    * Copyright (c) 2003-2008 by Cosylab d. d.
3    *
4    * This file is part of Java-Common.
5    *
6    * Java-Common is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU General Public License as published by
8    * the Free Software Foundation, either version 3 of the License, or
9    * (at your option) any later version.
10   *
11   * Java-Common is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU General Public License for more details.
15   *
16   * You should have received a copy of the GNU General Public License
17   * along with Java-Common.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  
20  package com.cosylab.util;
21  
22  import com.cosylab.introspection.ClassIntrospector;
23  
24  import java.io.PrintWriter;
25  import java.io.StringWriter;
26  import java.lang.reflect.Array;
27  import java.lang.reflect.InvocationTargetException;
28  import java.lang.reflect.Method;
29  
30  
31  /**
32   * Usefull methods for working with strings.
33   *
34   * @author <a href="mailto:miha.kadunc@cosylab.com">Miha Kadunc</a>
35   * @author Ales Pucelj
36   * @version $id$
37   */
38  public final class StringUtilities
39  {
40  	private static final PrintfFormat byteFormat= new PrintfFormat("%.1f");
41  
42  	// FP number segments  sign*decPart.fracPartEexpSign*expPart
43  	private static final int ERROR_STATE = 99;
44  
45  	// Parser state transitions: transitions[Current State][Symbol]
46  	private static final int[][] transitions = {
47  			{ 99, 1, 1, 2, 3 },
48  			{ 99, 99, 1, 2, 3 },
49  			{ 99, 99, 2, 99, 3 },
50  			{ 99, 4, 4, 99, 99 },
51  			{ 99, 99, 4, 99, 99 }
52  		};
53  	private static char decimalSeparator = '.';
54  
55  	/**
56  	 * Constructor for StringHelper.
57  	 */
58  	private StringUtilities()
59  	{
60  		//
61  	}
62  
63  	/**
64  	 * DOCUMENT ME!
65  	 *
66  	 * @param sValue DOCUMENT ME!
67  	 * @param targetClass DOCUMENT ME!
68  	 *
69  	 * @return DOCUMENT ME!
70  	 *
71  	 * @throws CommonException DOCUMENT ME!
72  	 */
73  	public static Object fromString(String sValue, Class targetClass)
74  		throws CommonException
75  	{
76  		try {
77  			if ((sValue == null) || (sValue.equals(""))) {
78  				return targetClass.newInstance();
79  			}
80  
81  			if (targetClass.isArray()) {
82  				return arrayFromString(sValue, targetClass);
83  			}
84  
85  			if (targetClass.equals(String.class)) {
86  				return sValue;
87  			} else if (Number.class.isAssignableFrom(targetClass)
88  			    || Boolean.class.isAssignableFrom(targetClass)) {
89  				return targetClass.getMethod("valueOf",
90  				    new Class[]{ String.class }).invoke(null,
91  				    new Object[]{ sValue });
92  			} else if (targetClass.isPrimitive()) {
93  				// we are not able to return Object of a primitive type so use its class implementation
94  				if (Boolean.TYPE.equals(targetClass)) {
95  					return Boolean.valueOf(sValue);
96  				}
97  
98  				if (Short.TYPE.equals(targetClass)) {
99  					return Short.valueOf(sValue);
100 				}
101 
102 				if (Byte.TYPE.equals(targetClass)) {
103 					return Byte.valueOf(sValue);
104 				}
105 
106 				if (Character.TYPE.equals(targetClass)) {
107 					return new Character(sValue.charAt(0));
108 				}
109 
110 				if (Integer.TYPE.equals(targetClass)) {
111 					return Integer.valueOf(sValue);
112 				}
113 
114 				if (Long.TYPE.equals(targetClass)) {
115 					return Long.valueOf(sValue);
116 				}
117 
118 				if (Float.TYPE.equals(targetClass)) {
119 					return Float.valueOf(sValue);
120 				}
121 
122 				if (Double.TYPE.equals(targetClass)) {
123 					return Double.valueOf(sValue);
124 				}
125 
126 				return null;
127 			} else if (Class.class == targetClass) {
128 				// in case of primitive types return its class
129 				return classFromString(sValue);
130 			}
131 			// check if the class has the valueOf(String) method and call it with given parameter if so
132 			// the example is in case of DataProducer which is instantiated by the parameters from the xml file
133 			else {
134 				Method met = ClassIntrospector.getMethod(targetClass,
135 					    "valueOf", new Class[]{ String.class });
136 
137 				if (met != null) {
138 					return met.invoke(null, new String[]{ sValue });
139 				}
140 
141 				return targetClass.newInstance();
142 			}
143 		} catch (NoSuchMethodException e) {
144 			throw new CommonException(ArrayHelper.class,
145 			    "Objects could not be instantiated from strings", e);
146 		} catch (IllegalAccessException e) {
147 			throw new CommonException(ArrayHelper.class,
148 			    "Objects could not be instantiated from strings", e);
149 		} catch (InvocationTargetException e) {
150 			throw new CommonException(ArrayHelper.class,
151 			    "Objects could not be instantiated from strings", e);
152 		} catch (InstantiationException e) {
153 			throw new CommonException(ArrayHelper.class,
154 			    "Objects could not be instantiated from strings", e);
155 		}
156 	}
157 
158 	/**
159 	 * DOCUMENT ME!
160 	 *
161 	 * @param sValue DOCUMENT ME!
162 	 *
163 	 * @return DOCUMENT ME!
164 	 *
165 	 * @throws CommonException DOCUMENT ME!
166 	 */
167 	public static Class classFromString(String sValue)
168 		throws CommonException
169 	{
170 		try {
171 			if (sValue.equals(Boolean.TYPE.getName())) {
172 				return Boolean.TYPE;
173 			}
174 
175 			if (sValue.equals(Short.TYPE.getName())) {
176 				return Short.TYPE;
177 			}
178 
179 			if (sValue.equals(Byte.TYPE.getName())) {
180 				return Byte.TYPE;
181 			}
182 
183 			if (sValue.equals(Character.TYPE.getName())) {
184 				return Character.TYPE;
185 			}
186 
187 			if (sValue.equals(Integer.TYPE.getName())) {
188 				return Integer.TYPE;
189 			}
190 
191 			if (sValue.equals(Long.TYPE.getName())) {
192 				return Long.TYPE;
193 			}
194 
195 			if (sValue.equals(Float.TYPE.getName())) {
196 				return Float.TYPE;
197 			}
198 
199 			if (sValue.equals(Double.TYPE.getName())) {
200 				return Double.TYPE;
201 			}
202 
203 			if (sValue.equals(Void.TYPE.getName())) {
204 				return Void.TYPE;
205 			}
206 
207 			return Class.forName(sValue);
208 		} catch (ClassNotFoundException e) {
209 			throw new CommonException(ArrayHelper.class,
210 			    "Class could not be instantiated from a String", e);
211 		}
212 	}
213 
214 	/**
215 	 * DOCUMENT ME!
216 	 *
217 	 * @param arrayString DOCUMENT ME!
218 	 * @param arrayClass DOCUMENT ME!
219 	 *
220 	 * @return DOCUMENT ME!
221 	 *
222 	 * @throws CommonException DOCUMENT ME!
223 	 */
224 	public static Object arrayFromString(String arrayString, Class arrayClass)
225 		throws CommonException
226 	{
227 		return arrayFromString(arrayString, arrayClass, ",", true);
228 	}
229 
230 	/**
231 	 * DOCUMENT ME!
232 	 *
233 	 * @param arrayString DOCUMENT ME!
234 	 * @param arrayClass DOCUMENT ME!
235 	 * @param separator DOCUMENT ME!
236 	 * @param parentheses DOCUMENT ME!
237 	 *
238 	 * @return DOCUMENT ME!
239 	 *
240 	 * @throws CommonException DOCUMENT ME!
241 	 */
242 	public static Object arrayFromString(String arrayString, Class arrayClass,
243 	    String separator, boolean parentheses) throws CommonException
244 	{
245 		String[] pStrings = arrayString.split(separator);
246 
247 		if (parentheses) {
248 			pStrings[0] = pStrings[0].substring(1);
249 			pStrings[pStrings.length - 1] = pStrings[pStrings.length - 1]
250 				.substring(0, pStrings[pStrings.length - 1].length() - 1);
251 		}
252 
253 		Object retArray = Array.newInstance(arrayClass.getComponentType(),
254 			    pStrings.length);
255 
256 		for (int i = 0; i < pStrings.length; i++) {
257 			Array.set(retArray, i,
258 			    StringUtilities.fromString(pStrings[i],
259 			        arrayClass.getComponentType()));
260 		}
261 
262 		return retArray;
263 	}
264 
265 	/**
266 	 * Simple, robust floating point number parsing, it returns
267 	 * <code>NaN</code> if parsing fails, no exception thrown.
268 	 * 
269 	 * <p>
270 	 * The parser is derived from the following regular expression:
271 	 * </p>
272 	 * 
273 	 * <p>
274 	 * <code>[+-]?[0-9](\.[0-9])?((e[+-]?)[0-9])?</code>
275 	 * </p>
276 	 *
277 	 * @param s input string
278 	 *
279 	 * @return double value
280 	 */
281 	public static final double parseDouble(final String s)
282 	{
283 		double decPart = 0.0;
284 		double sign = 1.0;
285 
286 		double fracPart = 0.0;
287 		double fracDiv = 10.0;
288 
289 		double expPart = 0.0;
290 		double expSign = 1.0;
291 
292 		char[] chars = s.trim().toCharArray();
293 		int n = chars.length;
294 
295 		int currState = 0;
296 		int nextState = ERROR_STATE;
297 		int symbol = 0;
298 		char ch = ' ';
299 
300 		int c = 0;
301 
302 		while ((c < n) && (currState != ERROR_STATE)) {
303 			ch = chars[c++];
304 			symbol = getNextSymbol(ch);
305 			nextState = transitions[currState][symbol];
306 
307 			//doAction(nextState, nextSymbol, nextChar);
308 			switch (nextState) {
309 			case 1:
310 
311 				if (symbol == 1) {
312 					if (ch == '-') {
313 						sign = -1.0;
314 					}
315 				} else {
316 					decPart *= 10;
317 					decPart += (ch - '0');
318 				}
319 
320 				break;
321 
322 			case 2:
323 
324 				if (symbol != 3) {
325 					fracPart += (ch - '0') / fracDiv;
326 					fracDiv *= 10;
327 				}
328 
329 				break;
330 
331 			case 4:
332 
333 				if (symbol == 1) {
334 					if (ch == '-') {
335 						expSign = -1.0;
336 					}
337 				} else {
338 					expPart *= 10;
339 					expPart += (ch - '0');
340 				}
341 			}
342 
343 			currState = nextState;
344 		}
345 
346 		if (currState != ERROR_STATE) {
347 			expPart = Math.pow(10, expSign * expPart);
348 
349 			return sign * (decPart + fracPart) * expPart;
350 		}
351 
352 		return Double.NaN;
353 	}
354 
355 	private static final int getNextSymbol(final char ch)
356 	{
357 		if (Character.isDigit(ch)) {
358 			return 2;
359 		}
360 
361 		if ((ch == '+') || (ch == '-')) {
362 			return 1;
363 		}
364 
365 		if (ch == decimalSeparator) {
366 			return 3;
367 		}
368 
369 		if ((ch == 'e') || (ch == 'E')) {
370 			return 4;
371 		}
372 
373 		return 0;
374 	}
375 
376 	/**
377 	 * Checks if string unambiguously represents double value. If string can
378 	 * not be transformed to unique double, then <code>false</code> is
379 	 * returned. Method <code>parseDouble</code> tries to convert to double
380 	 * any
381 	 *
382 	 * @param value the String to be tested
383 	 *
384 	 * @return <code>true</code> only if string unambiguously represents double
385 	 *         value
386 	 */
387 	public static boolean isTrueNumber(String value)
388 	{
389 		if (value == null) {
390 			return false;
391 		}
392 
393 		String s = value.trim();
394 
395 		if (s.length() == 0) {
396 			return false;
397 		}
398 
399 		if (s.length() == 1 && !Character.isDigit(s.charAt(0))) {
400 			return false;
401 		}
402 
403 		double d = parseDouble(s);
404 
405 		if (Double.isNaN(d) || Double.isInfinite(d)) {
406 			return false;
407 		}
408 
409 		return true;
410 	}
411 	
412 	/**
413 	 * Prints bytes with human readable format.
414 	 * @param bytes file or memory size in bytes
415 	 * @param pr print writer
416 	 */
417 	public static void printBytes(long bytes, PrintWriter pr) {
418 		
419 		if (bytes>=900000000) {
420 			pr.print(byteFormat.sprintf(bytes/1000000000.0));
421 			pr.print('G');
422 			pr.print('B');
423 			return;
424 		}
425 		if (bytes>=900000) {
426 			pr.print(byteFormat.sprintf(bytes/1000000.0));
427 			pr.print('M');
428 			pr.print('B');
429 			return;
430 		}
431 		if (bytes>900) {
432 			pr.print(byteFormat.sprintf(bytes/1000.0));
433 			pr.print('M');
434 			pr.print('B');
435 			return;
436 		}
437 		
438 		pr.print(bytes);
439 		pr.print('B');
440 	}
441 	
442 	/**
443 	 * Prints bytes with human readable format.
444 	 * @param bytes file or memory size in bytes
445 	 *
446      */
447 	public static String printBytes(long bytes) {
448 		StringWriter sw= new StringWriter(8);
449 		printBytes(bytes,new PrintWriter(sw));
450 		return sw.toString();
451 	}
452 }
453 
454 /* __oOo__ */