diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/OtherConverters.java b/src/main/java/org/springframework/data/couchbase/core/convert/OtherConverters.java index 1d2fe590..95b1c277 100644 --- a/src/main/java/org/springframework/data/couchbase/core/convert/OtherConverters.java +++ b/src/main/java/org/springframework/data/couchbase/core/convert/OtherConverters.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 the original author or authors + * Copyright 2021-2022 the original author or authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.springframework.data.couchbase.core.convert; +import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; @@ -47,6 +48,8 @@ public final class OtherConverters { converters.add(StringToUuid.INSTANCE); converters.add(BigIntegerToString.INSTANCE); converters.add(StringToBigInteger.INSTANCE); + converters.add(BigDecimalToString.INSTANCE); + converters.add(StringToBigDecimal.INSTANCE); return converters; } @@ -90,4 +93,24 @@ public final class OtherConverters { return source == null ? null : new BigInteger(source); } } + + @WritingConverter + public enum BigDecimalToString implements Converter { + INSTANCE; + + @Override + public String convert(BigDecimal source) { + return source == null ? null : source.toPlainString(); + } + } + + @ReadingConverter + public enum StringToBigDecimal implements Converter { + INSTANCE; + + @Override + public BigDecimal convert(String source) { + return source == null ? null : new BigDecimal(source); + } + } } diff --git a/src/test/java/org/springframework/data/couchbase/core/mapping/MappingCouchbaseConverterTests.java b/src/test/java/org/springframework/data/couchbase/core/mapping/MappingCouchbaseConverterTests.java index d807c915..5af38dbe 100644 --- a/src/test/java/org/springframework/data/couchbase/core/mapping/MappingCouchbaseConverterTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/mapping/MappingCouchbaseConverterTests.java @@ -16,14 +16,32 @@ package org.springframework.data.couchbase.core.mapping; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.data.Offset.offset; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertThrows; import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.ChoiceFormat; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.UUID; +import java.util.stream.Collectors; import org.junit.jupiter.api.Test; import org.springframework.core.convert.converter.Converter; @@ -35,7 +53,11 @@ import org.springframework.data.convert.WritingConverter; import org.springframework.data.couchbase.core.convert.CouchbaseCustomConversions; import org.springframework.data.couchbase.core.convert.CouchbaseJsr310Converters.LocalDateTimeToLongConverter; import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter; -import org.springframework.data.couchbase.core.mapping.id.*; +import org.springframework.data.couchbase.core.mapping.id.GeneratedValue; +import org.springframework.data.couchbase.core.mapping.id.GenerationStrategy; +import org.springframework.data.couchbase.core.mapping.id.IdAttribute; +import org.springframework.data.couchbase.core.mapping.id.IdPrefix; +import org.springframework.data.couchbase.core.mapping.id.IdSuffix; import org.springframework.data.couchbase.domain.Address; import org.springframework.data.couchbase.domain.Config; import org.springframework.data.couchbase.domain.Person; @@ -70,8 +92,9 @@ public class MappingCouchbaseConverterTests { void doesNotAllowSimpleType1() { try { converter.write("hello", new CouchbaseDocument()); - } catch(Exception e){ - if(!(e instanceof MappingException) && !e.getClass().getName().equals("java.lang.reflect.InaccessibleObjectException")){ + } catch (Exception e) { + if (!(e instanceof MappingException) + && !e.getClass().getName().equals("java.lang.reflect.InaccessibleObjectException")) { throw new RuntimeException("Should have thrown MappingException or InaccessibleObjectException", e); } } @@ -81,8 +104,9 @@ public class MappingCouchbaseConverterTests { void doesNotAllowSimpleType2() { try { converter.write(true, new CouchbaseDocument()); - } catch(Exception e){ - if(!(e instanceof MappingException) && !e.getClass().getName().equals("java.lang.reflect.InaccessibleObjectException")){ + } catch (Exception e) { + if (!(e instanceof MappingException) + && !e.getClass().getName().equals("java.lang.reflect.InaccessibleObjectException")) { throw new RuntimeException("Should have thrown MappingException or InaccessibleObjectException", e); } } @@ -92,8 +116,9 @@ public class MappingCouchbaseConverterTests { void doesNotAllowSimpleType3() { try { converter.write(42, new CouchbaseDocument()); - } catch(Exception e){ - if(!(e instanceof MappingException) && !e.getClass().getName().equals("java.lang.reflect.InaccessibleObjectException")){ + } catch (Exception e) { + if (!(e instanceof MappingException) + && !e.getClass().getName().equals("java.lang.reflect.InaccessibleObjectException")) { throw new RuntimeException("Should have thrown MappingException or InaccessibleObjectException", e); } } @@ -112,7 +137,7 @@ public class MappingCouchbaseConverterTests { converter.write(entity, converted); Map result = converted.export(); assertThat(result.get("_class")).isEqualTo(entity.getClass().getName()); - assertThat(result.get("attr0")).isEqualTo("foobar"); + assertThat(result.get("attr0")).isEqualTo(entity.attr0); assertThat(converted.getId()).isEqualTo(BaseEntity.ID); } @@ -123,7 +148,51 @@ public class MappingCouchbaseConverterTests { source.put("attr0", "foobar"); StringEntity converted = converter.read(StringEntity.class, source); - assertThat(converted.attr0).isEqualTo("foobar"); + assertThat(converted.attr0).isEqualTo(source.get("attr0")); + } + + @Test + void writesBigInteger() { + CouchbaseDocument converted = new CouchbaseDocument(); + BigIntegerEntity entity = new BigIntegerEntity(new BigInteger("12345")); + + converter.write(entity, converted); + Map result = converted.export(); + assertThat(result.get("_class")).isEqualTo(entity.getClass().getName()); + assertThat(result.get("attr0")).isEqualTo(entity.attr0.toString()); + assertThat(converted.getId()).isEqualTo(BaseEntity.ID); + } + + @Test + void readsBigInteger() { + CouchbaseDocument source = new CouchbaseDocument(); + source.put("_class", BigIntegerEntity.class.getName()); + source.put("attr0", "12345"); + + BigIntegerEntity converted = converter.read(BigIntegerEntity.class, source); + assertThat(converted.attr0).isEqualTo(new BigInteger((String) source.get("attr0"))); + } + + @Test + void writesBigDecimal() { + CouchbaseDocument converted = new CouchbaseDocument(); + BigDecimalEntity entity = new BigDecimalEntity(new BigDecimal("123.45")); + + converter.write(entity, converted); + Map result = converted.export(); + assertThat(result.get("_class")).isEqualTo(entity.getClass().getName()); + assertThat(result.get("attr0")).isEqualTo(entity.attr0.toString()); + assertThat(converted.getId()).isEqualTo(BaseEntity.ID); + } + + @Test + void readsBigDecimal() { + CouchbaseDocument source = new CouchbaseDocument(); + source.put("_class", BigDecimalEntity.class.getName()); + source.put("attr0", "123.45"); + + BigDecimalEntity converted = converter.read(BigDecimalEntity.class, source); + assertThat(converted.attr0).isEqualTo(new BigDecimal((String) source.get("attr0"))); } @Test @@ -438,8 +507,8 @@ public class MappingCouchbaseConverterTests { @Test void writesAndReadsCustomConvertedClass() { List converters = new ArrayList<>(); - converters.add(BigDecimalToStringConverter.INSTANCE); - converters.add(StringToBigDecimalConverter.INSTANCE); + converters.add(ChoiceFormatToStringConverter.INSTANCE); + converters.add(StringToChoiceFormatConverter.INSTANCE); CustomConversions customConversions = new CouchbaseCustomConversions(converters); converter.setCustomConversions(customConversions); converter.afterPropertiesSet(); @@ -448,14 +517,14 @@ public class MappingCouchbaseConverterTests { CouchbaseDocument converted = new CouchbaseDocument(); - final String valueStr = "12.345"; - final BigDecimal value = new BigDecimal(valueStr); - final String value2Str = "0.6789"; - final BigDecimal value2 = new BigDecimal(value2Str); - List listOfValues = new ArrayList<>(); + final String valueStr = "1.0,2.0,3.0,4.0,5.0,6.0,7.0|Sun,Mon,Tue,Wed,Thur,Fri,Sat"; + final ChoiceFormat value = fromString(valueStr); + final String value2Str = "1.0,2.0,3.0|Jan,Feb,Mar"; + final ChoiceFormat value2 = fromString(value2Str); + List listOfValues = new ArrayList<>(); listOfValues.add(value); listOfValues.add(value2); - Map mapOfValues = new TreeMap<>(); + Map mapOfValues = new TreeMap<>(); mapOfValues.put("val1", value); mapOfValues.put("val2", value2); @@ -486,11 +555,25 @@ public class MappingCouchbaseConverterTests { assertThat(readConverted.mapOfValues.get("val2")).isEqualTo(mapOfValues.get("val2")); } + static private String toString(ChoiceFormat choiceFormat) { + String limits = Arrays.stream(choiceFormat.getLimits()).mapToObj(String::valueOf).collect(Collectors.joining(",")); + String formats = Arrays.stream(choiceFormat.getFormats()).map((o) -> String.valueOf(o)) + .collect(Collectors.joining(",")); + return limits + "|" + formats; + } + + static private ChoiceFormat fromString(String source) { + String[] split = source.split("\\|"); + double[] limits = Arrays.stream(split[0].split(",")).mapToDouble(Double::parseDouble).toArray(); + String[] formats = split[1].split(","); + return new ChoiceFormat(limits, formats); + } + @Test void writesAndReadsCustomFieldsConvertedClass() { List converters = new ArrayList<>(); - converters.add(BigDecimalToStringConverter.INSTANCE); - converters.add(StringToBigDecimalConverter.INSTANCE); + converters.add(ChoiceFormatToStringConverter.INSTANCE); + converters.add(StringToChoiceFormatConverter.INSTANCE); CustomConversions customConversions = new CouchbaseCustomConversions(converters); converter.setCustomConversions(customConversions); converter.afterPropertiesSet(); @@ -499,14 +582,14 @@ public class MappingCouchbaseConverterTests { CouchbaseDocument converted = new CouchbaseDocument(); - final String valueStr = "12.345"; - final BigDecimal value = new BigDecimal(valueStr); - final String value2Str = "0.6789"; - final BigDecimal value2 = new BigDecimal(value2Str); - List listOfValues = new ArrayList<>(); + final String valueStr = "1.0,2.0,3.0,4.0,5.0,6.0,7.0|Sun,Mon,Tue,Wed,Thur,Fri,Sat"; + final ChoiceFormat value = fromString(valueStr); + final String value2Str = "1.0,2.0,3.0|Jan,Feb,Mar"; + final ChoiceFormat value2 = fromString(value2Str); + List listOfValues = new ArrayList<>(); listOfValues.add(value); listOfValues.add(value2); - Map mapOfValues = new HashMap<>(); + Map mapOfValues = new TreeMap<>(); mapOfValues.put("val1", value); mapOfValues.put("val2", value2); @@ -515,18 +598,18 @@ public class MappingCouchbaseConverterTests { CouchbaseDocument source = new CouchbaseDocument(); source.put("_class", CustomFieldsEntity.class.getName()); - source.put("decimalValue", valueStr); + source.put("choiceFormatValue", valueStr); CouchbaseList listOfValuesDoc = new CouchbaseList(); listOfValuesDoc.put(valueStr); listOfValuesDoc.put(value2Str); - source.put("listOfDecimalValues", listOfValuesDoc); + source.put("listOfChoiceFormatValues", listOfValuesDoc); CouchbaseDocument mapOfValuesDoc = new CouchbaseDocument(); mapOfValuesDoc.put("val1", valueStr); mapOfValuesDoc.put("val2", value2Str); - source.put("mapOfDecimalValues", mapOfValuesDoc); + source.put("mapOfChoiceFormatValues", mapOfValuesDoc); - assertThat(valueStr).isEqualTo(((CouchbaseList) converted.getContent().get("listOfDecimalValues")).get(0)); - assertThat(value2Str).isEqualTo(((CouchbaseList) converted.getContent().get("listOfDecimalValues")).get(1)); + assertThat(valueStr).isEqualTo(((CouchbaseList) converted.getContent().get("listOfChoiceFormatValues")).get(0)); + assertThat(value2Str).isEqualTo(((CouchbaseList) converted.getContent().get("listOfChoiceFormatValues")).get(1)); assertThat(converted.export().toString()).isEqualTo(source.export().toString()); CustomFieldsEntity readConverted = converter.read(CustomFieldsEntity.class, source); @@ -540,8 +623,8 @@ public class MappingCouchbaseConverterTests { @Test void writesAndReadsClassContainingCustomConvertedObjects() { List converters = new ArrayList<>(); - converters.add(BigDecimalToStringConverter.INSTANCE); - converters.add(StringToBigDecimalConverter.INSTANCE); + converters.add(ChoiceFormatToStringConverter.INSTANCE); + converters.add(StringToChoiceFormatConverter.INSTANCE); CustomConversions customConversions = new CouchbaseCustomConversions(converters); converter.setCustomConversions(customConversions); converter.afterPropertiesSet(); @@ -635,22 +718,22 @@ public class MappingCouchbaseConverterTests { } @WritingConverter - public enum BigDecimalToStringConverter implements Converter { + public enum ChoiceFormatToStringConverter implements Converter { INSTANCE; @Override - public String convert(BigDecimal source) { - return source.toPlainString(); + public String convert(ChoiceFormat source) { + return MappingCouchbaseConverterTests.toString(source); } } @ReadingConverter - public enum StringToBigDecimalConverter implements Converter { + public enum StringToChoiceFormatConverter implements Converter { INSTANCE; @Override - public BigDecimal convert(String source) { - return new BigDecimal(source); + public ChoiceFormat convert(String source) { + return MappingCouchbaseConverterTests.fromString(source); } } @@ -676,6 +759,22 @@ public class MappingCouchbaseConverterTests { } } + static class BigIntegerEntity extends BaseEntity { + private BigInteger attr0; + + public BigIntegerEntity(BigInteger attr0) { + this.attr0 = attr0; + } + } + + static class BigDecimalEntity extends BaseEntity { + private BigDecimal attr0; + + public BigDecimalEntity(BigDecimal attr0) { + this.attr0 = attr0; + } + } + static class NumberEntity extends BaseEntity { private long attr0; @@ -770,11 +869,11 @@ public class MappingCouchbaseConverterTests { } static class CustomEntity extends BaseEntity { - private BigDecimal value; - private List listOfValues; - private Map mapOfValues; + private ChoiceFormat value; + private List listOfValues; + private Map mapOfValues; - public CustomEntity(BigDecimal value, List listOfValues, Map mapOfValues) { + public CustomEntity(ChoiceFormat value, List listOfValues, Map mapOfValues) { this.value = value; this.listOfValues = listOfValues; this.mapOfValues = mapOfValues; @@ -782,11 +881,12 @@ public class MappingCouchbaseConverterTests { } static class CustomFieldsEntity extends BaseEntity { - @Field("decimalValue") private BigDecimal value; - @Field("listOfDecimalValues") private List listOfValues; - @Field("mapOfDecimalValues") private Map mapOfValues; + @Field("choiceFormatValue") private ChoiceFormat value; + @Field("listOfChoiceFormatValues") private List listOfValues; + @Field("mapOfChoiceFormatValues") private Map mapOfValues; - public CustomFieldsEntity(BigDecimal value, List listOfValues, Map mapOfValues) { + public CustomFieldsEntity(ChoiceFormat value, List listOfValues, + Map mapOfValues) { this.value = value; this.listOfValues = listOfValues; this.mapOfValues = mapOfValues;