-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #129 from lewisheadden/AddQuantity
Add Quantity type
- Loading branch information
Showing
13 changed files
with
712 additions
and
154 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
50 changes: 50 additions & 0 deletions
50
kubernetes/src/main/java/io/kubernetes/client/custom/BaseExponent.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package io.kubernetes.client.custom; | ||
|
||
public class BaseExponent { | ||
|
||
private final int base; | ||
private final int exponent; | ||
private final Quantity.Format format; | ||
|
||
public BaseExponent(final int base, final int exponent, final Quantity.Format format) { | ||
this.base = base; | ||
this.exponent = exponent; | ||
this.format = format; | ||
} | ||
|
||
public int getBase() { | ||
return base; | ||
} | ||
|
||
public int getExponent() { | ||
return exponent; | ||
} | ||
|
||
public Quantity.Format getFormat() { | ||
return format; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "BaseExponent{" + | ||
"base=" + base + | ||
", exponent=" + exponent + | ||
", format=" + format + | ||
'}'; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) return true; | ||
if (o == null || getClass() != o.getClass()) return false; | ||
|
||
BaseExponent that = (BaseExponent) o; | ||
|
||
return base == that.base && exponent == that.exponent && format == that.format; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return this.toString().hashCode(); | ||
} | ||
} |
71 changes: 71 additions & 0 deletions
71
kubernetes/src/main/java/io/kubernetes/client/custom/Quantity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package io.kubernetes.client.custom; | ||
|
||
import com.google.gson.TypeAdapter; | ||
import com.google.gson.annotations.JsonAdapter; | ||
import com.google.gson.stream.JsonReader; | ||
import com.google.gson.stream.JsonWriter; | ||
|
||
import java.io.IOException; | ||
import java.math.BigDecimal; | ||
|
||
@JsonAdapter(Quantity.QuantityAdapter.class) | ||
public class Quantity { | ||
|
||
private final BigDecimal number; | ||
private Format format; | ||
|
||
public enum Format { | ||
DECIMAL_EXPONENT(10), DECIMAL_SI(10), BINARY_SI(2); | ||
|
||
private int base; | ||
|
||
Format(final int base) { | ||
this.base = base; | ||
} | ||
|
||
public int getBase() { | ||
return base; | ||
} | ||
} | ||
|
||
public Quantity(final BigDecimal number, final Format format) { | ||
this.number = number; | ||
this.format = format; | ||
} | ||
|
||
public BigDecimal getNumber() { | ||
return number; | ||
} | ||
|
||
public Format getFormat() { | ||
return format; | ||
} | ||
|
||
public static Quantity fromString(final String value) { | ||
return new QuantityFormatter().parse(value); | ||
} | ||
|
||
public String toSuffixedString() { | ||
return new QuantityFormatter().format(this); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "Quantity{" + | ||
"number=" + number + | ||
", format=" + format + | ||
'}'; | ||
} | ||
|
||
public class QuantityAdapter extends TypeAdapter<Quantity> { | ||
@Override | ||
public void write(JsonWriter jsonWriter, Quantity quantity) throws IOException { | ||
jsonWriter.value(quantity.toSuffixedString()); | ||
} | ||
|
||
@Override | ||
public Quantity read(JsonReader jsonReader) throws IOException { | ||
return Quantity.fromString(jsonReader.nextString()); | ||
} | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
kubernetes/src/main/java/io/kubernetes/client/custom/QuantityFormatException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package io.kubernetes.client.custom; | ||
|
||
public class QuantityFormatException extends RuntimeException { | ||
public QuantityFormatException(String s) { | ||
super(s); | ||
} | ||
} |
92 changes: 92 additions & 0 deletions
92
kubernetes/src/main/java/io/kubernetes/client/custom/QuantityFormatter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package io.kubernetes.client.custom; | ||
|
||
import org.apache.commons.lang3.tuple.Pair; | ||
|
||
import java.math.BigDecimal; | ||
import java.math.MathContext; | ||
|
||
public class QuantityFormatter { | ||
|
||
private static final String PARTS_RE = "[eEinumkKMGTP]+"; | ||
|
||
public Quantity parse(final String value) { | ||
if (value == null || value.isEmpty()) { | ||
throw new QuantityFormatException(""); | ||
} | ||
final String[] parts = value.split(PARTS_RE); | ||
final BigDecimal numericValue = parseNumericValue(parts[0]); | ||
final String suffix = value.substring(parts[0].length()); | ||
final BaseExponent baseExponent = new SuffixFormatter().parse(suffix); | ||
final BigDecimal unitMultiplier = BigDecimal.valueOf(baseExponent.getBase()).pow(baseExponent.getExponent(), MathContext.DECIMAL64); | ||
final BigDecimal unitlessValue = numericValue.multiply(unitMultiplier); | ||
return new Quantity(unitlessValue, baseExponent.getFormat()); | ||
} | ||
|
||
private static BigDecimal parseNumericValue(String part) { | ||
try { | ||
return new BigDecimal(part); | ||
} catch (final NumberFormatException e) { | ||
throw new QuantityFormatException("Unable to parse numeric part of quantity: " + part); | ||
} | ||
} | ||
|
||
public String format(final Quantity quantity) { | ||
switch (quantity.getFormat()) { | ||
case DECIMAL_SI: | ||
case DECIMAL_EXPONENT: | ||
return toBase10String(quantity); | ||
case BINARY_SI: | ||
if (isFractional(quantity)) { | ||
return toBase10String(new Quantity(quantity.getNumber(), Quantity.Format.DECIMAL_SI)); | ||
} | ||
return toBase1024String(quantity); | ||
default: | ||
throw new IllegalArgumentException("Can't format a " + quantity.getFormat() + " quantity"); | ||
} | ||
} | ||
|
||
private boolean isFractional(Quantity quantity) { | ||
return quantity.getNumber().scale() > 0; | ||
} | ||
|
||
private String toBase1024String(final Quantity quantity) { | ||
final BigDecimal amount = quantity.getNumber(); | ||
final long value = amount.unscaledValue().longValue(); | ||
final int exponent = -amount.scale(); | ||
final Pair<Long, Integer> resultAndTimes = removeFactorsForBase(value, 1024); | ||
return resultAndTimes.getLeft() + new SuffixFormatter().format(quantity.getFormat(), exponent + resultAndTimes.getRight() * 10); | ||
} | ||
|
||
private String toBase10String(final Quantity quantity) { | ||
final BigDecimal amount = quantity.getNumber(); | ||
final long value = amount.unscaledValue().longValue(); | ||
final int exponent = -amount.scale(); | ||
final Pair<Long, Integer> resultAndTimes = removeFactorsForBase(value, 10); | ||
final int postFactoringExponent = exponent + resultAndTimes.getRight(); | ||
final Pair<Long, Integer> valueAndExponent = ensureExponentIsMultipleOf3(resultAndTimes.getLeft(), postFactoringExponent); | ||
return valueAndExponent.getLeft() + new SuffixFormatter().format(quantity.getFormat(), valueAndExponent.getRight()); | ||
} | ||
|
||
private Pair<Long, Integer> ensureExponentIsMultipleOf3(final long mantissa, final int exponent) { | ||
final long exponentRemainder = exponent % 3; | ||
if (exponentRemainder == 1 || exponentRemainder == -2) { | ||
return Pair.of(mantissa * 10, exponent - 1); | ||
} else if (exponentRemainder == -1 || exponentRemainder == 2) { | ||
return Pair.of(mantissa * 100, exponent - 2); | ||
} else { | ||
return Pair.of(mantissa, exponent); | ||
} | ||
} | ||
|
||
private Pair<Long, Integer> removeFactorsForBase(final long value, final int base) { | ||
int times = 0; | ||
long result = value; | ||
while (result >= base && result % base == 0) { | ||
times++; | ||
result = result / base; | ||
} | ||
return Pair.of(result, times); | ||
} | ||
|
||
} | ||
|
107 changes: 107 additions & 0 deletions
107
kubernetes/src/main/java/io/kubernetes/client/custom/SuffixFormatter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package io.kubernetes.client.custom; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
public class SuffixFormatter { | ||
|
||
private static final Map<String, BaseExponent> suffixToBinary = new HashMap<String, BaseExponent>() { | ||
{ | ||
put("", new BaseExponent(2, 0, Quantity.Format.BINARY_SI)); | ||
put("Ki", new BaseExponent(2, 10, Quantity.Format.BINARY_SI)); | ||
put("Mi", new BaseExponent(2, 20, Quantity.Format.BINARY_SI)); | ||
put("Gi", new BaseExponent(2, 30, Quantity.Format.BINARY_SI)); | ||
put("Ti", new BaseExponent(2, 40, Quantity.Format.BINARY_SI)); | ||
put("Pi", new BaseExponent(2, 50, Quantity.Format.BINARY_SI)); | ||
put("Ei", new BaseExponent(2, 60, Quantity.Format.BINARY_SI)); | ||
} | ||
}; | ||
|
||
private static final Map<String, BaseExponent> suffixToDecimal = new HashMap<String, BaseExponent>() { | ||
{ | ||
put("n", new BaseExponent(10, -9, Quantity.Format.DECIMAL_SI)); | ||
put("u", new BaseExponent(10, -6, Quantity.Format.DECIMAL_SI)); | ||
put("m", new BaseExponent(10, -3, Quantity.Format.DECIMAL_SI)); | ||
put("", new BaseExponent(10, 0, Quantity.Format.DECIMAL_SI)); | ||
put("k", new BaseExponent(10, 3, Quantity.Format.DECIMAL_SI)); | ||
put("M", new BaseExponent(10, 6, Quantity.Format.DECIMAL_SI)); | ||
put("G", new BaseExponent(10, 9, Quantity.Format.DECIMAL_SI)); | ||
put("T", new BaseExponent(10, 12, Quantity.Format.DECIMAL_SI)); | ||
put("P", new BaseExponent(10, 15, Quantity.Format.DECIMAL_SI)); | ||
put("E", new BaseExponent(10, 18, Quantity.Format.DECIMAL_SI)); | ||
} | ||
}; | ||
|
||
private static final Map<BaseExponent, String> decimalToSuffix = new HashMap<BaseExponent, String>() { | ||
{ | ||
for (Entry<String, BaseExponent> entry : suffixToDecimal.entrySet()) { | ||
put(entry.getValue(), entry.getKey()); | ||
} | ||
} | ||
}; | ||
|
||
private static final Map<BaseExponent, String> binaryToSuffix = new HashMap<BaseExponent, String>() { | ||
{ | ||
for (Entry<String, BaseExponent> entry : suffixToBinary.entrySet()) { | ||
put(entry.getValue(), entry.getKey()); | ||
} | ||
} | ||
}; | ||
|
||
public BaseExponent parse(final String suffix) { | ||
final BaseExponent decimalSuffix = suffixToDecimal.get(suffix); | ||
if (decimalSuffix != null) { | ||
return decimalSuffix; | ||
} | ||
|
||
final BaseExponent binarySuffix = suffixToBinary.get(suffix); | ||
if (binarySuffix != null) { | ||
return binarySuffix; | ||
} | ||
|
||
if (suffix.length() > 0 && (suffix.charAt(0) == 'E' || suffix.charAt(0) == 'e')) { | ||
return extractDecimalExponent(suffix); | ||
} | ||
|
||
throw new QuantityFormatException("Could not parse suffix"); | ||
} | ||
|
||
private BaseExponent extractDecimalExponent(String suffix) { | ||
try { | ||
final int exponent = Integer.parseInt(suffix.substring(1)); | ||
return new BaseExponent(10, exponent, Quantity.Format.DECIMAL_EXPONENT); | ||
} catch (final NumberFormatException e) { | ||
throw new QuantityFormatException("Can't parse decimal exponent from " + suffix.substring(1)); | ||
} | ||
} | ||
|
||
public String format(final Quantity.Format format, final int exponent) { | ||
switch (format) { | ||
case DECIMAL_SI: | ||
return getDecimalSiSuffix(exponent); | ||
case BINARY_SI: | ||
return getBinarySiSuffix(exponent); | ||
case DECIMAL_EXPONENT: | ||
return exponent == 0 ? "" : "e" + exponent; | ||
default: | ||
throw new IllegalStateException("Can't format " + format + " with exponent " + exponent); | ||
} | ||
} | ||
|
||
private String getBinarySiSuffix(int exponent) { | ||
final String suffix = binaryToSuffix.get(new BaseExponent(2, exponent, Quantity.Format.BINARY_SI)); | ||
if (suffix == null) { | ||
throw new IllegalArgumentException("No suffix for exponent" + exponent); | ||
} | ||
return suffix; | ||
} | ||
|
||
private String getDecimalSiSuffix(int exponent) { | ||
final String suffix = decimalToSuffix.get(new BaseExponent(10, exponent, Quantity.Format.DECIMAL_SI)); | ||
if (suffix == null) { | ||
throw new IllegalArgumentException("No suffix for exponent" + exponent); | ||
} | ||
return suffix; | ||
} | ||
|
||
} |
Oops, something went wrong.