View Javadoc
1   /**
2    * Copyright 2010-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.common.util;
17  
18  import static java.util.concurrent.TimeUnit.MILLISECONDS;
19  
20  import java.text.NumberFormat;
21  import java.text.ParseException;
22  import java.text.SimpleDateFormat;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.Date;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.TimeZone;
29  
30  import org.apache.commons.lang3.StringUtils;
31  import org.kuali.common.util.base.Exceptions;
32  
33  import com.google.common.base.Stopwatch;
34  import com.google.common.collect.Iterables;
35  
36  /**
37   * Format time, bytes, counts, dates, and transfer rates into human friendly form
38   * 
39   * @author Jeff Caddel
40   * @since May 27, 2010 6:46:17 PM
41   */
42  public class FormatUtils {
43  
44  	public static final double SECOND = 1000;
45  	public static final double MINUTE = 60 * SECOND;
46  	public static final double HOUR = 60 * MINUTE;
47  	public static final double DAY = 24 * HOUR;
48  	public static final double YEAR = 365 * DAY;
49  
50  	/**
51  	 * This is the format produced by the {@code toString()} method of {@code java.util.Date} instance
52  	 */
53  	public static final String JAVA_UTIL_DATE_TO_STRING_FORMAT = "EEE MMM d HH:mm:ss zzz y";
54  	private static final SimpleDateFormat JAVA_UTIL_DATE_TO_STRING_FORMATTER = new SimpleDateFormat(JAVA_UTIL_DATE_TO_STRING_FORMAT);
55  
56  	private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ";
57  	private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat(DATE_FORMAT);
58  
59  	private static final List<String> TIME_TOKENS = Arrays.asList("ms", "s", "m", "h", "d", "y");
60  	private static final List<Long> TIME_MULTIPLIERS = getTimeMultipliers();
61  
62  	private static final List<String> SIZE_TOKENS = Arrays.asList("b", "k", "m", "g", "t", "p", "e");
63  	private static final int BASE = 1024;
64  
65  	private static NumberFormat largeSizeFormatter = NumberFormat.getInstance();
66  	private static NumberFormat sizeFormatter = NumberFormat.getInstance();
67  	private static NumberFormat timeFormatter = NumberFormat.getInstance();
68  	private static NumberFormat rateFormatter = NumberFormat.getInstance();
69  	private static NumberFormat countFormatter = NumberFormat.getInstance();
70  	private static NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance();
71  	private static NumberFormat integerFormatter = NumberFormat.getInstance();
72  
73  	static {
74  		integerFormatter.setGroupingUsed(false);
75  		integerFormatter.setMaximumFractionDigits(0);
76  		integerFormatter.setMinimumFractionDigits(0);
77  		sizeFormatter.setGroupingUsed(false);
78  		sizeFormatter.setMaximumFractionDigits(1);
79  		sizeFormatter.setMinimumFractionDigits(1);
80  		largeSizeFormatter.setGroupingUsed(false);
81  		largeSizeFormatter.setMaximumFractionDigits(3);
82  		largeSizeFormatter.setMinimumFractionDigits(3);
83  		timeFormatter.setGroupingUsed(false);
84  		timeFormatter.setMaximumFractionDigits(3);
85  		timeFormatter.setMinimumFractionDigits(3);
86  		rateFormatter.setGroupingUsed(false);
87  		rateFormatter.setMaximumFractionDigits(3);
88  		rateFormatter.setMinimumFractionDigits(3);
89  		countFormatter.setGroupingUsed(true);
90  		countFormatter.setMaximumFractionDigits(0);
91  		countFormatter.setMinimumFractionDigits(0);
92  	}
93  
94  	public static String getCurrency(double number) {
95  		return currencyFormatter.format(number);
96  	}
97  
98  	/**
99  	 * Parse bytes from a size string that ends with a unit of measure. If no unit of measure is provided, bytes is assumed. Unit of measure is case insensitive.
100 	 * 
101 	 * <pre>
102 	 *   1  == 1 byte
103 	 *   1b == 1 byte
104 	 *   1k == 1 kilobyte == 1024   bytes ==                     1,024 bytes
105 	 *   1m == 1 megabyte == 1024^2 bytes ==                 1,048,576 bytes
106 	 *   1g == 1 gigabyte == 1024^3 bytes ==             1,073,741,824 bytes
107 	 *   1t == 1 terabyte == 1024^4 bytes ==         1,099,511,627,776 bytes
108 	 *   1p == 1 petabyte == 1024^5 bytes ==     1,125,899,906,842,624 bytes
109 	 *   1e == 1 exabyte  == 1024^6 bytes == 1,152,921,504,606,846,976 bytes
110 	 * </pre>
111 	 */
112 	public static long getBytes(String size) {
113 		return getBytes(size, SIZE_TOKENS, BASE);
114 	}
115 
116 	public static long getBytes(String size, List<String> tokens, int base) {
117 		Assert.notBlank(size);
118 		for (int i = 0; i < tokens.size(); i++) {
119 			String token = tokens.get(i);
120 			long multiplier = (long) Math.pow(base, i);
121 			if (StringUtils.endsWithIgnoreCase(size, token)) {
122 				return getByteValue(size, token, multiplier);
123 			}
124 		}
125 		// Assume bytes
126 		return getByteValue(size, "", 1);
127 	}
128 
129 	protected static long getByteValue(String time, String suffix, long multiplier) {
130 		int len = StringUtils.length(time);
131 		String substring = StringUtils.substring(time, 0, len - suffix.length());
132 		Double value = new Double(substring);
133 		value = value * multiplier;
134 		return value.longValue();
135 	}
136 
137 	/**
138 	 * Parse milliseconds from a time string that ends with a unit of measure. If no unit of measure is provided, milliseconds is assumed. Unit of measure is case insensitive.
139 	 * 
140 	 * <pre>
141 	 *   1   == 1 millisecond
142 	 *   1ms == 1 millisecond
143 	 *   1s  == 1 second ==           1000 milliseconds
144 	 *   1m  == 1 minute ==         60,000 milliseconds
145 	 *   1h  == 1 hour   ==      3,600,000 milliseconds 
146 	 *   1d  == 1 day    ==     86,400,000 milliseconds
147 	 *   1y  == 1 year   == 31,536,000,000 milliseconds
148 	 * </pre>
149 	 */
150 	public static long getMillis(String time) {
151 		return getMillis(time, TIME_TOKENS, TIME_MULTIPLIERS);
152 	}
153 
154 	/**
155 	 * Parse milliseconds from a time string that ends with a unit of measure. If no unit of measure is provided, milliseconds is assumed. Unit of measure is case insensitive.
156 	 * 
157 	 * <pre>
158 	 *   1   == 1 millisecond
159 	 *   1ms == 1 millisecond
160 	 *   1s  == 1 second ==           1000 milliseconds
161 	 *   1m  == 1 minute ==         60,000 milliseconds
162 	 *   1h  == 1 hour   ==      3,600,000 milliseconds 
163 	 *   1d  == 1 day    ==     86,400,000 milliseconds
164 	 *   1y  == 1 year   == 31,536,000,000 milliseconds
165 	 * </pre>
166 	 */
167 	public static int getMillisAsInt(String time) {
168 		Long millis = getMillis(time);
169 		if (millis <= Integer.MAX_VALUE) {
170 			return millis.intValue();
171 		} else {
172 			throw Exceptions.illegalArgument("[%s] converts to [%s]. maximum allowable integer value is [%s]", time, millis, Integer.MAX_VALUE);
173 		}
174 	}
175 
176 	public static long getMillis(String time, List<String> tokens, List<Long> multipliers) {
177 		Assert.notBlank(time);
178 		Assert.isTrue(tokens.size() == multipliers.size());
179 		for (int i = 0; i < tokens.size(); i++) {
180 			String token = tokens.get(i);
181 			long multiplier = multipliers.get(i);
182 			if (StringUtils.endsWithIgnoreCase(time, token)) {
183 				return getTimeValue(time, token, multiplier);
184 			}
185 		}
186 		// Assume milliseconds
187 		return getTimeValue(time, "", 1);
188 	}
189 
190 	protected static long getTimeValue(String time, String suffix, long multiplier) {
191 		int len = StringUtils.length(time);
192 		String substring = StringUtils.substring(time, 0, len - suffix.length());
193 		Double value = new Double(substring);
194 		value = value * multiplier;
195 		return value.longValue();
196 	}
197 
198 	/**
199 	 * Parse a date from the string. The string must be in the same format returned by the getDate() methods
200 	 */
201 	public static Date parseDate(String date) {
202 		try {
203 			synchronized (DATE_FORMATTER) {
204 				return DATE_FORMATTER.parse(date);
205 			}
206 		} catch (ParseException e) {
207 			throw new IllegalArgumentException("Can't parse [" + date + "]", e);
208 		}
209 	}
210 
211 	/**
212 	 * Return a formatted date
213 	 */
214 	public static String dateAsString(long millis) {
215 		return dateAsString(new Date(millis));
216 	}
217 
218 	/**
219 	 * Return a formatted date
220 	 */
221 	public static String dateAsString(Date date) {
222 		synchronized (JAVA_UTIL_DATE_TO_STRING_FORMATTER) {
223 			return JAVA_UTIL_DATE_TO_STRING_FORMATTER.format(date);
224 		}
225 	}
226 
227 	/**
228 	 * Return a formatted date
229 	 */
230 	public static String dateAsString(long millis, String timezone) {
231 		return dateAsString(new Date(millis), timezone);
232 	}
233 
234 	/**
235 	 * Return a formatted date
236 	 */
237 	public static String dateAsString(Date date, String timezone) {
238 		SimpleDateFormat sdf = new SimpleDateFormat(JAVA_UTIL_DATE_TO_STRING_FORMAT);
239 		sdf.setTimeZone(TimeZone.getTimeZone(timezone));
240 		return sdf.format(date);
241 	}
242 
243 	/**
244 	 * Return a formatted date
245 	 */
246 	public static String getDate(long millis) {
247 		return getDate(new Date(millis));
248 	}
249 
250 	/**
251 	 * Return a formatted date
252 	 */
253 	public static String getDate(Date date) {
254 		synchronized (DATE_FORMATTER) {
255 			return DATE_FORMATTER.format(date);
256 		}
257 	}
258 
259 	/**
260 	 * 
261 	 */
262 	public static String getThroughputInSeconds(long millis, long count, String label) {
263 		double seconds = millis / SECOND;
264 		double countPerSecond = count / seconds;
265 		synchronized (countFormatter) {
266 			return countFormatter.format(countPerSecond) + " " + label;
267 		}
268 	}
269 
270 	/**
271 	 * Given a number of bytes and the number of milliseconds it took to transfer that number of bytes, return bytes/s, KB/s, MB/s, GB/s, TB/s, PB/s, or EB/s as appropriate
272 	 */
273 	public static String getRate(long millis, long bytes) {
274 		return getRate(millis, bytes, rateFormatter);
275 	}
276 
277 	/**
278 	 * Given a number of bytes and the number of milliseconds it took to transfer that number of bytes, return bytes/s, KB/s, MB/s, GB/s, TB/s, PB/s, or EB/s as appropriate
279 	 */
280 	public static String getRate(long millis, long bytes, NumberFormat rateFormatter) {
281 		double seconds = millis / SECOND;
282 		double bytesPerSecond = bytes / seconds;
283 		Size bandwidthLevel = getSizeEnum(bytesPerSecond);
284 		double transferRate = bytesPerSecond / bandwidthLevel.getValue();
285 		synchronized (rateFormatter) {
286 			return rateFormatter.format(transferRate) + " " + bandwidthLevel.getRateLabel();
287 		}
288 	}
289 
290 	/**
291 	 * Return a formatted <code>count</code> representing the number of keys in the map
292 	 */
293 	public static String getCount(Map<?, ?> map) {
294 		return getCount(map.keySet());
295 	}
296 
297 	/**
298 	 * Return a formatted <code>count</code> representing the number of elements in the iterable
299 	 */
300 	public static String getCount(Iterable<?> iterable) {
301 		return getCount(Iterables.size(iterable));
302 	}
303 
304 	/**
305 	 * Return a formatted <code>count</code>
306 	 */
307 	public static String getCount(long count) {
308 		synchronized (countFormatter) {
309 			return countFormatter.format(count);
310 		}
311 	}
312 
313 	/**
314 	 * Given milliseconds, return milliseconds, seconds, minutes, hours, days, or years as appropriate. Note that years is approximate since the logic always assumes there are
315 	 * exactly 365 days per year.
316 	 */
317 	public static String getTime(long millis) {
318 		return getTime(millis, timeFormatter);
319 	}
320 
321 	/**
322 	 * Given a stopwatch, return milliseconds, seconds, minutes, hours, days, or years as appropriate. Note that years is approximate since the logic always assumes there are
323 	 * exactly 365 days per year.
324 	 */
325 	public static String getTime(Stopwatch stopwatch) {
326 		return getTime(stopwatch.elapsed(MILLISECONDS));
327 	}
328 
329 	/**
330 	 * Given milliseconds, return milliseconds, seconds, minutes, hours, days, or years as appropriate. Note that years is approximate since the logic always assumes there are
331 	 * exactly 365 days per year.
332 	 */
333 	public static String getTime(long millis, NumberFormat formatter) {
334 		long abs = Math.abs(millis);
335 		synchronized (formatter) {
336 			if (abs < SECOND) {
337 				return millis + "ms";
338 			} else if (abs < MINUTE) {
339 				return formatter.format(millis / SECOND) + "s";
340 			} else if (abs < HOUR) {
341 				return formatter.format(millis / MINUTE) + "m";
342 			} else if (abs < DAY) {
343 				return formatter.format(millis / HOUR) + "h";
344 			} else if (abs < YEAR) {
345 				return formatter.format(millis / DAY) + "d";
346 			} else {
347 				return formatter.format(millis / YEAR) + "y";
348 			}
349 		}
350 	}
351 
352 	/**
353 	 * Given a number of bytes return bytes, kilobytes, megabytes, gigabytes, terabytes, petabytes, or exabytes as appropriate.
354 	 */
355 	public static String getSize(long bytes) {
356 		return getSize(bytes, (Size) null);
357 	}
358 
359 	/**
360 	 * Given a number of bytes return bytes, kilobytes, megabytes, gigabytes, terabytes, petabytes, or exabytes as appropriate.
361 	 */
362 	public static String getIntegerSize(long bytes) {
363 		return getIntegerSize(bytes, null);
364 	}
365 
366 	/**
367 	 * Given a number of bytes return a string formatted into the unit of measure indicated
368 	 */
369 	public static String getIntegerSize(long bytes, final Size unitOfMeasure) {
370 		Size uom = (unitOfMeasure == null) ? getSizeEnum(bytes) : unitOfMeasure;
371 		StringBuilder sb = new StringBuilder();
372 		synchronized (integerFormatter) {
373 			sb.append(integerFormatter.format(bytes / (double) uom.getValue()));
374 		}
375 		sb.append(uom.getSizeLabel());
376 		return sb.toString();
377 	}
378 
379 	/**
380 	 * Given a number of bytes return a string formatted into the unit of measure indicated
381 	 */
382 	public static String getSize(long bytes, NumberFormat formatter) {
383 		return getSize(bytes, null, formatter);
384 	}
385 
386 	/**
387 	 * Given a number of bytes return a string formatted into the unit of measure indicated
388 	 */
389 	public static String getSize(long bytes, final Size unitOfMeasure, NumberFormat formatter) {
390 		Size uom = (unitOfMeasure == null) ? getSizeEnum(bytes) : unitOfMeasure;
391 		StringBuilder sb = new StringBuilder();
392 		synchronized (formatter) {
393 			sb.append(formatter.format(bytes / (double) uom.getValue()));
394 		}
395 		sb.append(uom.getSizeLabel());
396 		return sb.toString();
397 	}
398 
399 	/**
400 	 * Given a number of bytes return a string formatted into the unit of measure indicated
401 	 */
402 	public static String getSize(long bytes, Size unitOfMeasure) {
403 		Size uom = (unitOfMeasure == null) ? getSizeEnum(bytes) : unitOfMeasure;
404 		StringBuilder sb = new StringBuilder();
405 		sb.append(getFormattedSize(bytes, uom));
406 		sb.append(uom.getSizeLabel());
407 		return sb.toString();
408 	}
409 
410 	public static String getFormattedSize(long bytes, Size size) {
411 		switch (size) {
412 		case BYTE:
413 			return bytes + "";
414 		case KB:
415 		case MB:
416 		case GB:
417 			synchronized (sizeFormatter) {
418 				return sizeFormatter.format(bytes / (double) size.getValue());
419 			}
420 		default:
421 			synchronized (largeSizeFormatter) {
422 				return largeSizeFormatter.format(bytes / (double) size.getValue());
423 			}
424 		}
425 	}
426 
427 	public static Size getSizeEnum(double bytes) {
428 		bytes = Math.abs(bytes);
429 		if (bytes < Size.KB.getValue()) {
430 			return Size.BYTE;
431 		} else if (bytes < Size.MB.getValue()) {
432 			return Size.KB;
433 		} else if (bytes < Size.GB.getValue()) {
434 			return Size.MB;
435 		} else if (bytes < Size.TB.getValue()) {
436 			return Size.GB;
437 		} else if (bytes < Size.PB.getValue()) {
438 			return Size.TB;
439 		} else if (bytes < Size.EB.getValue()) {
440 			return Size.PB;
441 		} else {
442 			return Size.EB;
443 		}
444 	}
445 
446 	protected static final List<Long> getTimeMultipliers() {
447 		List<Long> m = new ArrayList<Long>();
448 		m.add(1L);
449 		m.add(new Double(SECOND).longValue());
450 		m.add(new Double(MINUTE).longValue());
451 		m.add(new Double(HOUR).longValue());
452 		m.add(new Double(DAY).longValue());
453 		m.add(new Double(YEAR).longValue());
454 		return m;
455 	}
456 }