import com.google.common.annotations.Beta;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
-import com.google.common.primitives.Longs;
import java.math.BigDecimal;
-import org.opendaylight.yangtools.concepts.Immutable;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
/**
* Dedicated type for YANG's 'type decimal64' type. This class is similar to {@link BigDecimal}, but provides more
* @author Robert Varga
*/
@Beta
-public final class Decimal64 extends Number implements Comparable<Decimal64>, Immutable {
+@NonNullByDefault
+public class Decimal64 extends Number implements CanonicalValue<Decimal64> {
+ private static final class Support extends AbstractCanonicalValueSupport<Decimal64> {
+ Support() {
+ super(Decimal64.class);
+ }
+
+ @Override
+ public Decimal64 fromString(final String str) {
+ return Decimal64.valueOf(str);
+ }
+ }
+
+ private static final CanonicalValueSupport<Decimal64> SUPPORT = new Support();
private static final long serialVersionUID = 1L;
private static final int MAX_FRACTION_DIGITS = 18;
this.value = negative ? -bits : bits;
}
+ protected Decimal64(final Decimal64 other) {
+ this.scaleOffset = other.scaleOffset;
+ this.value = other.value;
+ }
+
public static Decimal64 valueOf(final byte byteVal) {
return byteVal < 0 ? new Decimal64(1, -byteVal, 0, true) : new Decimal64(1, byteVal, 0, false);
}
}
public static Decimal64 valueOf(final int intVal) {
- return intVal < 0 ? new Decimal64(1, -intVal, 0, true) : new Decimal64(1, intVal, 0, false);
+ return intVal < 0 ? new Decimal64(1, - (long)intVal, 0, true) : new Decimal64(1, intVal, 0, false);
}
public static Decimal64 valueOf(final long longVal) {
return new Decimal64(fracLen, intPart, fracPart, negative);
}
- public BigDecimal decimalValue() {
+ public final BigDecimal decimalValue() {
return BigDecimal.valueOf(value, scaleOffset + 1);
}
@Override
- public int intValue() {
+ public final int intValue() {
return (int) intPart();
}
@Override
- public long longValue() {
+ public final long longValue() {
return intPart();
}
@Override
- public float floatValue() {
+ public final float floatValue() {
return (float) doubleValue();
}
@Override
- public double doubleValue() {
+ public final double doubleValue() {
return 1.0 * value / SCALE[scaleOffset];
}
+ /**
+ * Converts this {@code BigDecimal} to a {@code byte}, checking for lost information. If this {@code Decimal64} has
+ * a nonzero fractional part or is out of the possible range for a {@code byte} result then
+ * an {@code ArithmeticException} is thrown.
+ *
+ * @return this {@code Decimal64} converted to a {@code byte}.
+ * @throws ArithmeticException if {@code this} has a nonzero fractional part, or will not fit in a {@code byte}.
+ */
+ public final byte byteValueExact() {
+ final long val = longValueExact();
+ final byte ret = (byte) val;
+ if (val != ret) {
+ throw new ArithmeticException("Value " + val + " is outside of byte range");
+ }
+ return ret;
+ }
+
+ /**
+ * Converts this {@code BigDecimal} to a {@code short}, checking for lost information. If this {@code Decimal64} has
+ * a nonzero fractional part or is out of the possible range for a {@code short} result then
+ * an {@code ArithmeticException} is thrown.
+ *
+ * @return this {@code Decimal64} converted to a {@code short}.
+ * @throws ArithmeticException if {@code this} has a nonzero fractional part, or will not fit in a {@code short}.
+ */
+ public final short shortValueExact() {
+ final long val = longValueExact();
+ final short ret = (short) val;
+ if (val != ret) {
+ throw new ArithmeticException("Value " + val + " is outside of short range");
+ }
+ return ret;
+ }
+
+ /**
+ * Converts this {@code BigDecimal} to an {@code int}, checking for lost information. If this {@code Decimal64} has
+ * a nonzero fractional part or is out of the possible range for an {@code int} result then
+ * an {@code ArithmeticException} is thrown.
+ *
+ * @return this {@code Decimal64} converted to an {@code int}.
+ * @throws ArithmeticException if {@code this} has a nonzero fractional part, or will not fit in an {@code int}.
+ */
+ public final int intValueExact() {
+ final long val = longValueExact();
+ final int ret = (int) val;
+ if (val != ret) {
+ throw new ArithmeticException("Value " + val + " is outside of integer range");
+ }
+ return ret;
+ }
+
+ /**
+ * Converts this {@code BigDecimal} to a {@code long}, checking for lost information. If this {@code Decimal64} has
+ * a nonzero fractional part then an {@code ArithmeticException} is thrown.
+ *
+ * @return this {@code Decimal64} converted to a {@code long}.
+ * @throws ArithmeticException if {@code this} has a nonzero fractional part.
+ */
+ public final long longValueExact() {
+ if (fracPart() != 0) {
+ throw new ArithmeticException("Conversion of " + this + " would lose fraction");
+ }
+ return intPart();
+ }
+
@Override
@SuppressWarnings("checkstyle:parameterName")
- public int compareTo(final Decimal64 o) {
+ public final int compareTo(final Decimal64 o) {
if (this == o) {
return 0;
}
}
@Override
- public int hashCode() {
+ public final String toCanonicalString() {
+ // https://tools.ietf.org/html/rfc6020#section-9.3.2
+ //
+ // The canonical form of a positive decimal64 does not include the sign
+ // "+". The decimal point is required. Leading and trailing zeros are
+ // prohibited, subject to the rule that there MUST be at least one digit
+ // before and after the decimal point. The value zero is represented as
+ // "0.0".
+ final StringBuilder sb = new StringBuilder(21).append(intPart()).append('.');
+ final long fracPart = fracPart();
+ if (fracPart != 0) {
+ // We may need to zero-pad the fraction part
+ sb.append(Strings.padStart(Long.toString(fracPart), scaleOffset + 1, '0'));
+ } else {
+ sb.append('0');
+ }
+
+ return sb.toString();
+ }
+
+ @Override
+ public final CanonicalValueSupport<Decimal64> support() {
+ return SUPPORT;
+ }
+
+ @Override
+ public final int hashCode() {
// We need to normalize the results in order to be consistent with equals()
- return Longs.hashCode(intPart()) * 31 + Long.hashCode(fracPart());
+ return Long.hashCode(intPart()) * 31 + Long.hashCode(fracPart());
}
@Override
- public boolean equals(final Object obj) {
+ public final boolean equals(final @Nullable Object obj) {
if (this == obj) {
return true;
}
}
@Override
- public String toString() {
- // https://tools.ietf.org/html/rfc6020#section-9.3.2
- //
- // The canonical form of a positive decimal64 does not include the sign
- // "+". The decimal point is required. Leading and trailing zeros are
- // prohibited, subject to the rule that there MUST be at least one digit
- // before and after the decimal point. The value zero is represented as
- // "0.0".
- final StringBuilder sb = new StringBuilder(21).append(intPart()).append('.');
- final long fracPart = fracPart();
- if (fracPart != 0) {
- // We may need to zero-pad the fraction part
- sb.append(Strings.padStart(Long.toString(fracPart), scaleOffset + 1, '0'));
- } else {
- sb.append('0');
- }
-
- return sb.toString();
+ public final String toString() {
+ return toCanonicalString();
}
private long intPart() {