From 0cb053df8d0d8cf3f1370f6afa76c98c5c62b3ce Mon Sep 17 00:00:00 2001 From: "jason.bodnar@blackbaud.com" Date: Tue, 23 Mar 2021 13:40:30 -0500 Subject: [PATCH] #2391 Add implicit conversion between UUID <-> String --- .../chapter-5-data-type-conversions.asciidoc | 7 ++- .../internal/conversion/ConversionUtils.java | 12 ++++ .../ap/internal/conversion/Conversions.java | 3 + .../conversion/UUIDToStringConversion.java | 37 ++++++++++++ .../conversion/uuid/UUIDConversionTest.java | 56 +++++++++++++++++++ .../ap/test/conversion/uuid/UUIDMapper.java | 19 +++++++ .../ap/test/conversion/uuid/UUIDSource.java | 33 +++++++++++ .../ap/test/conversion/uuid/UUIDTarget.java | 30 ++++++++++ 8 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/conversion/UUIDToStringConversion.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDConversionTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDTarget.java diff --git a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc index 7800b6a93..24f019fa6 100644 --- a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc +++ b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc @@ -115,7 +115,10 @@ public interface CarMapper { * When converting from a `String`, omitting `Mapping#dateFormat`, it leads to usage of the default pattern and date format symbols for the default locale. An exception to this rule is `XmlGregorianCalendar` which results in parsing the `String` according to http://www.w3.org/TR/xmlschema-2/#dateTime[XML Schema 1.0 Part 2, Section 3.2.7-14.1, Lexical Representation]. * Between `java.util.Currency` and `String`. -** When converting from a `String`, the value needs to be a valid https://en.wikipedia.org/wiki/ISO_4217[ISO-4217] alphabetic code otherwise an `IllegalArgumentException` is thrown +** When converting from a `String`, the value needs to be a valid https://en.wikipedia.org/wiki/ISO_4217[ISO-4217] alphabetic code otherwise an `IllegalArgumentException` is thrown. + +* Between `java.util.UUID` and `String`. +** When converting from a `String`, the value needs to be a valid https://en.wikipedia.org/wiki/Universally_unique_identifier[UUID] otherwise an `IllegalArgumentException` is thrown. * Between `String` and `StringBuilder` @@ -326,7 +329,7 @@ public abstract class FishTankMapperWithVolume { ---- ==== -Note the `@Mapping` annotation where `source` field is equal to `"source"`, indicating the parameter name `source` itself in the method `map(FishTank source)` instead of a (target) property in `FishTank`. +Note the `@Mapping` annotation where `source` field is equal to `"source"`, indicating the parameter name `source` itself in the method `map(FishTank source)` instead of a (target) property in `FishTank`. [[invoking-other-mappers]] diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java index 2afcf3c6a..20fb8a2fe 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/ConversionUtils.java @@ -18,6 +18,7 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Currency; import java.util.Locale; +import java.util.UUID; import org.mapstruct.ap.internal.model.common.ConversionContext; import org.mapstruct.ap.internal.util.JodaTimeConstants; @@ -242,4 +243,15 @@ public final class ConversionUtils { public static String stringBuilder(ConversionContext conversionContext) { return typeReferenceName( conversionContext, StringBuilder.class ); } + + /** + * Name for {@link java.util.UUID}. + * + * @param conversionContext Conversion context + * + * @return Name or fully-qualified name. + */ + public static String uuid(ConversionContext conversionContext) { + return typeReferenceName( conversionContext, UUID.class ); + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java index 02c478bb7..4b3ab2a81 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java @@ -22,6 +22,7 @@ import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Objects; +import java.util.UUID; import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.TypeFactory; @@ -190,6 +191,8 @@ public class Conversions { // java.util.Currency <~> String register( Currency.class, String.class, new CurrencyToStringConversion() ); + + register( UUID.class, String.class, new UUIDToStringConversion() ); } private void registerJodaConversions() { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/UUIDToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/UUIDToStringConversion.java new file mode 100644 index 000000000..b07d79938 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/UUIDToStringConversion.java @@ -0,0 +1,37 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.internal.conversion; + +import java.util.Set; +import java.util.UUID; + +import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.util.Collections; + +import static org.mapstruct.ap.internal.conversion.ConversionUtils.uuid; + +/** + * Conversion between {@link java.util.UUID} and {@link String}. + * + * @author Jason Bodnar + */ +public class UUIDToStringConversion extends SimpleConversion { + @Override + protected String getToExpression(ConversionContext conversionContext) { + return ".toString()"; + } + + @Override + protected String getFromExpression(ConversionContext conversionContext) { + return uuid( conversionContext ) + ".fromString( )"; + } + + @Override + protected Set getFromConversionImportTypes(final ConversionContext conversionContext) { + return Collections.asSet( conversionContext.getTypeFactory().getType( UUID.class ) ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDConversionTest.java new file mode 100644 index 000000000..51eab29cd --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDConversionTest.java @@ -0,0 +1,56 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.conversion.uuid; + +import java.util.UUID; + +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * Tests conversions between {@link java.util.UUID} and String. + * + * @author Jason Bodnar + */ +@IssueKey("2391") +@WithClasses({ UUIDSource.class, UUIDTarget.class, UUIDMapper.class }) +public class UUIDConversionTest { + + @ProcessorTest + public void shouldApplyUUIDConversion() { + UUIDSource source = new UUIDSource(); + source.setUUIDA( UUID.randomUUID() ); + + UUIDTarget target = UUIDMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target ).isNotNull(); + assertThat( target.getUUIDA() ).isEqualTo( source.getUUIDA().toString() ); + } + + @ProcessorTest + public void shouldApplyReverseUUIDConversion() { + UUIDTarget target = new UUIDTarget(); + target.setUUIDA( UUID.randomUUID().toString() ); + + UUIDSource source = UUIDMapper.INSTANCE.targetToSource( target ); + + assertThat( source ).isNotNull(); + assertThat( source.getUUIDA() ).isEqualTo( UUID.fromString( target.getUUIDA() ) ); + } + + @ProcessorTest + public void shouldHandleInvalidUUIDString() { + UUIDTarget target = new UUIDTarget(); + target.setInvalidUUID( "XXXXXXXXX" ); + + assertThatThrownBy( () -> UUIDMapper.INSTANCE.targetToSource( target ) ) + .isInstanceOf( IllegalArgumentException.class ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDMapper.java new file mode 100644 index 000000000..8430b0fdc --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDMapper.java @@ -0,0 +1,19 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.conversion.uuid; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface UUIDMapper { + + UUIDMapper INSTANCE = Mappers.getMapper( UUIDMapper.class ); + + UUIDTarget sourceToTarget(UUIDSource source); + + UUIDSource targetToSource(UUIDTarget target); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDSource.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDSource.java new file mode 100644 index 000000000..220ce5a76 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDSource.java @@ -0,0 +1,33 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.conversion.uuid; + +import java.util.UUID; + +/** + * @author Jason Bodnar + */ +public class UUIDSource { + private UUID uuidA; + + private UUID invalidUUID; + + public UUID getUUIDA() { + return this.uuidA; + } + + public void setUUIDA(final UUID uuidA) { + this.uuidA = uuidA; + } + + public UUID getInvalidUUID() { + return invalidUUID; + } + + public void setInvalidUUID(final UUID invalidUUID) { + this.invalidUUID = invalidUUID; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDTarget.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDTarget.java new file mode 100644 index 000000000..4fd255814 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/uuid/UUIDTarget.java @@ -0,0 +1,30 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.conversion.uuid; + +/** + * @author Jason Bodnar + */ +public class UUIDTarget { + private String uuidA; + private String invalidUUID; + + public String getUUIDA() { + return this.uuidA; + } + + public void setUUIDA(final String uuidA) { + this.uuidA = uuidA; + } + + public String getInvalidUUID() { + return this.invalidUUID; + } + + public void setInvalidUUID(final String invalidUUID) { + this.invalidUUID = invalidUUID; + } +}