001package net.filebot.format; 002 003import java.math.BigDecimal; 004import java.math.RoundingMode; 005import java.text.DecimalFormat; 006import java.text.DecimalFormatSymbols; 007import java.util.Locale; 008import java.util.stream.Stream; 009 010import net.filebot.util.AlphanumComparator; 011 012public class AutoScaleInteger implements StringBinding, Comparable<Object> { 013 014 public enum Scale { 015 016 AUTO(1), K(1_000), M(1_000_000), G(1_000_000_000); 017 018 public final int factor; 019 020 private Scale(int factor) { 021 this.factor = factor; 022 } 023 024 public static Scale find(long value) { 025 return Stream.of(G, M, K).filter(m -> value >= m.factor).findFirst().orElse(AUTO); 026 } 027 } 028 029 protected final long value; 030 protected final Scale scale; 031 032 public AutoScaleInteger(long value, Scale scale) { 033 this.value = value; 034 this.scale = scale; 035 } 036 037 public long getValue() { 038 return value; 039 } 040 041 public Scale getScale() { 042 return scale; 043 } 044 045 public AutoScaleInteger scale(Scale scale) { 046 return new AutoScaleInteger(value, scale); 047 } 048 049 public AutoScaleInteger getG() { 050 return scale(Scale.G); 051 } 052 053 public AutoScaleInteger getM() { 054 return scale(Scale.M); 055 } 056 057 public AutoScaleInteger getK() { 058 return scale(Scale.K); 059 } 060 061 public long toLong() { 062 return value / scale.factor; 063 } 064 065 public double toDouble() { 066 return (double) value / scale.factor; 067 } 068 069 public int toInteger() { 070 return (int) toLong(); 071 } 072 073 public float toFloat() { 074 return (float) toDouble(); 075 } 076 077 public BigDecimal toNumber() { 078 return BigDecimal.valueOf(toDouble()); 079 } 080 081 @Override 082 public int compareTo(Object other) { 083 if (other instanceof Integer) { 084 Number n = (Number) other; 085 return Integer.compare(toInteger(), n.intValue()); 086 } 087 088 if (other instanceof Number) { 089 Number n = (Number) other; 090 return Double.compare(toDouble(), n.doubleValue()); 091 } 092 093 if (other instanceof AutoScaleInteger) { 094 AutoScaleInteger n = (AutoScaleInteger) other; 095 return Long.compare(value, n.value); 096 } 097 098 return AlphanumComparator.getInstance().compare(toString(), other.toString()); 099 } 100 101 @Override 102 public boolean equals(Object other) { 103 return (other instanceof Number && compareTo(other) == 0) || (other instanceof CharSequence && toString().equals(other)); 104 } 105 106 public Object asType(Class<?> c) { 107 if (c == int.class || c == Integer.class) { 108 return toInteger(); 109 } 110 111 if (c == long.class || c == Long.class) { 112 return toLong(); 113 } 114 115 if (c == float.class || c == Float.class) { 116 return toFloat(); 117 } 118 119 if (c == double.class || c == Double.class) { 120 return toDouble(); 121 } 122 123 if (c == Number.class) { 124 return toNumber(); 125 } 126 127 if (c == String.class) { 128 return toString(); 129 } 130 131 // fail with type cast exception message 132 return c.cast(this); 133 } 134 135 public Number div(Number divisor) { 136 return BigDecimal.valueOf(toDouble() / divisor.doubleValue()); 137 } 138 139 public Number round(int precision) { 140 return BigDecimal.valueOf(toDouble()).setScale(precision, RoundingMode.HALF_UP); 141 } 142 143 public String format(String pattern) { 144 return format(pattern, Locale.ROOT); 145 } 146 147 public String format(String pattern, String locale) { 148 return format(pattern, Locale.forLanguageTag(locale)); 149 } 150 151 public String format(String pattern, Locale locale) { 152 DecimalFormat f = new DecimalFormat(pattern, new DecimalFormatSymbols(locale)); 153 return f.format(toDouble()); 154 } 155 156 public String getUnit(Scale scale) { 157 return scale == Scale.AUTO ? null : scale.name(); 158 } 159 160 @Override 161 public String toString() { 162 // auto scale or fixed scale 163 return scale == Scale.AUTO ? toString(Scale.find(value)) : toString(scale); 164 } 165 166 public String toString(Scale target) { 167 String u = getUnit(target); 168 169 // format absolute value 170 if (u == null) { 171 return String.format(Locale.ROOT, "%,d", value); 172 } 173 174 // scaled value 175 double v = (double) value / target.factor; 176 177 // format single digit scaled values with one fraction digit 178 if (v < 9.5 && target.factor > 1) { 179 return String.format(Locale.ROOT, "%,.1f %s", v, u); 180 } 181 182 // format scaled value 183 return String.format(Locale.ROOT, "%,.0f %s", v, u); 184 } 185 186}