-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Quantity type #129
Merged
brendandburns
merged 7 commits into
kubernetes-client:master
from
lewisheadden:AddQuantity
Nov 29, 2017
Merged
Add Quantity type #129
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
a295a5d
Add Quantity type and tests
924458f
Use Commons Pair
af94a84
getBigDecimal -> getNumber
cd4108d
Update or remove imports
6cf2c64
Simplify BaseExponent#equals
f37eee2
Fix line-spacing
cb1bfc2
Use String#hashCode instead of auto-generated one
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JavaDoc?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy to add JavaDoc - to clarify, this is an across the board ask rather than just for
BaseExponent
?