Skip to content

Commit d469d9e

Browse files
committed
Merge branch '2.19' into 3.x
2 parents 57f2216 + 7e0c35e commit d469d9e

33 files changed

+2150
-123
lines changed

javax-money/MonetaryAmountSerializerTest.java

Lines changed: 430 additions & 0 deletions
Large diffs are not rendered by default.

javax-money/delombok.tgz

4.71 KB
Binary file not shown.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.fasterxml.jackson.datatype.javax.money;
2+
3+
import org.apiguardian.api.API;
4+
5+
import javax.money.MonetaryAmount;
6+
7+
import static org.apiguardian.api.API.Status.EXPERIMENTAL;
8+
9+
@API(status = EXPERIMENTAL)
10+
public interface AmountWriter<T> {
11+
12+
Class<T> getType();
13+
14+
T write(MonetaryAmount amount);
15+
16+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.fasterxml.jackson.datatype.javax.money;
2+
3+
import org.apiguardian.api.API;
4+
5+
import java.math.BigDecimal;
6+
7+
import static org.apiguardian.api.API.Status.EXPERIMENTAL;
8+
9+
@API(status = EXPERIMENTAL)
10+
public interface BigDecimalAmountWriter extends AmountWriter<BigDecimal> {
11+
12+
@Override
13+
default Class<BigDecimal> getType() {
14+
return BigDecimal.class;
15+
}
16+
17+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.fasterxml.jackson.datatype.javax.money;
2+
3+
import com.fasterxml.jackson.core.JsonParser;
4+
import com.fasterxml.jackson.databind.DeserializationContext;
5+
import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
6+
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
7+
import org.apiguardian.api.API;
8+
9+
import javax.money.CurrencyUnit;
10+
import javax.money.Monetary;
11+
import java.io.IOException;
12+
13+
import static org.apiguardian.api.API.Status.MAINTAINED;
14+
15+
@API(status = MAINTAINED)
16+
public final class CurrencyUnitDeserializer extends StdScalarDeserializer<CurrencyUnit>
17+
{
18+
private static final long serialVersionUID = 1L;
19+
20+
public CurrencyUnitDeserializer() {
21+
super(CurrencyUnit.class);
22+
}
23+
24+
@Override
25+
public Object deserializeWithType(final JsonParser parser, final DeserializationContext context,
26+
final TypeDeserializer deserializer) throws IOException {
27+
28+
// effectively assuming no type information at all
29+
return deserialize(parser, context);
30+
}
31+
32+
@Override
33+
public CurrencyUnit deserialize(final JsonParser parser, final DeserializationContext context) throws IOException {
34+
final String currencyCode = parser.getValueAsString();
35+
return Monetary.getCurrency(currencyCode);
36+
}
37+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.fasterxml.jackson.datatype.javax.money;
2+
3+
import com.fasterxml.jackson.core.JsonGenerator;
4+
import com.fasterxml.jackson.databind.JavaType;
5+
import com.fasterxml.jackson.databind.JsonMappingException;
6+
import com.fasterxml.jackson.databind.SerializerProvider;
7+
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
8+
import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;
9+
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
10+
import org.apiguardian.api.API;
11+
12+
import javax.money.CurrencyUnit;
13+
import java.io.IOException;
14+
15+
import static org.apiguardian.api.API.Status.MAINTAINED;
16+
17+
@API(status = MAINTAINED)
18+
public final class CurrencyUnitSerializer extends StdScalarSerializer<CurrencyUnit> {
19+
20+
CurrencyUnitSerializer() {
21+
super(CurrencyUnit.class);
22+
}
23+
24+
@Override
25+
public void serialize(final CurrencyUnit value, final JsonGenerator generator, final SerializerProvider serializers)
26+
throws IOException {
27+
generator.writeString(value.getCurrencyCode());
28+
}
29+
30+
@Override
31+
public void acceptJsonFormatVisitor(final JsonFormatVisitorWrapper visitor, final JavaType hint)
32+
throws JsonMappingException {
33+
visitor.expectStringFormat(hint);
34+
}
35+
36+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.fasterxml.jackson.datatype.javax.money;
2+
3+
import javax.annotation.Nonnull;
4+
import javax.money.MonetaryAmount;
5+
import java.math.BigDecimal;
6+
import java.math.RoundingMode;
7+
8+
final class DecimalAmountWriter implements BigDecimalAmountWriter {
9+
10+
@Override
11+
public BigDecimal write(@Nonnull final MonetaryAmount amount) {
12+
final BigDecimal decimal = amount.getNumber().numberValueExact(BigDecimal.class);
13+
final int defaultFractionDigits = amount.getCurrency().getDefaultFractionDigits();
14+
final int scale = Math.max(decimal.scale(), defaultFractionDigits);
15+
16+
return decimal.setScale(scale, RoundingMode.UNNECESSARY);
17+
}
18+
19+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.fasterxml.jackson.datatype.javax.money;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Getter;
5+
import lombok.With;
6+
7+
@AllArgsConstructor(staticName = "valueOf")
8+
@Getter
9+
public final class FieldNames
10+
{
11+
static final FieldNames DEFAULT = FieldNames.valueOf("amount", "currency", "formatted");
12+
13+
@With
14+
private final String amount;
15+
16+
@With
17+
private final String currency;
18+
19+
@With
20+
private final String formatted;
21+
22+
public static FieldNames defaults() {
23+
return DEFAULT;
24+
}
25+
26+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package com.fasterxml.jackson.datatype.javax.money;
2+
3+
import javax.money.CurrencyUnit;
4+
import javax.money.Monetary;
5+
import javax.money.MonetaryAmount;
6+
import javax.money.format.MonetaryFormats;
7+
8+
import org.apiguardian.api.API;
9+
10+
import com.fasterxml.jackson.core.Version;
11+
import com.fasterxml.jackson.databind.Module;
12+
import com.fasterxml.jackson.databind.module.SimpleDeserializers;
13+
import com.fasterxml.jackson.databind.module.SimpleSerializers;
14+
15+
import static org.apiguardian.api.API.Status.EXPERIMENTAL;
16+
import static org.apiguardian.api.API.Status.STABLE;
17+
18+
@API(status = STABLE)
19+
public final class JavaxMoneyModule extends Module
20+
{
21+
private final AmountWriter<?> writer;
22+
private final FieldNames names;
23+
private final MonetaryAmountFormatFactory formatFactory;
24+
private final MonetaryAmountFactory<? extends MonetaryAmount> amountFactory;
25+
26+
public JavaxMoneyModule() {
27+
//When No AmountFactory is provided, use the default MonetaryAmountFactory from Monetary
28+
this(new DecimalAmountWriter(), FieldNames.defaults(), MonetaryAmountFormatFactory.NONE, (amount, currency) -> Monetary.getDefaultAmountFactory().setNumber(amount).setCurrency(currency).create());
29+
}
30+
31+
private <T extends MonetaryAmount> JavaxMoneyModule(final AmountWriter<?> writer, final FieldNames names, final MonetaryAmountFormatFactory formatFactory, final MonetaryAmountFactory<T> amountFactory) {
32+
33+
this.writer = writer;
34+
this.names = names;
35+
this.formatFactory = formatFactory;
36+
this.amountFactory = amountFactory;
37+
}
38+
39+
@Override
40+
public String getModuleName() {
41+
return JavaxMoneyModule.class.getSimpleName();
42+
}
43+
44+
@Override
45+
public Version version() {
46+
return PackageVersion.VERSION;
47+
}
48+
49+
@Override
50+
public void setupModule(final SetupContext context) {
51+
final SimpleSerializers serializers = new SimpleSerializers();
52+
serializers.addSerializer(CurrencyUnit.class, new CurrencyUnitSerializer());
53+
serializers.addSerializer(MonetaryAmount.class, new MonetaryAmountSerializer(names, writer, formatFactory));
54+
context.addSerializers(serializers);
55+
56+
final SimpleDeserializers deserializers = new SimpleDeserializers();
57+
deserializers.addDeserializer(CurrencyUnit.class, new CurrencyUnitDeserializer());
58+
59+
//Use provided amountFactory to deserialize a MonetaryAmount
60+
deserializers.addDeserializer(MonetaryAmount.class, new MonetaryAmountDeserializer<>(amountFactory, names));
61+
62+
context.addDeserializers(deserializers);
63+
}
64+
65+
public JavaxMoneyModule withDecimalNumbers() {
66+
return withNumbers(new DecimalAmountWriter());
67+
}
68+
69+
public JavaxMoneyModule withQuotedDecimalNumbers() {
70+
return withNumbers(new QuotedDecimalAmountWriter());
71+
}
72+
73+
@API(status = EXPERIMENTAL)
74+
public JavaxMoneyModule withNumbers(final AmountWriter<?> writer) {
75+
return new JavaxMoneyModule(writer, names, formatFactory, amountFactory);
76+
}
77+
78+
public <T extends MonetaryAmount> JavaxMoneyModule withMonetaryAmountFactory(final MonetaryAmountFactory<T> amountFactory) {
79+
return new JavaxMoneyModule(writer, names, formatFactory, amountFactory);
80+
}
81+
82+
public JavaxMoneyModule withoutFormatting() {
83+
return withFormatting(MonetaryAmountFormatFactory.NONE);
84+
}
85+
86+
public JavaxMoneyModule withDefaultFormatting() {
87+
return withFormatting(MonetaryFormats::getAmountFormat);
88+
}
89+
90+
public JavaxMoneyModule withFormatting(final MonetaryAmountFormatFactory formatFactory) {
91+
return new JavaxMoneyModule(writer, names, formatFactory, amountFactory);
92+
}
93+
94+
public JavaxMoneyModule withAmountFieldName(final String name) {
95+
return withFieldNames(names.withAmount(name));
96+
}
97+
98+
public JavaxMoneyModule withCurrencyFieldName(final String name) {
99+
return withFieldNames(names.withCurrency(name));
100+
}
101+
102+
public JavaxMoneyModule withFormattedFieldName(final String name) {
103+
return withFieldNames(names.withFormatted(name));
104+
}
105+
106+
private JavaxMoneyModule withFieldNames(final FieldNames names) {
107+
return new JavaxMoneyModule(writer, names, formatFactory, amountFactory);
108+
}
109+
110+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package com.fasterxml.jackson.datatype.javax.money;
2+
3+
import com.fasterxml.jackson.core.JsonParser;
4+
import com.fasterxml.jackson.core.JsonToken;
5+
import com.fasterxml.jackson.databind.DeserializationContext;
6+
import com.fasterxml.jackson.databind.JsonDeserializer;
7+
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
8+
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
9+
10+
import javax.money.CurrencyUnit;
11+
import javax.money.MonetaryAmount;
12+
import java.io.IOException;
13+
import java.math.BigDecimal;
14+
import java.util.Arrays;
15+
import java.util.Objects;
16+
17+
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
18+
import static java.lang.String.format;
19+
20+
public final class MonetaryAmountDeserializer<M extends MonetaryAmount> extends JsonDeserializer<M> {
21+
22+
private final MonetaryAmountFactory<M> factory;
23+
private final FieldNames names;
24+
25+
public MonetaryAmountDeserializer(final MonetaryAmountFactory<M> factory, final FieldNames names) {
26+
this.factory = factory;
27+
this.names = names;
28+
}
29+
30+
@Override
31+
public Object deserializeWithType(final JsonParser parser, final DeserializationContext context,
32+
final TypeDeserializer deserializer) throws IOException {
33+
34+
// effectively assuming no type information at all
35+
return deserialize(parser, context);
36+
}
37+
38+
@Override
39+
public M deserialize(final JsonParser parser, final DeserializationContext context) throws IOException {
40+
BigDecimal amount = null;
41+
CurrencyUnit currency = null;
42+
43+
while (parser.nextToken() != JsonToken.END_OBJECT) {
44+
final String field = parser.currentName();
45+
46+
parser.nextToken();
47+
48+
if (field.equals(names.getAmount())) {
49+
amount = context.readValue(parser, BigDecimal.class);
50+
} else if (field.equals(names.getCurrency())) {
51+
currency = context.readValue(parser, CurrencyUnit.class);
52+
} else if (field.equals(names.getFormatted())) {
53+
//noinspection UnnecessaryContinue
54+
continue;
55+
} else if (context.isEnabled(FAIL_ON_UNKNOWN_PROPERTIES)) {
56+
throw UnrecognizedPropertyException.from(parser, MonetaryAmount.class, field,
57+
Arrays.asList(names.getAmount(), names.getCurrency(), names.getFormatted()));
58+
} else {
59+
parser.skipChildren();
60+
}
61+
}
62+
63+
String missingName;
64+
65+
if (Objects.isNull(currency)) {
66+
missingName = names.getCurrency();
67+
} else if (Objects.isNull(amount)) {
68+
missingName = names.getAmount();
69+
} else {
70+
return factory.create(amount, currency);
71+
}
72+
73+
return context.reportPropertyInputMismatch(MonetaryAmount.class, missingName, format("Missing property: '%s'", missingName));
74+
}
75+
}

0 commit comments

Comments
 (0)