001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.util.lang; 018 019import java.text.NumberFormat; 020import java.text.ParseException; 021import java.util.Locale; 022import java.util.regex.Matcher; 023import java.util.regex.Pattern; 024 025import org.apache.wicket.util.string.StringValue; 026import org.apache.wicket.util.string.StringValueConversionException; 027import org.apache.wicket.util.value.LongValue; 028 029 030/** 031 * Represents an immutable byte count. These static factory methods allow easy construction of value 032 * objects using either long values like bytes(2034) or megabytes(3): 033 * <p> 034 * <ul> 035 * <li>Bytes.bytes(long) 036 * <li>Bytes.kilobytes(long) 037 * <li>Bytes.megabytes(long) 038 * <li>Bytes.gigabytes(long) 039 * <li>Bytes.terabytes(long) 040 * </ul> 041 * <p> 042 * or double precision floating point values like megabytes(3.2): 043 * <p> 044 * <ul> 045 * <li>Bytes.bytes(double) 046 * <li>Bytes.kilobytes(double) 047 * <li>Bytes.megabytes(double) 048 * <li>Bytes.gigabytes(double) 049 * <li>Bytes.terabytes(double) 050 * </ul> 051 * <p> 052 * In the case of bytes(double), the value will be rounded off to the nearest integer byte count 053 * using Math.round(). 054 * <p> 055 * The precise number of bytes in a Bytes object can be retrieved by calling bytes(). Approximate 056 * values for different units can be retrieved as double precision values using these methods: 057 * <p> 058 * <ul> 059 * <li>kilobytes() 060 * <li>megabytes() 061 * <li>gigabytes() 062 * <li>terabytes() 063 * </ul> 064 * <p> 065 * Also, value objects can be constructed from strings, optionally using a Locale with 066 * valueOf(String) and valueOf(String,Locale). The string may contain a decimal or floating point 067 * number followed by optional whitespace followed by a unit (nothing for bytes, K for kilobyte, M 068 * for megabytes, G for gigabytes or T for terabytes) optionally followed by a B (for bytes). Any of 069 * these letters can be any case. So, examples of permissible string values are: 070 * <p> 071 * <ul> 072 * <li>37 (37 bytes) 073 * <li>2.3K (2.3 kilobytes) 074 * <li>2.5 kb (2.5 kilobytes) 075 * <li>4k (4 kilobytes) 076 * <li>35.2GB (35.2 gigabytes) 077 * <li>1024M (1024 megabytes) 078 * </ul> 079 * <p> 080 * Note that if the Locale was not US, the values might substitute "," for "." as that is the custom 081 * in Euroland. 082 * <p> 083 * The toString() and toString(Locale) methods are smart enough to convert a given value object to 084 * the most appropriate units for the given value. 085 * 086 * @author Jonathan Locke 087 */ 088public final class Bytes extends LongValue 089{ 090 private static final long serialVersionUID = 1L; 091 092 /** Pattern for string parsing. */ 093 private static final Pattern valuePattern = Pattern.compile( 094 "([0-9]+([\\.,][0-9]+)?)\\s*(|K|M|G|T)B?", Pattern.CASE_INSENSITIVE); 095 096 /** Maximum bytes value */ 097 public static final Bytes MAX = bytes(Long.MAX_VALUE); 098 099 /** 100 * Private constructor forces use of static factory methods. 101 * 102 * @param bytes 103 * Number of bytes 104 */ 105 private Bytes(final long bytes) 106 { 107 super(bytes); 108 109 if (bytes < 0) 110 { 111 throw new IllegalArgumentException("'bytes' cannot be negative: " + bytes); 112 } 113 } 114 115 /** 116 * Instantiate immutable Bytes value object.. 117 * 118 * @param bytes 119 * Value to convert 120 * @return Input as Bytes 121 */ 122 public static Bytes bytes(final long bytes) 123 { 124 return new Bytes(bytes); 125 } 126 127 /** 128 * Instantiate immutable Bytes value object.. 129 * 130 * @param kilobytes 131 * Value to convert 132 * @return Input as Bytes 133 */ 134 public static Bytes kilobytes(final long kilobytes) 135 { 136 return bytes(kilobytes * 1024); 137 } 138 139 /** 140 * Instantiate immutable Bytes value object.. 141 * 142 * @param megabytes 143 * Value to convert 144 * @return Input as Bytes 145 */ 146 public static Bytes megabytes(final long megabytes) 147 { 148 return kilobytes(megabytes * 1024); 149 } 150 151 /** 152 * Instantiate immutable Bytes value object.. 153 * 154 * @param gigabytes 155 * Value to convert 156 * @return Input as Bytes 157 */ 158 public static Bytes gigabytes(final long gigabytes) 159 { 160 return megabytes(gigabytes * 1024); 161 } 162 163 /** 164 * Instantiate immutable Bytes value object.. 165 * 166 * @param terabytes 167 * Value to convert 168 * @return Input as Bytes 169 */ 170 public static Bytes terabytes(final long terabytes) 171 { 172 return gigabytes(terabytes * 1024); 173 } 174 175 /** 176 * Instantiate immutable Bytes value object.. 177 * 178 * @param bytes 179 * Value to convert 180 * @return Input as Bytes 181 */ 182 public static Bytes bytes(final double bytes) 183 { 184 return bytes(Math.round(bytes)); 185 } 186 187 /** 188 * Instantiate immutable Bytes value object.. 189 * 190 * @param kilobytes 191 * Value to convert 192 * @return Input as Bytes 193 */ 194 public static Bytes kilobytes(final double kilobytes) 195 { 196 return bytes(kilobytes * 1024.0); 197 } 198 199 /** 200 * Instantiate immutable Bytes value object.. 201 * 202 * @param megabytes 203 * Value to convert 204 * @return Input as Bytes 205 */ 206 public static Bytes megabytes(final double megabytes) 207 { 208 return kilobytes(megabytes * 1024.0); 209 } 210 211 /** 212 * Instantiate immutable Bytes value object.. 213 * 214 * @param gigabytes 215 * Value to convert 216 * @return Input as Bytes 217 */ 218 public static Bytes gigabytes(final double gigabytes) 219 { 220 return megabytes(gigabytes * 1024.0); 221 } 222 223 /** 224 * Instantiate immutable Bytes value object.. 225 * 226 * @param terabytes 227 * Value to convert 228 * @return Input as Bytes 229 */ 230 public static Bytes terabytes(final double terabytes) 231 { 232 return gigabytes(terabytes * 1024.0); 233 } 234 235 /** 236 * Gets the byte count represented by this value object. 237 * 238 * @return Byte count 239 */ 240 public final long bytes() 241 { 242 return value; 243 } 244 245 /** 246 * Gets the byte count in kilobytes. 247 * 248 * @return The value in kilobytes 249 */ 250 public final double kilobytes() 251 { 252 return value / 1024.0; 253 } 254 255 /** 256 * Gets the byte count in megabytes. 257 * 258 * @return The value in megabytes 259 */ 260 public final double megabytes() 261 { 262 return kilobytes() / 1024.0; 263 } 264 265 /** 266 * Gets the byte count in gigabytes. 267 * 268 * @return The value in gigabytes 269 */ 270 public final double gigabytes() 271 { 272 return megabytes() / 1024.0; 273 } 274 275 /** 276 * Gets the byte count in terabytes. 277 * 278 * @return The value in terabytes 279 */ 280 public final double terabytes() 281 { 282 return gigabytes() / 1024.0; 283 } 284 285 /** 286 * Converts a string to a number of bytes. Strings consist of a floating point value followed by 287 * K, M, G or T for kilobytes, megabytes, gigabytes or terabytes, respectively. The 288 * abbreviations KB, MB, GB and TB are also accepted. Matching is case insensitive. 289 * 290 * @param string 291 * The string to convert 292 * @param locale 293 * The Locale to be used for transformation 294 * @return The Bytes value for the string 295 * @throws StringValueConversionException 296 */ 297 public static Bytes valueOf(final String string, final Locale locale) 298 throws StringValueConversionException 299 { 300 final Matcher matcher = valuePattern.matcher(string); 301 302 // Valid input? 303 if (matcher.matches()) 304 { 305 try 306 { 307 // Get double precision value 308 final double value = NumberFormat.getNumberInstance(locale) 309 .parse(matcher.group(1)) 310 .doubleValue(); 311 312 // Get units specified 313 final String units = matcher.group(3); 314 315 if (units.equalsIgnoreCase("")) 316 { 317 return bytes(value); 318 } 319 else if (units.equalsIgnoreCase("K")) 320 { 321 return kilobytes(value); 322 } 323 else if (units.equalsIgnoreCase("M")) 324 { 325 return megabytes(value); 326 } 327 else if (units.equalsIgnoreCase("G")) 328 { 329 return gigabytes(value); 330 } 331 else if (units.equalsIgnoreCase("T")) 332 { 333 return terabytes(value); 334 } 335 else 336 { 337 throw new StringValueConversionException("Units not recognized: " + string); 338 } 339 } 340 catch (ParseException e) 341 { 342 throw new StringValueConversionException("Unable to parse numeric part: " + string, 343 e); 344 } 345 } 346 else 347 { 348 throw new StringValueConversionException("Unable to parse bytes: " + string); 349 } 350 } 351 352 /** 353 * Converts a string to a number of bytes. Strings consist of a floating point value followed by 354 * K, M, G or T for kilobytes, megabytes, gigabytes or terabytes, respectively. The 355 * abbreviations KB, MB, GB and TB are also accepted. Matching is case insensitive. 356 * 357 * @param string 358 * The string to convert 359 * @return The Bytes value for the string 360 * @throws StringValueConversionException 361 */ 362 public static Bytes valueOf(final String string) throws StringValueConversionException 363 { 364 return valueOf(string, Locale.getDefault(Locale.Category.FORMAT)); 365 } 366 367 /** 368 * Converts this byte count to a string using the default locale. 369 * 370 * @return The string for this byte count 371 */ 372 @Override 373 public String toString() 374 { 375 return toString(Locale.getDefault(Locale.Category.FORMAT)); 376 } 377 378 /** 379 * Converts this byte count to a string using the given locale. 380 * 381 * @param locale 382 * Locale to use for conversion 383 * @return The string for this byte count 384 */ 385 public String toString(final Locale locale) 386 { 387 if (terabytes() >= 1.0) 388 { 389 return unitString(terabytes(), "TB", locale); 390 } 391 392 if (gigabytes() >= 1.0) 393 { 394 return unitString(gigabytes(), "GB", locale); 395 } 396 397 if (megabytes() >= 1.0) 398 { 399 return unitString(megabytes(), "MB", locale); 400 } 401 402 if (kilobytes() >= 1.0) 403 { 404 return unitString(kilobytes(), "KB", locale); 405 } 406 407 return Long.toString(value) + " bytes"; 408 } 409 410 /** 411 * Convert value to formatted floating point number and units. 412 * 413 * @param value 414 * The value 415 * @param units 416 * The units 417 * @param locale 418 * The locale 419 * @return The formatted string 420 */ 421 private String unitString(final double value, final String units, final Locale locale) 422 { 423 return StringValue.valueOf(value, locale) + units; 424 } 425 426 /** 427 * Compares this <code>Bytes</code> with another <code>Bytes</code> instance. 428 * 429 * @param other 430 * the <code>Bytes</code> instance to compare with 431 * @return <code>true</code> if this <code>Bytes</code> is greater than the given 432 * <code>Bytes</code> instance 433 */ 434 public boolean greaterThan(final Bytes other) 435 { 436 if ((this == other) || (other == null)) 437 { 438 return false; 439 } 440 return bytes() > other.bytes(); 441 } 442}