diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 570e4bc5..4f39adb7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -12,7 +12,7 @@ jobs:
build:
strategy:
matrix:
- java: [ '8', '11', '17' ]
+ java: [ '8', '11', '17', '21' ]
os: [ 'ubuntu-latest' ]
runs-on: ${{ matrix.os }}
steps:
diff --git a/.gitignore b/.gitignore
index cba934ab..3c7a5f1d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,4 +8,5 @@ release.properties
.settings/
_site/
user-manual/
-*.iml
\ No newline at end of file
+*.iml
+.idea/
diff --git a/CHANGES.md b/CHANGES.md
index 6157b977..60ebb24c 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,7 @@
+# 3.2.0
+
+* Bump asm and byte-buddy for JDK 21 support
+
# 3.1.0
* Migrates optional converters from modelmapper-module-java8
diff --git a/benchmarks/pom.xml b/benchmarks/pom.xml
index 92f2f216..b901d1c6 100644
--- a/benchmarks/pom.xml
+++ b/benchmarks/pom.xml
@@ -5,7 +5,7 @@
modelmapper-parent
org.modelmapper
- 3.1.0
+ 3.2.1-SNAPSHOT
benchmarks
diff --git a/core/pom.xml b/core/pom.xml
index 14c9b1e5..ef19fef2 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -5,7 +5,7 @@
org.modelmapper
modelmapper-parent
- 3.1.0
+ 3.2.1-SNAPSHOT
modelmapper
diff --git a/core/src/main/java/org/modelmapper/ModelMapper.java b/core/src/main/java/org/modelmapper/ModelMapper.java
index 4c3a6f86..e60f895a 100644
--- a/core/src/main/java/org/modelmapper/ModelMapper.java
+++ b/core/src/main/java/org/modelmapper/ModelMapper.java
@@ -379,6 +379,22 @@ public TypeMap emptyTypeMap(Class sourceType, Class destinati
return config.typeMapStore.createEmptyTypeMap(sourceType, destinationType, null, config, engine);
}
+ /**
+ * Creates an empty TypeMap for the {@code sourceType}, {@code destinationType}.
+ *
+ * @param source type
+ * @param destination type
+ * @throws IllegalArgumentException is {@code sourceType} or {@code destinationType} are null, or {@code TypeMap TypeMap emptyTypeMap(Class sourceType, Class destinationType, String typeMapName) {
+ Assert.notNull(sourceType, "sourceType");
+ Assert.notNull(destinationType, "destinationType");
+ Assert.notNull(typeMapName, "typeMapName");
+ Assert.isNull(config.typeMapStore.get(sourceType, destinationType, typeMapName), "TypeMap already defined");
+ return config.typeMapStore.createEmptyTypeMap(sourceType, destinationType, typeMapName, config, engine);
+ }
+
/**
* Returns all TypeMaps for the ModelMapper.
*/
diff --git a/core/src/main/java/org/modelmapper/internal/Errors.java b/core/src/main/java/org/modelmapper/internal/Errors.java
index ee4df925..6b714897 100644
--- a/core/src/main/java/org/modelmapper/internal/Errors.java
+++ b/core/src/main/java/org/modelmapper/internal/Errors.java
@@ -26,7 +26,6 @@
import java.util.Collections;
import java.util.Formatter;
import java.util.List;
-
import org.modelmapper.ConfigurationException;
import org.modelmapper.MappingException;
import org.modelmapper.TypeMap;
@@ -375,6 +374,13 @@ Errors sourceOutsideOfMap() {
return addMessage("'source' cannot be used outside of a map statement.");
}
+ Errors skipConflict(String skip, List paths) {
+ return addMessage("Not able to skip %s, because there are already nested properties are mapped: [%s]. "
+ + "Do you skip the property after the implicit mappings mapped? "
+ + "We recommended you to create an empty type map, and followed by addMappings and implicitMappings calls",
+ skip, String.join(" ", paths));
+ }
+
void throwMappingExceptionIfErrorsExist() {
if (hasErrors())
throw new MappingException(getMessages());
diff --git a/core/src/main/java/org/modelmapper/internal/InheritingConfiguration.java b/core/src/main/java/org/modelmapper/internal/InheritingConfiguration.java
index ba3cec66..8c8c8078 100644
--- a/core/src/main/java/org/modelmapper/internal/InheritingConfiguration.java
+++ b/core/src/main/java/org/modelmapper/internal/InheritingConfiguration.java
@@ -172,6 +172,10 @@ public boolean equals(Object obj) {
return false;
if (isFieldMatchingEnabled() != other.isFieldMatchingEnabled())
return false;
+ if (!getSourceNamingConvention().equals(other.getSourceNamingConvention()))
+ return false;
+ if (!getDestinationNamingConvention().equals(other.getDestinationNamingConvention()))
+ return false;
return true;
}
@@ -283,6 +287,8 @@ public int hashCode() {
result = prime * result + getDestinationNameTransformer().hashCode();
result = prime * result + getFieldAccessLevel().hashCode();
result = prime * result + getMethodAccessLevel().hashCode();
+ result = prime * result + getSourceNamingConvention().hashCode();
+ result = prime * result + getDestinationNamingConvention().hashCode();
result = prime * result + (isFieldMatchingEnabled() ? 1231 : 1237);
return result;
}
diff --git a/core/src/main/java/org/modelmapper/internal/ReferenceMapExpressionImpl.java b/core/src/main/java/org/modelmapper/internal/ReferenceMapExpressionImpl.java
index f73c006b..42166bdc 100644
--- a/core/src/main/java/org/modelmapper/internal/ReferenceMapExpressionImpl.java
+++ b/core/src/main/java/org/modelmapper/internal/ReferenceMapExpressionImpl.java
@@ -15,13 +15,16 @@
*/
package org.modelmapper.internal;
+import static java.util.stream.Collectors.toList;
import static org.modelmapper.internal.ExplicitMappingBuilder.MappingOptions;
import static org.modelmapper.internal.util.Assert.notNull;
+import java.util.List;
import net.jodah.typetools.TypeResolver;
import org.modelmapper.builder.ReferenceMapExpression;
import org.modelmapper.internal.util.Primitives;
import org.modelmapper.spi.DestinationSetter;
+import org.modelmapper.spi.Mapping;
import org.modelmapper.spi.SourceGetter;
/**
@@ -62,7 +65,7 @@ class ReferenceMapExpressionImpl implements ReferenceMapExpression {
public void map(SourceGetter sourceGetter, DestinationSetter destinationSetter) {
visitSource(sourceGetter);
visitDestination(destinationSetter);
- typeMap.addMapping(collector.collect());
+ skipMapping(collector.collect());
collector.reset();
}
@@ -70,16 +73,30 @@ public void map(SourceGetter sourceGetter, DestinationSetter destin
public void skip(DestinationSetter destinationSetter) {
options.skipType = 1;
visitDestination(destinationSetter);
- typeMap.addMapping(collector.collect());
+ skipMapping(collector.collect());
collector.reset();
}
+ private void skipMapping(MappingImpl skipMapping) {
+ String prefix = skipMapping.getPath();
+ List conflictPaths = typeMap.getMappings().stream()
+ .map(Mapping::getPath)
+ .filter(path -> path.startsWith(prefix) && !path.equals(prefix))
+ .collect(toList());
+ if (conflictPaths.isEmpty()) {
+ typeMap.addMapping(skipMapping);
+ } else {
+ collector.getErrors().skipConflict(skipMapping.getPath(), conflictPaths);
+ collector.getErrors().throwConfigurationExceptionIfErrorsExist();
+ }
+ }
+
@Override
public void skip(SourceGetter sourceGetter, DestinationSetter destinationSetter) {
options.skipType = 1;
visitSource(sourceGetter);
visitDestination(destinationSetter);
- typeMap.addMapping(collector.collect());
+ skipMapping(collector.collect());
collector.reset();
}
diff --git a/core/src/main/java/org/modelmapper/internal/converter/ConverterStore.java b/core/src/main/java/org/modelmapper/internal/converter/ConverterStore.java
index a947d87a..e5e15407 100644
--- a/core/src/main/java/org/modelmapper/internal/converter/ConverterStore.java
+++ b/core/src/main/java/org/modelmapper/internal/converter/ConverterStore.java
@@ -30,7 +30,7 @@ public final class ConverterStore {
new FromOptionalConverter(),new OptionalConverter(), new ToOptionalConverter(),
new AssignableConverter(), new StringConverter(), new EnumConverter(), new NumberConverter(),
new BooleanConverter(), new CharacterConverter(), new DateConverter(),
- new CalendarConverter(),
+ new CalendarConverter(), new UuidConverter(),
};
private final List> converters;
diff --git a/core/src/main/java/org/modelmapper/internal/converter/UuidConverter.java b/core/src/main/java/org/modelmapper/internal/converter/UuidConverter.java
new file mode 100644
index 00000000..f8b0f90c
--- /dev/null
+++ b/core/src/main/java/org/modelmapper/internal/converter/UuidConverter.java
@@ -0,0 +1,37 @@
+package org.modelmapper.internal.converter;
+
+import java.util.UUID;
+import org.modelmapper.spi.ConditionalConverter;
+import org.modelmapper.spi.MappingContext;
+
+/**
+ * Converts objects to UUID.
+ */
+class UuidConverter implements ConditionalConverter