View Javadoc
1   /**
2    * Copyright 2010-2013 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  
28  import org.apache.commons.lang3.StringUtils;
29  import org.kuali.common.util.base.Exceptions;
30  
31  import com.google.common.base.Stopwatch;
32  
33  /**
34   * Format time, bytes, counts, dates, and transfer rates into human friendly form
35   * 
36   * @author Jeff Caddel
37   * @since May 27, 2010 6:46:17 PM
38   */
39  public class FormatUtils {
40  
41  	public static final double SECOND = 1000;
42  	public static final double MINUTE = 60 * SECOND;
43  	public static final double HOUR = 60 * MINUTE;
44  	public static final double DAY = 24 * HOUR;
45  	public static final double YEAR = 365 * DAY;
46  
47  	private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ";
48  	private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat(DATE_FORMAT);
49  
50  	private static final List<String> TIME_TOKENS = Arrays.asList("ms", "s", "m", "h", "d", "y");
51  	private static final List<Long> TIME_MULTIPLIERS = getTimeMultipliers();
52  
53  	private static final List<String> SIZE_TOKENS = Arrays.asList("b", "k", "m", "g", "t", "p", "e");
54  	private static final int BASE = 1024;
55  
56  	private static NumberFormat largeSizeFormatter = NumberFormat.getInstance();
57  	private static NumberFormat sizeFormatter = NumberFormat.getInstance();
58  	private static NumberFormat timeFormatter = NumberFormat.getInstance();
59  	private static NumberFormat rateFormatter = NumberFormat.getInstance();
60  	private static NumberFormat countFormatter = NumberFormat.getInstance();
61  	private static NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance();
62  	private static NumberFormat integerFormatter = NumberFormat.getInstance();
63  
64  	static {
65  		integerFormatter.setGroupingUsed(false);
66  		integerFormatter.setMaximumFractionDigits(0);
67  		integerFormatter.setMinimumFractionDigits(0);
68  		sizeFormatter.setGroupingUsed(false);
69  		sizeFormatter.setMaximumFractionDigits(1);
70  		sizeFormatter.setMinimumFractionDigits(1);
71  		largeSizeFormatter.setGroupingUsed(false);
72  		largeSizeFormatter.setMaximumFractionDigits(3);
73  		largeSizeFormatter.setMinimumFractionDigits(3);
74  		timeFormatter.setGroupingUsed(false);
75  		timeFormatter.setMaximumFractionDigits(3);
76  		timeFormatter.setMinimumFractionDigits(3);
77  		rateFormatter.setGroupingUsed(false);
78  		rateFormatter.setMaximumFractionDigits(3);
79  		rateFormatter.setMinimumFractionDigits(3);
80  		countFormatter.setGroupingUsed(true);
81  		countFormatter.setMaximumFractionDigits(0);
82  		countFormatter.setMinimumFractionDigits(0);
83  	}
84  
85  	public static String getCurrency(double number) {
86  		return currencyFormatter.format(number);
87  	}
88  
89  	/**
90  	 * 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.
91  	 * 
92  	 * <pre>
93  	 *   1  == 1 byte
94  	 *   1b == 1 byte
95  	 *   1k == 1 kilobyte == 1024   bytes ==                     1,024 bytes
96  	 *   1m == 1 megabyte == 1024^2 bytes ==                 1,048,576 bytes
97  	 *   1g == 1 gigabyte == 1024^3 bytes ==             1,073,741,824 bytes
98  	 *   1t == 1 terabyte == 1024^4 bytes ==         1,099,511,627,776 bytes
99  	 *   1p == 1 petabyte == 1024^5 bytes ==     1,125,899,906,842,624 bytes
100 	 *   1e == 1 exabyte  == 1024^6 bytes == 1,152,921,504,606,846,976 bytes
101 	 * </pre>
102 	 */
103 	public static long getBytes(String size) {
104 		return getBytes(size, SIZE_TOKENS, BASE);
105 	}
106 
107 	public static long getBytes(String size, List<String> tokens, int base) {
108 		Assert.notBlank(size);
109 		for (int i = 0; i < tokens.size(); i++) {
110 			String token = tokens.get(i);
111 			long multiplier = (long) Math.pow(base, i);
112 			if (StringUtils.endsWithIgnoreCase(size, token)) {
113 				return getByteValue(size, token, multiplier);
114 			}
115 		}
116 		// Assume bytes
117 		return getByteValue(size, "", 1);
118 	}
119 
120 	protected static long getByteValue(String time, String suffix, long multiplier) {
121 		int len = StringUtils.length(time);
122 		String substring = StringUtils.substring(time, 0, len - suffix.length());
123 		Double value = new Double(substring);
124 		value = value * multiplier;
125 		return value.longValue();
126 	}
127 
128 	/**
129 	 * 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.
130 	 * 
131 	 * <pre>
132 	 *   1   == 1 millisecond
133 	 *   1ms == 1 millisecond
134 	 *   1s  == 1 second ==           1000 milliseconds
135 	 *   1m  == 1 minute ==         60,000 milliseconds
136 	 *   1h  == 1 hour   ==      3,600,000 milliseconds 
137 	 *   1d  == 1 day    ==     86,400,000 milliseconds
138 	 *   1y  == 1 year   == 31,536,000,000 milliseconds
139 	 * </pre>
140 	 */
141 	public static long getMillis(String time) {
142 		return getMillis(time, TIME_TOKENS, TIME_MULTIPLIERS);
143 	}
144 
145 	/**
146 	 * 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.
147 	 * 
148 	 * <pre>
149 	 *   1   == 1 millisecond
150 	 *   1ms == 1 millisecond
151 	 *   1s  == 1 second ==           1000 milliseconds
152 	 *   1m  == 1 minute ==         60,000 milliseconds
153 	 *   1h  == 1 hour   ==      3,600,000 milliseconds 
154 	 *   1d  == 1 day    ==     86,400,000 milliseconds
155 	 *   1y  == 1 year   == 31,536,000,000 milliseconds
156 	 * </pre>
157 	 */
158 	public static int getMillisAsInt(String time) {
159 		Long millis = getMillis(time);
160 		if (millis <= Integer.MAX_VALUE) {
161 			return millis.intValue();
162 		} else {
163 			throw Exceptions.illegalArg("[%s] converts to [%s]. maximum allowable integer value is [%s]", time, millis, Integer.MAX_VALUE);
164 		}
165 	}
166 
167 	public static long getMillis(String time, List<String> tokens, List<Long> multipliers) {
168 		Assert.notBlank(time);
169 		Assert.isTrue(tokens.size() == multipliers.size());
170 		for (int i = 0; i < tokens.size(); i++) {
171 			String token = tokens.get(i);
172 			long multiplier = multipliers.get(i);
173 			if (StringUtils.endsWithIgnoreCase(time, token)) {
174 				return getTimeValue(time, token, multiplier);
175 			}
176 		}
177 		// Assume milliseconds
178 		return getTimeValue(time, "", 1);
179 	}
180 
181 	protected static long getTimeValue(String time, String suffix, long multiplier) {
182 		int len = StringUtils.length(time);
183 		String substring = StringUtils.substring(time, 0, len - suffix.length());
184 		Double value = new Double(substring);
185 		value = value * multiplier;
186 		return value.longValue();
187 	}
188 
189 	/**
190 	 * Parse a date from the string. The string must be in the same format returned by the getDate() methods
191 	 */
192 	public static Date parseDate(String date) {
193 		try {
194 			synchronized (DATE_FORMATTER) {
195 				return DATE_FORMATTER.parse(date);
196 			}
197 		} catch (ParseException e) {
198 			throw new IllegalArgumentException("Can't parse [" + date + "]", e);
199 		}
200 	}
201 
202 	/**
203 	 * Return a formatted date
204 	 */
205 	public static String getDate(long millis) {
206 		return getDate(new Date(millis));
207 	}
208 
209 	/**
210 	 * Return a formatted date
211 	 */
212 	public static String getDate(Date date) {
213 		synchronized (DATE_FORMATTER) {
214 			return DATE_FORMATTER.format(date);
215 		}
216 	}
217 
218 	/**
219 	 * 
220 	 */
221 	public static String getThroughputInSeconds(long millis, long count, String label) {
222 		double seconds = millis / SECOND;
223 		double countPerSecond = count / seconds;
224 		synchronized (countFormatter) {
225 			return countFormatter.format(countPerSecond) + " " + label;
226 		}
227 	}
228 
229 	/**
230 	 * 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
231 	 */
232 	public static String getRate(long millis, long bytes) {
233 		double seconds = millis / SECOND;
234 		double bytesPerSecond = bytes / seconds;
235 		Size bandwidthLevel = getSizeEnum(bytesPerSecond);
236 		double transferRate = bytesPerSecond / bandwidthLevel.getValue();
237 		synchronized (rateFormatter) {
238 			return rateFormatter.format(transferRate) + " " + bandwidthLevel.getRateLabel();
239 		}
240 	}
241 
242 	/**
243 	 * Return a formatted <code>count</code>
244 	 */
245 	public static String getCount(long count) {
246 		synchronized (countFormatter) {
247 			return countFormatter.format(count);
248 		}
249 	}
250 
251 	/**
252 	 * Given milliseconds, return milliseconds, seconds, minutes, hours, days, or years as appropriate. Note that years is approximate since the logic always assumes there are
253 	 * exactly 365 days per year.
254 	 */
255 	public static String getTime(long millis) {
256 		return getTime(millis, timeFormatter);
257 	}
258 
259 	/**
260 	 * 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
261 	 * exactly 365 days per year.
262 	 */
263 	public static String getTime(Stopwatch stopwatch) {
264 		return getTime(stopwatch.elapsed(MILLISECONDS));
265 	}
266 
267 	/**
268 	 * Given milliseconds, return milliseconds, seconds, minutes, hours, days, or years as appropriate. Note that years is approximate since the logic always assumes there are
269 	 * exactly 365 days per year.
270 	 */
271 	public static String getTime(long millis, NumberFormat formatter) {
272 		long abs = Math.abs(millis);
273 		synchronized (formatter) {
274 			if (abs < SECOND) {
275 				return millis + "ms";
276 			} else if (abs < MINUTE) {
277 				return formatter.format(millis / SECOND) + "s";
278 			} else if (abs < HOUR) {
279 				return formatter.format(millis / MINUTE) + "m";
280 			} else if (abs < DAY) {
281 				return formatter.format(millis / HOUR) + "h";
282 			} else if (abs < YEAR) {
283 				return formatter.format(millis / DAY) + "d";
284 			} else {
285 				return formatter.format(millis / YEAR) + "y";
286 			}
287 		}
288 	}
289 
290 	/**
291 	 * Given a number of bytes return bytes, kilobytes, megabytes, gigabytes, terabytes, petabytes, or exabytes as appropriate.
292 	 */
293 	public static String getSize(long bytes) {
294 		return getSize(bytes, null);
295 	}
296 
297 	/**
298 	 * Given a number of bytes return bytes, kilobytes, megabytes, gigabytes, terabytes, petabytes, or exabytes as appropriate.
299 	 */
300 	public static String getIntegerSize(long bytes) {
301 		return getIntegerSize(bytes, null);
302 	}
303 
304 	/**
305 	 * Given a number of bytes return a string formatted into the unit of measure indicated
306 	 */
307 	public static String getIntegerSize(long bytes, final Size unitOfMeasure) {
308 		Size uom = (unitOfMeasure == null) ? getSizeEnum(bytes) : unitOfMeasure;
309 		StringBuilder sb = new StringBuilder();
310 		synchronized (integerFormatter) {
311 			sb.append(integerFormatter.format(bytes / (double) uom.getValue()));
312 		}
313 		sb.append(uom.getSizeLabel());
314 		return sb.toString();
315 	}
316 
317 	/**
318 	 * Given a number of bytes return a string formatted into the unit of measure indicated
319 	 */
320 	public static String getSize(long bytes, Size unitOfMeasure) {
321 		Size uom = (unitOfMeasure == null) ? getSizeEnum(bytes) : unitOfMeasure;
322 		StringBuilder sb = new StringBuilder();
323 		sb.append(getFormattedSize(bytes, uom));
324 		sb.append(uom.getSizeLabel());
325 		return sb.toString();
326 	}
327 
328 	public static String getFormattedSize(long bytes, Size size) {
329 		switch (size) {
330 		case BYTE:
331 			return bytes + "";
332 		case KB:
333 		case MB:
334 		case GB:
335 			synchronized (sizeFormatter) {
336 				return sizeFormatter.format(bytes / (double) size.getValue());
337 			}
338 		default:
339 			synchronized (largeSizeFormatter) {
340 				return largeSizeFormatter.format(bytes / (double) size.getValue());
341 			}
342 		}
343 	}
344 
345 	public static Size getSizeEnum(double bytes) {
346 		bytes = Math.abs(bytes);
347 		if (bytes < Size.KB.getValue()) {
348 			return Size.BYTE;
349 		} else if (bytes < Size.MB.getValue()) {
350 			return Size.KB;
351 		} else if (bytes < Size.GB.getValue()) {
352 			return Size.MB;
353 		} else if (bytes < Size.TB.getValue()) {
354 			return Size.GB;
355 		} else if (bytes < Size.PB.getValue()) {
356 			return Size.TB;
357 		} else if (bytes < Size.EB.getValue()) {
358 			return Size.PB;
359 		} else {
360 			return Size.EB;
361 		}
362 	}
363 
364 	protected static final List<Long> getTimeMultipliers() {
365 		List<Long> m = new ArrayList<Long>();
366 		m.add(1L);
367 		m.add(new Double(SECOND).longValue());
368 		m.add(new Double(MINUTE).longValue());
369 		m.add(new Double(HOUR).longValue());
370 		m.add(new Double(DAY).longValue());
371 		m.add(new Double(YEAR).longValue());
372 		return m;
373 	}
374 }