1 /* |
|
2 * CDDL HEADER START |
|
3 * |
|
4 * The contents of this file are subject to the terms of the |
|
5 * Common Development and Distribution License (the "License"). |
|
6 * You may not use this file except in compliance with the License. |
|
7 * |
|
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
|
9 * or http://www.opensolaris.org/os/licensing. |
|
10 * See the License for the specific language governing permissions |
|
11 * and limitations under the License. |
|
12 * |
|
13 * When distributing Covered Code, include this CDDL HEADER in each |
|
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
|
15 * If applicable, add the following below this CDDL HEADER, with the |
|
16 * fields enclosed by brackets "[]" replaced with your own identifying |
|
17 * information: Portions Copyright [yyyy] [name of copyright owner] |
|
18 * |
|
19 * CDDL HEADER END |
|
20 */ |
|
21 |
|
22 /* |
|
23 * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. |
|
24 */ |
|
25 |
|
26 package com.oracle.solaris.vp.util.misc; |
|
27 |
|
28 import java.text.DecimalFormat; |
|
29 import com.oracle.solaris.vp.util.misc.finder.Finder; |
|
30 |
|
31 public class DataSizeUtil { |
|
32 // |
|
33 // Inner classes |
|
34 // |
|
35 |
|
36 public static class DataSize { |
|
37 public double value; |
|
38 public int unitsIndex; |
|
39 public DataSize(double value, int unitsIndex) { |
|
40 this.value = value; |
|
41 this.unitsIndex = unitsIndex; |
|
42 } |
|
43 } |
|
44 |
|
45 // |
|
46 // Static methods |
|
47 // |
|
48 |
|
49 public static String[] getDefaultUnits() { |
|
50 return new String[] { |
|
51 Finder.getString("constants.bytes.short"), |
|
52 Finder.getString("constants.kilobytes.short"), |
|
53 Finder.getString("constants.megabytes.short"), |
|
54 Finder.getString("constants.gigabytes.short"), |
|
55 Finder.getString("constants.terabytes.short"), |
|
56 Finder.getString("constants.petabytes.short"), |
|
57 Finder.getString("constants.exabytes.short"), |
|
58 }; |
|
59 } |
|
60 |
|
61 /** |
|
62 * Gets a DataSize object that represents the most displayable |
|
63 * form of the given data size. |
|
64 * |
|
65 * @param bytes |
|
66 * the size of the data in bytes |
|
67 * |
|
68 * @param precision |
|
69 * the number of digits to appear after the decimal |
|
70 * separator |
|
71 * |
|
72 * @param units |
|
73 * a String array of the units to be shown after the |
|
74 * size, in the following order: |
|
75 * <pre> |
|
76 * index unit |
|
77 * --------------------------- |
|
78 * 0 byte unit string |
|
79 * 1 kilobyte unit string |
|
80 * 2 megabyte unit string |
|
81 * 3 gigabyte unit string |
|
82 * 4 terabyte unit string |
|
83 * 5 petabyte unit string |
|
84 * 6 exabyte unit string |
|
85 * </pre> |
|
86 * If null, the default units will be used. |
|
87 * |
|
88 * @param unit |
|
89 * a string from the units array to force the use of |
|
90 * a particular unit, or {@code null} to use the |
|
91 * most appropriate unit for the given size |
|
92 * |
|
93 * @return a DataSize object that represents the most |
|
94 * displayable form of the given data size. |
|
95 */ |
|
96 public static DataSize getDataSize( |
|
97 long bytes, int precision, String[] units, String unit) { |
|
98 |
|
99 if (units == null) { |
|
100 units = getDefaultUnits(); |
|
101 } |
|
102 |
|
103 // Handle negative values |
|
104 double factor = 1; |
|
105 if (bytes < 0) { |
|
106 bytes = -bytes; |
|
107 factor = -1; |
|
108 } |
|
109 |
|
110 double adjusted; |
|
111 long bytesPerUnit = 1; |
|
112 int i; |
|
113 |
|
114 if (unit == null) { |
|
115 // Determine which units to use |
|
116 double slop = precision > 0 ? .5 : 0; |
|
117 for (i = 0; i < precision; i++) { |
|
118 slop /= 10; |
|
119 } |
|
120 |
|
121 adjusted = bytes; |
|
122 |
|
123 for (i = 0; i < units.length; i++) { |
|
124 adjusted = (double)bytes / (double)bytesPerUnit; |
|
125 |
|
126 if (i == units.length - 1 || adjusted + slop < 1024) { |
|
127 break; |
|
128 } |
|
129 |
|
130 bytesPerUnit <<= 10; |
|
131 } |
|
132 } else { |
|
133 for (i = 0; i < units.length - 1 && ! units[i].equals(unit); i++) { |
|
134 bytesPerUnit <<= 10; |
|
135 } |
|
136 |
|
137 adjusted = (double)bytes / (double)bytesPerUnit; |
|
138 } |
|
139 |
|
140 return new DataSize(factor * adjusted, i); |
|
141 } |
|
142 |
|
143 /** |
|
144 * Gets the given data size in a human-readable format. |
|
145 * |
|
146 * @param bytes |
|
147 * the size of the data in bytes |
|
148 * |
|
149 * @param precision |
|
150 * the number of digits to appear after the decimal |
|
151 * separator |
|
152 * |
|
153 * @param forcePrecision |
|
154 * whether to keep the decimal portion of the |
|
155 * output if all digits after the decimal separator |
|
156 * are 0. If units are in bytes, the decimal portion |
|
157 * is never included in the output. |
|
158 * |
|
159 * @param units |
|
160 * a String array of the units to be shown after the |
|
161 * size, in the following order: |
|
162 * <pre> |
|
163 * index unit |
|
164 * --------------------------- |
|
165 * 0 byte unit string |
|
166 * 1 kilobyte unit string |
|
167 * 2 megabyte unit string |
|
168 * 3 gigabyte unit string |
|
169 * 4 terabyte unit string |
|
170 * 5 petabyte unit string |
|
171 * 6 exabyte unit string |
|
172 * </pre> |
|
173 * If null, the default units will be used. |
|
174 * |
|
175 * @param unit |
|
176 * a string from the units array to force the use of |
|
177 * a particular unit, or {@code null} to use the |
|
178 * most appropriate unit for the given size |
|
179 * |
|
180 * @param addSpace |
|
181 * whether to add a space between the numerical value |
|
182 * and the units text |
|
183 * |
|
184 * @param useCommas |
|
185 * whether to separate every third digit with a comma |
|
186 * |
|
187 * @return a data size string in human-readable format |
|
188 */ |
|
189 public static String getHumanReadableDataSize( |
|
190 long bytes, int precision, boolean forcePrecision, |
|
191 String[] units, String unit, boolean addSpace, boolean useCommas) { |
|
192 |
|
193 if (units == null) { |
|
194 units = getDefaultUnits(); |
|
195 } |
|
196 |
|
197 DataSize dataSize = getDataSize(bytes, precision, units, unit); |
|
198 |
|
199 // Build format string |
|
200 StringBuilder pattern = new StringBuilder(); |
|
201 pattern.append(useCommas ? "#,##0" : "###0"); |
|
202 char formatChar = |
|
203 (forcePrecision && dataSize.unitsIndex != 0) ? '0' : '#'; |
|
204 for (int j = 0; j < precision; j++) { |
|
205 if (j == 0) { |
|
206 pattern.append('.'); |
|
207 } |
|
208 pattern.append(formatChar); |
|
209 } |
|
210 DecimalFormat format = new DecimalFormat(pattern.toString()); |
|
211 String space = addSpace ? " " : ""; |
|
212 |
|
213 return format.format(dataSize.value) + space + |
|
214 units[dataSize.unitsIndex]; |
|
215 } |
|
216 |
|
217 /** |
|
218 * Tries to parse the given string as a human-readable expression |
|
219 * of size. Only enough of the unit string must appear in the |
|
220 * given string to uniquely distinguish a single unit string. |
|
221 * Parsing is case-insensitive. |
|
222 * |
|
223 * @param text |
|
224 * the text to parse |
|
225 * |
|
226 * @param units |
|
227 * a String array of valid units that may appear |
|
228 * after the size. |
|
229 * <pre> |
|
230 * index unit |
|
231 * --------------------------- |
|
232 * 0 byte unit string |
|
233 * 1 kilobyte unit string |
|
234 * 2 megabyte unit string |
|
235 * 3 gigabyte unit string |
|
236 * 4 terabyte unit string |
|
237 * 5 petabyte unit string |
|
238 * 6 exabyte unit string |
|
239 * </pre> |
|
240 * If null, the default units will be used. |
|
241 * |
|
242 * @param defaultUnitIndex |
|
243 * the default units (specified as an index of the |
|
244 * {@code units} array) to use if no unit text |
|
245 * is specified as part of the given string |
|
246 * |
|
247 * @return the size in bytes that the given string represents |
|
248 * |
|
249 * @exception NumberFormatException |
|
250 * if the given text does not represent a valid size |
|
251 */ |
|
252 public static long parseHumanReadableDataSize( |
|
253 String text, String[] units, int defaultUnitIndex) { |
|
254 |
|
255 if (units == null) { |
|
256 units = getDefaultUnits(); |
|
257 } |
|
258 |
|
259 String[] groups = TextUtil.match( |
|
260 text, "^\\s*(\\d+(\\.\\d+)?)\\s*(\\S*)\\s*$"); |
|
261 if (groups == null) { |
|
262 throw new NumberFormatException(Finder.getString( |
|
263 "error.invalidsize", text)); |
|
264 } |
|
265 |
|
266 double value = Double.parseDouble(groups[1]); |
|
267 long multiplier = 1; |
|
268 |
|
269 // Was a units string specified? |
|
270 if (groups[3].isEmpty()) { |
|
271 |
|
272 // No, use default |
|
273 multiplier <<= 10 * defaultUnitIndex; |
|
274 } else { |
|
275 |
|
276 // Yes, search for matching units string |
|
277 String matchingUnit = null; |
|
278 String unitText = groups[3].toLowerCase(); |
|
279 |
|
280 for (int i = 0; i < units.length; i++) { |
|
281 if (units[i].toLowerCase().startsWith(unitText)) { |
|
282 |
|
283 // If we've already found a match, then the given |
|
284 // units text is ambiguous |
|
285 if (matchingUnit != null) { |
|
286 throw new NumberFormatException(Finder.getString( |
|
287 "error.ambiguousunits", |
|
288 unitText, matchingUnit, units[i])); |
|
289 } |
|
290 |
|
291 matchingUnit = units[i]; |
|
292 } |
|
293 |
|
294 if (matchingUnit == null) { |
|
295 multiplier <<= 10; |
|
296 } |
|
297 } |
|
298 |
|
299 if (matchingUnit == null) { |
|
300 throw new NumberFormatException(Finder.getString( |
|
301 "error.invalidunits", unitText)); |
|
302 } |
|
303 } |
|
304 |
|
305 return (long)(value * multiplier); |
|
306 } |
|
307 } |
|