diff --git a/codecs/json-codec/src/main/java/software/amazon/smithy/java/json/JsonDocuments.java b/codecs/json-codec/src/main/java/software/amazon/smithy/java/json/JsonDocuments.java index d019456cb..336cfe5fc 100644 --- a/codecs/json-codec/src/main/java/software/amazon/smithy/java/json/JsonDocuments.java +++ b/codecs/json-codec/src/main/java/software/amazon/smithy/java/json/JsonDocuments.java @@ -48,7 +48,17 @@ public static Document of(boolean value, JsonSettings settings) { } public static Document of(Number value, JsonSettings settings) { - return new NumberDocument(value, settings, DocumentUtils.getSchemaForNumber(value)); + return switch (value) { + case Byte b -> new ByteDocument(b, settings); + case Short s -> new ShortDocument(s, settings); + case Integer i -> new IntegerDocument(i, settings); + case Long l -> new LongDocument(l, settings); + case Float f -> new FloatDocument(f, settings); + case Double d -> new DoubleDocument(d, settings); + case BigInteger bi -> new NumberDocument(bi, settings, PreludeSchemas.BIG_INTEGER); + case BigDecimal bd -> new NumberDocument(bd, settings, PreludeSchemas.BIG_DECIMAL); + default -> throw new IllegalArgumentException("Unsupported Number: %s".formatted(value.getClass())); + }; } public static Document of(List values, JsonSettings settings) { @@ -140,12 +150,12 @@ public double asDouble() { @Override public BigInteger asBigInteger() { - return value instanceof BigInteger ? (BigInteger) value : BigInteger.valueOf(value.longValue()); + return value instanceof BigInteger bi ? bi : BigInteger.valueOf(value.longValue()); } @Override public BigDecimal asBigDecimal() { - return value instanceof BigDecimal ? (BigDecimal) value : BigDecimal.valueOf(value.doubleValue()); + return value instanceof BigDecimal bd ? bd : BigDecimal.valueOf(value.doubleValue()); } @Override @@ -170,6 +180,409 @@ public boolean equals(Object obj) { } } + // Primitive numeric document types - no auto-boxing + record ByteDocument(byte value, JsonSettings settings) implements Document { + @Override + public ShapeType type() { + return ShapeType.BYTE; + } + + @Override + public byte asByte() { + return value; + } + + @Override + public short asShort() { + return value; + } + + @Override + public int asInteger() { + return value; + } + + @Override + public long asLong() { + return value; + } + + @Override + public float asFloat() { + return value; + } + + @Override + public double asDouble() { + return value; + } + + @Override + public BigInteger asBigInteger() { + return BigInteger.valueOf(value); + } + + @Override + public BigDecimal asBigDecimal() { + return BigDecimal.valueOf((double) value); + } + + @Override + public Instant asTimestamp() { + return TimestampResolver.readTimestamp(value, settings.timestampResolver().defaultFormat()); + } + + @Override + public ShapeDeserializer createDeserializer() { + return new JsonDocumentDeserializer(settings, this); + } + + @Override + public void serializeContents(ShapeSerializer serializer) { + serializer.writeByte(PreludeSchemas.BYTE, value); + } + + @Override + public boolean equals(Object obj) { + return Document.equals(this, obj); + } + } + + record ShortDocument(short value, JsonSettings settings) implements Document { + @Override + public ShapeType type() { + return ShapeType.SHORT; + } + + @Override + public byte asByte() { + return (byte) value; + } + + @Override + public short asShort() { + return value; + } + + @Override + public int asInteger() { + return value; + } + + @Override + public long asLong() { + return value; + } + + @Override + public float asFloat() { + return value; + } + + @Override + public double asDouble() { + return value; + } + + @Override + public BigInteger asBigInteger() { + return BigInteger.valueOf(value); + } + + @Override + public BigDecimal asBigDecimal() { + return BigDecimal.valueOf((double) value); + } + + @Override + public Instant asTimestamp() { + return TimestampResolver.readTimestamp(value, settings.timestampResolver().defaultFormat()); + } + + @Override + public ShapeDeserializer createDeserializer() { + return new JsonDocumentDeserializer(settings, this); + } + + @Override + public void serializeContents(ShapeSerializer serializer) { + serializer.writeShort(PreludeSchemas.SHORT, value); + } + + @Override + public boolean equals(Object obj) { + return Document.equals(this, obj); + } + } + + record IntegerDocument(int value, JsonSettings settings) implements Document { + @Override + public ShapeType type() { + return ShapeType.INTEGER; + } + + @Override + public byte asByte() { + return (byte) value; + } + + @Override + public short asShort() { + return (short) value; + } + + @Override + public int asInteger() { + return value; + } + + @Override + public long asLong() { + return value; + } + + @Override + public float asFloat() { + return value; + } + + @Override + public double asDouble() { + return value; + } + + @Override + public BigInteger asBigInteger() { + return BigInteger.valueOf(value); + } + + @Override + public BigDecimal asBigDecimal() { + return BigDecimal.valueOf((double) value); + } + + @Override + public Instant asTimestamp() { + return TimestampResolver.readTimestamp(value, settings.timestampResolver().defaultFormat()); + } + + @Override + public ShapeDeserializer createDeserializer() { + return new JsonDocumentDeserializer(settings, this); + } + + @Override + public void serializeContents(ShapeSerializer serializer) { + serializer.writeInteger(PreludeSchemas.INTEGER, value); + } + + @Override + public boolean equals(Object obj) { + return Document.equals(this, obj); + } + } + + record LongDocument(long value, JsonSettings settings) implements Document { + @Override + public ShapeType type() { + return ShapeType.LONG; + } + + @Override + public byte asByte() { + return (byte) value; + } + + @Override + public short asShort() { + return (short) value; + } + + @Override + public int asInteger() { + return (int) value; + } + + @Override + public long asLong() { + return value; + } + + @Override + public float asFloat() { + return value; + } + + @Override + public double asDouble() { + return value; + } + + @Override + public BigInteger asBigInteger() { + return BigInteger.valueOf(value); + } + + @Override + public BigDecimal asBigDecimal() { + return BigDecimal.valueOf((double) value); + } + + @Override + public Instant asTimestamp() { + return TimestampResolver.readTimestamp(value, settings.timestampResolver().defaultFormat()); + } + + @Override + public ShapeDeserializer createDeserializer() { + return new JsonDocumentDeserializer(settings, this); + } + + @Override + public void serializeContents(ShapeSerializer serializer) { + serializer.writeLong(PreludeSchemas.LONG, value); + } + + @Override + public boolean equals(Object obj) { + return Document.equals(this, obj); + } + } + + record FloatDocument(float value, JsonSettings settings) implements Document { + @Override + public ShapeType type() { + return ShapeType.FLOAT; + } + + @Override + public byte asByte() { + return (byte) value; + } + + @Override + public short asShort() { + return (short) value; + } + + @Override + public int asInteger() { + return (int) value; + } + + @Override + public long asLong() { + return (long) value; + } + + @Override + public float asFloat() { + return value; + } + + @Override + public double asDouble() { + return value; + } + + @Override + public BigInteger asBigInteger() { + return BigInteger.valueOf((long) value); + } + + @Override + public BigDecimal asBigDecimal() { + return BigDecimal.valueOf(value); + } + + @Override + public Instant asTimestamp() { + return TimestampResolver.readTimestamp(value, settings.timestampResolver().defaultFormat()); + } + + @Override + public ShapeDeserializer createDeserializer() { + return new JsonDocumentDeserializer(settings, this); + } + + @Override + public void serializeContents(ShapeSerializer serializer) { + serializer.writeFloat(PreludeSchemas.FLOAT, value); + } + + @Override + public boolean equals(Object obj) { + return Document.equals(this, obj); + } + } + + record DoubleDocument(double value, JsonSettings settings) implements Document { + @Override + public ShapeType type() { + return ShapeType.DOUBLE; + } + + @Override + public byte asByte() { + return (byte) value; + } + + @Override + public short asShort() { + return (short) value; + } + + @Override + public int asInteger() { + return (int) value; + } + + @Override + public long asLong() { + return (long) value; + } + + @Override + public float asFloat() { + return (float) value; + } + + @Override + public double asDouble() { + return value; + } + + @Override + public BigInteger asBigInteger() { + return BigInteger.valueOf((long) value); + } + + @Override + public BigDecimal asBigDecimal() { + return BigDecimal.valueOf(value); + } + + @Override + public Instant asTimestamp() { + return TimestampResolver.readTimestamp(value, settings.timestampResolver().defaultFormat()); + } + + @Override + public ShapeDeserializer createDeserializer() { + return new JsonDocumentDeserializer(settings, this); + } + + @Override + public void serializeContents(ShapeSerializer serializer) { + serializer.writeDouble(PreludeSchemas.DOUBLE, value); + } + + @Override + public boolean equals(Object obj) { + return Document.equals(this, obj); + } + } + record BooleanDocument(boolean value, JsonSettings settings) implements Document { @Override public ShapeType type() { diff --git a/core/src/main/java/software/amazon/smithy/java/core/serde/document/Document.java b/core/src/main/java/software/amazon/smithy/java/core/serde/document/Document.java index 2f4dd45f0..69aaa7ad2 100644 --- a/core/src/main/java/software/amazon/smithy/java/core/serde/document/Document.java +++ b/core/src/main/java/software/amazon/smithy/java/core/serde/document/Document.java @@ -461,7 +461,17 @@ default T asShape(ShapeBuilder builder) { * @return the Document type. */ static Document ofNumber(Number value) { - return new Documents.NumberDocument(DocumentUtils.getSchemaForNumber(value), value); + return switch (value) { + case Byte b -> new Documents.ByteDocument(PreludeSchemas.BYTE, b); + case Short s -> new Documents.ShortDocument(PreludeSchemas.SHORT, s); + case Integer i -> new Documents.IntegerDocument(PreludeSchemas.INTEGER, i); + case Long l -> new Documents.LongDocument(PreludeSchemas.LONG, l); + case Float f -> new Documents.FloatDocument(PreludeSchemas.FLOAT, f); + case Double d -> new Documents.DoubleDocument(PreludeSchemas.DOUBLE, d); + case BigInteger bi -> new Documents.NumberDocument(PreludeSchemas.BIG_INTEGER, bi); + case BigDecimal bd -> new Documents.NumberDocument(PreludeSchemas.BIG_DECIMAL, bd); + default -> throw new IllegalArgumentException("Unsupported Number: %s".formatted(value.getClass())); + }; } /** @@ -471,7 +481,7 @@ static Document ofNumber(Number value) { * @return the Document type. */ static Document of(byte value) { - return new Documents.NumberDocument(PreludeSchemas.BYTE, value); + return new Documents.ByteDocument(PreludeSchemas.BYTE, value); } /** @@ -481,7 +491,7 @@ static Document of(byte value) { * @return the Document type. */ static Document of(short value) { - return new Documents.NumberDocument(PreludeSchemas.SHORT, value); + return new Documents.ShortDocument(PreludeSchemas.SHORT, value); } /** @@ -491,7 +501,7 @@ static Document of(short value) { * @return the Document type. */ static Document of(int value) { - return new Documents.NumberDocument(PreludeSchemas.INTEGER, value); + return new Documents.IntegerDocument(PreludeSchemas.INTEGER, value); } /** @@ -501,7 +511,7 @@ static Document of(int value) { * @return the Document type. */ static Document of(long value) { - return new Documents.NumberDocument(PreludeSchemas.LONG, value); + return new Documents.LongDocument(PreludeSchemas.LONG, value); } /** @@ -511,7 +521,7 @@ static Document of(long value) { * @return the Document type. */ static Document of(float value) { - return new Documents.NumberDocument(PreludeSchemas.FLOAT, value); + return new Documents.FloatDocument(PreludeSchemas.FLOAT, value); } /** @@ -521,7 +531,7 @@ static Document of(float value) { * @return the Document type. */ static Document of(double value) { - return new Documents.NumberDocument(PreludeSchemas.DOUBLE, value); + return new Documents.DoubleDocument(PreludeSchemas.DOUBLE, value); } /** diff --git a/core/src/main/java/software/amazon/smithy/java/core/serde/document/DocumentParser.java b/core/src/main/java/software/amazon/smithy/java/core/serde/document/DocumentParser.java index d1832063f..a34da8838 100644 --- a/core/src/main/java/software/amazon/smithy/java/core/serde/document/DocumentParser.java +++ b/core/src/main/java/software/amazon/smithy/java/core/serde/document/DocumentParser.java @@ -159,32 +159,32 @@ public void writeBoolean(Schema schema, boolean value) { @Override public void writeByte(Schema schema, byte value) { - setResult(new Documents.NumberDocument(schema, value)); + setResult(new Documents.ByteDocument(schema, value)); } @Override public void writeShort(Schema schema, short value) { - setResult(new Documents.NumberDocument(schema, value)); + setResult(new Documents.ShortDocument(schema, value)); } @Override public void writeInteger(Schema schema, int value) { - setResult(new Documents.NumberDocument(schema, value)); + setResult(new Documents.IntegerDocument(schema, value)); } @Override public void writeLong(Schema schema, long value) { - setResult(new Documents.NumberDocument(schema, value)); + setResult(new Documents.LongDocument(schema, value)); } @Override public void writeFloat(Schema schema, float value) { - setResult(new Documents.NumberDocument(schema, value)); + setResult(new Documents.FloatDocument(schema, value)); } @Override public void writeDouble(Schema schema, double value) { - setResult(new Documents.NumberDocument(schema, value)); + setResult(new Documents.DoubleDocument(schema, value)); } @Override diff --git a/core/src/main/java/software/amazon/smithy/java/core/serde/document/DocumentUtils.java b/core/src/main/java/software/amazon/smithy/java/core/serde/document/DocumentUtils.java index 7e8730f19..1cf562c4b 100644 --- a/core/src/main/java/software/amazon/smithy/java/core/serde/document/DocumentUtils.java +++ b/core/src/main/java/software/amazon/smithy/java/core/serde/document/DocumentUtils.java @@ -7,10 +7,6 @@ import java.math.BigDecimal; import java.math.BigInteger; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; -import software.amazon.smithy.java.core.schema.PreludeSchemas; import software.amazon.smithy.java.core.schema.Schema; import software.amazon.smithy.java.core.schema.SchemaUtils; import software.amazon.smithy.java.core.schema.SerializableStruct; @@ -22,28 +18,6 @@ public final class DocumentUtils { private DocumentUtils() {} - private static final Map, Schema> NUMBER_MAPPING = Map.of( - AtomicLong.class, - PreludeSchemas.LONG, - AtomicInteger.class, - PreludeSchemas.INTEGER, - Byte.class, - PreludeSchemas.BYTE, - Short.class, - PreludeSchemas.SHORT, - Integer.class, - PreludeSchemas.INTEGER, - Long.class, - PreludeSchemas.LONG, - Float.class, - PreludeSchemas.FLOAT, - Double.class, - PreludeSchemas.DOUBLE, - BigInteger.class, - PreludeSchemas.BIG_INTEGER, - BigDecimal.class, - PreludeSchemas.BIG_DECIMAL); - public static void serializeNumber(ShapeSerializer serializer, Schema schema, Number value) { switch (schema.type()) { case BYTE -> serializer.writeByte(schema, value.byteValue()); @@ -82,24 +56,6 @@ private static BigInteger toBigInteger(Number number) { } } - public static Schema getSchemaForNumber(Number value) { - var result = NUMBER_MAPPING.get(value.getClass()); - // Note that BigInteger and BigDecimal can be extended. - if (result != null) { - return result; - } else if (value instanceof BigInteger) { - return PreludeSchemas.BIG_INTEGER; - } else if (value instanceof BigDecimal) { - return PreludeSchemas.BIG_DECIMAL; - } else { - throw new IllegalArgumentException( - String.format( - "Unsupported Number: %s; expected one of %s", - value.getClass(), - NUMBER_MAPPING.keySet())); - } - } - static boolean numberEquals(Number a, Document rightDocument) { try { return compareWithPromotion(a, rightDocument) == 0; diff --git a/core/src/main/java/software/amazon/smithy/java/core/serde/document/Documents.java b/core/src/main/java/software/amazon/smithy/java/core/serde/document/Documents.java index 0e39a5a26..d19c64221 100644 --- a/core/src/main/java/software/amazon/smithy/java/core/serde/document/Documents.java +++ b/core/src/main/java/software/amazon/smithy/java/core/serde/document/Documents.java @@ -49,6 +49,7 @@ public void serializeContents(ShapeSerializer serializer) { } } + // NumberDocument is kept for BigInteger and BigDecimal (already objects) record NumberDocument(Schema schema, Number value) implements Document { @Override public ShapeType type() { @@ -87,12 +88,12 @@ public double asDouble() { @Override public BigInteger asBigInteger() { - return value instanceof BigInteger ? (BigInteger) value : BigInteger.valueOf(value.longValue()); + return value instanceof BigInteger bi ? bi : BigInteger.valueOf(value.longValue()); } @Override public BigDecimal asBigDecimal() { - return value instanceof BigDecimal ? (BigDecimal) value : BigDecimal.valueOf(value.doubleValue()); + return value instanceof BigDecimal bd ? bd : BigDecimal.valueOf(value.doubleValue()); } @Override @@ -101,6 +102,349 @@ public void serializeContents(ShapeSerializer serializer) { } } + // Primitive numeric document types - no auto-boxing + record ByteDocument(Schema schema, byte value) implements Document { + @Override + public ShapeType type() { + return ShapeType.BYTE; + } + + @Override + public byte asByte() { + return value; + } + + @Override + public short asShort() { + return value; + } + + @Override + public int asInteger() { + return value; + } + + @Override + public long asLong() { + return value; + } + + @Override + public float asFloat() { + return value; + } + + @Override + public double asDouble() { + return value; + } + + @Override + public BigInteger asBigInteger() { + return BigInteger.valueOf(value); + } + + @Override + public BigDecimal asBigDecimal() { + return BigDecimal.valueOf((double) value); + } + + @Override + public void serializeContents(ShapeSerializer serializer) { + serializer.writeByte(schema, value); + } + + @Override + public boolean equals(Object obj) { + return Document.equals(this, obj); + } + } + + record ShortDocument(Schema schema, short value) implements Document { + @Override + public ShapeType type() { + return ShapeType.SHORT; + } + + @Override + public byte asByte() { + return (byte) value; + } + + @Override + public short asShort() { + return value; + } + + @Override + public int asInteger() { + return value; + } + + @Override + public long asLong() { + return value; + } + + @Override + public float asFloat() { + return value; + } + + @Override + public double asDouble() { + return value; + } + + @Override + public BigInteger asBigInteger() { + return BigInteger.valueOf(value); + } + + @Override + public BigDecimal asBigDecimal() { + return BigDecimal.valueOf((double) value); + } + + @Override + public void serializeContents(ShapeSerializer serializer) { + serializer.writeShort(schema, value); + } + + @Override + public boolean equals(Object obj) { + return Document.equals(this, obj); + } + } + + record IntegerDocument(Schema schema, int value) implements Document { + @Override + public ShapeType type() { + return ShapeType.INTEGER; + } + + @Override + public byte asByte() { + return (byte) value; + } + + @Override + public short asShort() { + return (short) value; + } + + @Override + public int asInteger() { + return value; + } + + @Override + public long asLong() { + return value; + } + + @Override + public float asFloat() { + return value; + } + + @Override + public double asDouble() { + return value; + } + + @Override + public BigInteger asBigInteger() { + return BigInteger.valueOf(value); + } + + @Override + public BigDecimal asBigDecimal() { + return BigDecimal.valueOf((double) value); + } + + @Override + public void serializeContents(ShapeSerializer serializer) { + serializer.writeInteger(schema, value); + } + + @Override + public boolean equals(Object obj) { + return Document.equals(this, obj); + } + } + + record LongDocument(Schema schema, long value) implements Document { + @Override + public ShapeType type() { + return ShapeType.LONG; + } + + @Override + public byte asByte() { + return (byte) value; + } + + @Override + public short asShort() { + return (short) value; + } + + @Override + public int asInteger() { + return (int) value; + } + + @Override + public long asLong() { + return value; + } + + @Override + public float asFloat() { + return value; + } + + @Override + public double asDouble() { + return value; + } + + @Override + public BigInteger asBigInteger() { + return BigInteger.valueOf(value); + } + + @Override + public BigDecimal asBigDecimal() { + return BigDecimal.valueOf((double) value); + } + + @Override + public void serializeContents(ShapeSerializer serializer) { + serializer.writeLong(schema, value); + } + + @Override + public boolean equals(Object obj) { + return Document.equals(this, obj); + } + } + + record FloatDocument(Schema schema, float value) implements Document { + @Override + public ShapeType type() { + return ShapeType.FLOAT; + } + + @Override + public byte asByte() { + return (byte) value; + } + + @Override + public short asShort() { + return (short) value; + } + + @Override + public int asInteger() { + return (int) value; + } + + @Override + public long asLong() { + return (long) value; + } + + @Override + public float asFloat() { + return value; + } + + @Override + public double asDouble() { + return value; + } + + @Override + public BigInteger asBigInteger() { + return BigInteger.valueOf((long) value); + } + + @Override + public BigDecimal asBigDecimal() { + return BigDecimal.valueOf(value); + } + + @Override + public void serializeContents(ShapeSerializer serializer) { + serializer.writeFloat(schema, value); + } + + @Override + public boolean equals(Object obj) { + return Document.equals(this, obj); + } + } + + record DoubleDocument(Schema schema, double value) implements Document { + @Override + public ShapeType type() { + return ShapeType.DOUBLE; + } + + @Override + public byte asByte() { + return (byte) value; + } + + @Override + public short asShort() { + return (short) value; + } + + @Override + public int asInteger() { + return (int) value; + } + + @Override + public long asLong() { + return (long) value; + } + + @Override + public float asFloat() { + return (float) value; + } + + @Override + public double asDouble() { + return value; + } + + @Override + public BigInteger asBigInteger() { + return BigInteger.valueOf((long) value); + } + + @Override + public BigDecimal asBigDecimal() { + return BigDecimal.valueOf(value); + } + + @Override + public void serializeContents(ShapeSerializer serializer) { + serializer.writeDouble(schema, value); + } + + @Override + public boolean equals(Object obj) { + return Document.equals(this, obj); + } + } + record StringDocument(Schema schema, String value) implements Document { @Override public ShapeType type() { @@ -350,8 +694,8 @@ public boolean equals(Object obj) { return true; } else if (obj == null) { return false; - } else if (obj instanceof LazyStructure) { - return getDocument().equals(((LazyStructure) obj).getDocument()); + } else if (obj instanceof LazyStructure ls) { + return getDocument().equals(ls.getDocument()); } else if (obj instanceof Document) { return getDocument().equals(obj); } else { diff --git a/core/src/test/java/software/amazon/smithy/java/core/serde/document/DocumentTest.java b/core/src/test/java/software/amazon/smithy/java/core/serde/document/DocumentTest.java index c498be199..1753ef2db 100644 --- a/core/src/test/java/software/amazon/smithy/java/core/serde/document/DocumentTest.java +++ b/core/src/test/java/software/amazon/smithy/java/core/serde/document/DocumentTest.java @@ -42,12 +42,12 @@ public class DocumentTest { @Test public void getAsNumber() { - assertThat(Document.of((byte) 1).asNumber(), equalTo(Byte.valueOf((byte) 1))); - assertThat(Document.of((short) 1).asNumber(), equalTo(Short.valueOf((short) 1))); - assertThat(Document.of(1).asNumber(), equalTo(Integer.valueOf(1))); - assertThat(Document.of(1L).asNumber(), equalTo(Long.valueOf(1L))); - assertThat(Document.of(1f).asNumber(), equalTo(Float.valueOf(1f))); - assertThat(Document.of(1.0).asNumber(), equalTo(Double.valueOf(1.0))); + assertThat(Document.of((byte) 1).asNumber(), equalTo((byte) 1)); + assertThat(Document.of((short) 1).asNumber(), equalTo((short) 1)); + assertThat(Document.of(1).asNumber(), equalTo(1)); + assertThat(Document.of(1L).asNumber(), equalTo(1L)); + assertThat(Document.of(1f).asNumber(), equalTo(1f)); + assertThat(Document.of(1.0).asNumber(), equalTo(1.0)); assertThat(Document.of(new BigDecimal(1)).asNumber(), equalTo(new BigDecimal(1))); assertThat(Document.of(BigInteger.valueOf(1)).asNumber(), equalTo(BigInteger.valueOf(1))); }