/*
* This file is part of muCommander, http://www.mucommander.com
* Copyright (C) 2002-2016 Maxence Bernard
*
* muCommander is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* muCommander is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.mucommander.text;
import java.text.DecimalFormat;
import java.text.NumberFormat;
/**
* SizeFormat formats byte sizes into localized string representations.
*
* @author Maxence Bernard.
*/
public class SizeFormat {
/** Bitmask for short digits, e.g. "15" */
public final static int DIGITS_SHORT = 1;
/** Bitmask for medium digits, e.g. "15,2" */
public final static int DIGITS_MEDIUM = 2;
/** Bitmask for full digits, e.g. "15,204,405" */
public final static int DIGITS_FULL = 4;
/** Bitmask for no unit string */
public final static int UNIT_NONE = 0;
/** Bitmask for short unit string, e.g. "b" */
public final static int UNIT_SHORT = 8;
/** Bitmask for short unit string, e.g. "bytes" */
public final static int UNIT_LONG = 16;
/** Byte unit */
public final static int BYTE_UNIT = 0;
/** Kilobyte unit */
public final static int KILOBYTE_UNIT = 1;
/** Megabyte unit */
public final static int MEGABYTE_UNIT = 2;
/** Gigabyte unit */
public final static int GIGABYTE_UNIT = 3;
/** Terabyte unit */
public final static int TERABYTE_UNIT = 4;
/** Bitmask to add '/s' (per second) to the returned String */
public final static int UNIT_SPEED = 32;
/** Bitmask to include a space character to separate the digits and unit parts */
public final static int INCLUDE_SPACE = 64;
/** Bitmask to round any size < 1KB to 1KB (except 0 which will be 0 KB) */
public final static int ROUND_TO_KB = 128;
/** One kilobyte: 2^10 */
private final static int KB_1 = 1024;
/** Ten kilobytes: (2^10)*10 */
private final static int KB_10 = 10240;
/** One megabyte: 2^20 */
private final static int MB_1 = 1048576;
/** Ten megabytes: (2^20)*10 */
private final static int MB_10 = 10485760;
/** One gigabyte: 2^30 */
private final static int GB_1 = 1073741824;
/** Ten gigabytes: (2^10)*10 */
private final static long GB_10 = 10737418240l;
/** One terabyte: 2^40 */
private final static long TB_1 = 1099511627776l;
/** Ten terabytes: (2^40)*10 */
private final static long TB_10 = 10995116277760l;
/** DecimalFormat instance to localize thousands separators */
private final static DecimalFormat DECIMAL_FORMAT = (DecimalFormat)NumberFormat.getInstance();
/** Localized decimal separator */
private final static String DECIMAL_SEPARATOR = ""+DECIMAL_FORMAT.getDecimalFormatSymbols().getDecimalSeparator();
/////////////////////
// Dictionary keys //
/////////////////////
private final static String BYTE = Translator.get("unit.byte");
private final static String BYTES = Translator.get("unit.bytes");
private final static String B = Translator.get("unit.bytes_short");
private final static String KB = Translator.get("unit.kb");
private final static String MB = Translator.get("unit.mb");
private final static String GB = Translator.get("unit.gb");
private final static String TB = Translator.get("unit.tb");
private final static String SPEED_KEY = "unit.speed";
/**
* Returns a String representation of the given byte size.
*
* @param size the size to format
* @param format format bitmask, see constant fields for allowed values
* @return a String representation of the given byte size
*/
public static String format(long size, int format) {
if(size<0)
return "?";
String digitsString;
String unitString;
// Whether the unit string should be long or not
boolean unitLong = (format&UNIT_LONG)!=0;
// Whether the unit string should be short or not
boolean unitShort = (format&UNIT_SHORT)!=0;
// Whether the unit string should be short or not
boolean noUnit = !(unitLong||unitShort);
// Whether the digits string should be short or not
boolean digitsShort = (format&DIGITS_SHORT)!=0;
// Whether any size < 1024 bytes should be rounded to a kilobyte
boolean roundToKb = (format&ROUND_TO_KB)!=0;
// size < 1KB
if(size<KB_1) {
if(roundToKb) {
// Note: ROUND_TO_KB must have precedence over DIGITS_FULL
digitsString = size==0?"0":"1";
unitString = noUnit?"":KB;
}
else {
digitsString = ""+size;
unitString = unitLong?(size<=1?BYTE:BYTES):unitShort?B:"";
}
}
else if((format&DIGITS_FULL)!=0) {
// DecimalFormat localizes thousands separators
// Calls to DecimalFormat must be synchronized.
// Quote from DecimalFormat's Javadoc: "Decimal formats are generally not synchronized. It is recommended
// to create separate format instances for each thread. If multiple threads access a format concurrently,
// it must be synchronized externally."
synchronized(DECIMAL_FORMAT) {
digitsString = DECIMAL_FORMAT.format(size);
}
unitString = unitLong?BYTES:unitShort?B:"";
}
else {
// size < 10KB -> "9.6 KB"
if(size<KB_10 && !digitsShort) {
int nKB = (int)size/KB_1;
digitsString = nKB+DECIMAL_SEPARATOR+(int)((size-nKB*KB_1)/(float)KB_1*10);
unitString = noUnit?"":KB;
}
// size < 1MB -> "436 KB"
else if(size<MB_1) {
digitsString = ""+size/KB_1;
unitString = noUnit?"":KB;
}
// size < 10MB -> "4.3 MB"
else if(size<MB_10 && !digitsShort) {
int nMB = (int)size/MB_1;
digitsString = nMB+DECIMAL_SEPARATOR+(int)((size-nMB*MB_1)/(float)MB_1*10);
unitString = noUnit?"":MB;
}
// size < 1GB -> "548 MB"
else if(size<GB_1) {
digitsString = ""+size/MB_1;
unitString = noUnit?"":MB;
}
// size < 10GB -> "4.8 GB"
else if(size<GB_10 && !digitsShort) {
long nGB = size/GB_1;
digitsString = nGB+DECIMAL_SEPARATOR+(int)((size-nGB*GB_1)/(double)GB_1*10);
unitString = noUnit?"":GB;
}
// size < 1TB -> "216 GB"
else if(size<TB_1) {
digitsString = ""+size/GB_1;
unitString = noUnit?"":GB;
}
// size < 10TB -> "4.8 TB"
else if(size<TB_10 && !digitsShort) {
long nTB = size/TB_1;
digitsString = nTB+DECIMAL_SEPARATOR+(int)((size-nTB*TB_1)/(double)TB_1*10);
unitString = noUnit?"":TB;
}
else {
// Will I live long enough to see files that large ??
digitsString = ""+size/TB_1;
unitString = noUnit?"":TB;
}
}
// Add localized '/s' to unit string if unit is speed
if((format&UNIT_SPEED)!=0)
unitString = Translator.get(SPEED_KEY, unitString);
return digitsString+((format&INCLUDE_SPACE)!=0?" ":"")+unitString;
}
public static String getUnitString(int unit, boolean speedUnit) {
String unitString;
switch(unit) {
case BYTE_UNIT:
unitString = B;
break;
case KILOBYTE_UNIT:
unitString = KB;
break;
case MEGABYTE_UNIT:
unitString = MB;
break;
case GIGABYTE_UNIT:
unitString = GB;
break;
case TERABYTE_UNIT:
unitString = TB;
break;
default:
return "";
}
return speedUnit?Translator.get(SPEED_KEY, unitString):unitString;
}
/**
* Returns the size in bytes of the given byte unit, e.g. <code>1024</code> for {@link #KILOBYTE_UNIT}.
*
* @param unit a unit constant, see constant fields for allowed values
* @return the size in bytes of the given byte unit
*/
public static long getUnitBytes(int unit) {
long bytes;
switch(unit) {
case BYTE_UNIT:
bytes = 1;
break;
case KILOBYTE_UNIT:
bytes = KB_1;
break;
case MEGABYTE_UNIT:
bytes = MB_1;
break;
case GIGABYTE_UNIT:
bytes = GB_1;
break;
case TERABYTE_UNIT:
bytes = TB_1;
break;
default:
return 0;
}
return bytes;
}
}