Example #1
0
public enum SizeUnit {
  BYTES(0, "B"),
  KILOBYTES(1, "KB"),
  MEGABYTES(2, "MB"),
  GIGABYTES(3, "GB"),
  TERABYTES(4, "TB");

  private final int ordinal;
  private final String abbreviation;

  private SizeUnit(int ordinal, String abbreviation) {
    this.ordinal = ordinal;
    this.abbreviation = abbreviation;
  }

  public int getOrdinal() {
    return ordinal;
  }

  public String getAbbreviation() {
    return abbreviation;
  }

  private static long multiplyByByteOrderOfMagnitude(double size, int magnitude) {
    if (magnitude == 0) {
      return (long) size;
    } else if (magnitude > 0) {
      return BigDecimal.valueOf(size).multiply(BigDecimal.valueOf(1024).pow(magnitude)).longValue();
    } else {
      return BigDecimal.valueOf(size)
          .divide(BigDecimal.valueOf(1024).pow(-1 * magnitude))
          .longValue();
    }
  }

  private static final ImmutableMap<String, SizeUnit> SHORT_TO_CODE =
      ImmutableMap.<String, SizeUnit>builder()
          .put("b", BYTES)
          .put("kb", KILOBYTES)
          .put("kilobytes", KILOBYTES)
          .put("mb", MEGABYTES)
          .put("megabytes", MEGABYTES)
          .put("gb", GIGABYTES)
          .put("gigabytes", GIGABYTES)
          .put("tb", TERABYTES)
          .put("terabytes", TERABYTES)
          .build();

  private static final Pattern SIZE_PATTERN =
      Pattern.compile(
          "([\\d]+(?:\\.[\\d]+)?)\\s*" + MoreStrings.regexPatternForAny(SHORT_TO_CODE.keySet()),
          Pattern.CASE_INSENSITIVE);

  /** Parses a string that represents a size into the number of bytes represented by that string. */
  public static long parseBytes(String input) throws NumberFormatException {
    Matcher matcher = SIZE_PATTERN.matcher(input);
    if (matcher.find()) {
      String number = matcher.group(1);
      SizeUnit sizeUnit = SHORT_TO_CODE.get(matcher.group(2).toLowerCase());
      if (sizeUnit != null) {
        try {
          double value = Double.parseDouble(number);
          return sizeUnit.toBytes(value);
        } catch (NumberFormatException e) {
          // If the number was so large as to overflow Long.MAX_VALUE, return LONG.MAX_VALUE.
          return Long.MAX_VALUE;
        }
      }
    }
    throw new NumberFormatException(String.format("%s is not a valid file size", input));
  }

  public long toBytes(double size) {
    return multiplyByByteOrderOfMagnitude(size, getOrdinal() - BYTES.getOrdinal());
  }

  public long toKilobytes(double size) {
    return multiplyByByteOrderOfMagnitude(size, getOrdinal() - KILOBYTES.getOrdinal());
  }

  public long toMegabytes(double size) {
    return multiplyByByteOrderOfMagnitude(size, getOrdinal() - MEGABYTES.getOrdinal());
  }

  public long toGigabytes(double size) {
    return multiplyByByteOrderOfMagnitude(size, getOrdinal() - GIGABYTES.getOrdinal());
  }

  public long toTerabytes(double size) {
    return multiplyByByteOrderOfMagnitude(size, getOrdinal() - TERABYTES.getOrdinal());
  }

  public static Pair<Double, SizeUnit> getHumanReadableSize(double size, SizeUnit unit) {
    if (size == 0) {
      return new Pair<>(size, unit);
    }
    int ordinal = unit.getOrdinal();
    double resultSize = size;
    if (size > 1) {
      while (size > 1 && ordinal < 4) {
        size = size / 1024;
        if (size > 1) {
          ordinal++;
          resultSize = size;
        }
      }
    } else {
      while (size < 1024 && ordinal > 0) {
        size = size * 1024;
        if (size < 1024) {
          ordinal--;
          resultSize = size;
        }
      }
    }
    return new Pair<>(resultSize, SizeUnit.values()[ordinal]);
  }

  public static String toHumanReadableString(Pair<Double, SizeUnit> size, Locale locale) {
    return String.format(locale, "%.2f %s", size.getFirst(), size.getSecond().getAbbreviation());
  }
}