Skip to content

Commit

Permalink
Various Sonar improvements
Browse files Browse the repository at this point in the history
- Converted BeanMapper to record
- Converted AnnotationClass to record
- Added Virtual Thread concurrency test.
- Improved use of generics.
- Added private constructors to utility-classes, to hide implicit public
 constructors.
- Removed redundant MappingException.java
  • Loading branch information
marcus-talbot42 committed Dec 2, 2024
1 parent 27dcaad commit 5b5c702
Show file tree
Hide file tree
Showing 34 changed files with 353 additions and 203 deletions.
12 changes: 6 additions & 6 deletions docs/src/test/java/io/beanmapper/BeanCollectionExample.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ public class BeanCollectionExample {
@Test
void mapCollectionWithConstruct() {
Source source = new Source();
source.items = new LinkedHashSet<Person>();
source.items = new LinkedHashSet<>();
source.items.add(new Person(1L, "Henk", 42));
source.items.add(new Person(2L, "Piet", 18));
source.items.add(new Person(3L, "Gijs", 67));

TargetWithConstruct target = new TargetWithConstruct();
target.items = new ArrayList<PersonResult>();
target.items = new ArrayList<>();
target.items.add(new PersonResult("Kees", 13));
target.items.add(new PersonResult("Klaas", 24));

Expand All @@ -40,13 +40,13 @@ void mapCollectionWithConstruct() {
@Test
void mapCollectionWithReuse() {
Source source = new Source();
source.items = new LinkedHashSet<Person>();
source.items = new LinkedHashSet<>();
source.items.add(new Person(1L, "Henk", 42));
source.items.add(new Person(2L, "Piet", 18));
source.items.add(new Person(3L, "Gijs", 67));

TargetWithReuse target = new TargetWithReuse();
target.items = new ArrayList<PersonResult>();
target.items = new ArrayList<>();
target.items.add(new PersonResult("Kees", 13));
target.items.add(new PersonResult("Klaas", 24));

Expand All @@ -64,13 +64,13 @@ void mapCollectionWithReuse() {
@Test
void mapCollectionWithClear() {
Source source = new Source();
source.items = new LinkedHashSet<Person>();
source.items = new LinkedHashSet<>();
source.items.add(new Person(1L, "Henk", 42));
source.items.add(new Person(2L, "Piet", 18));
source.items.add(new Person(3L, "Gijs", 67));

TargetWithClear target = new TargetWithClear();
target.items = new ArrayList<PersonResult>();
target.items = new ArrayList<>();
target.items.add(new PersonResult("Kees", 13));
target.items.add(new PersonResult("Klaas", 24));

Expand Down
46 changes: 19 additions & 27 deletions src/main/java/io/beanmapper/BeanMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,16 @@
* objects. Once that has been determined, the applicable properties will be copied from
* source to target.
*/
public final class BeanMapper {
public record BeanMapper(Configuration configuration) {

private final Configuration configuration;

public BeanMapper(Configuration configuration) {
this.configuration = configuration;
}

private <S> void addDiagnosticsNode(S source) {
@SuppressWarnings("unchecked")
private <S, T> void addDiagnosticsNode(S source) {
if (configuration instanceof DiagnosticsConfiguration dc && dc.isInDiagnosticsMode()) {
if (dc.getParentConfiguration().orElse(null) instanceof DiagnosticsConfigurationImpl dci) {
dci.getBeanMapperDiagnostics().ifPresent(bd -> bd.getDiagnostics().clear());
}
Class<S> sourceClass = source != null ? (Class<S>) configuration.getBeanUnproxy().unproxy(source.getClass()) : (Class<S>) Void.class;
DiagnosticsNode<?, ?> node = getsDiagnosticsNode(source, sourceClass);
DiagnosticsNode<S, T> node = getsDiagnosticsNode(source, sourceClass);
dc.setBeanMapperDiagnostics(node);
if (configuration instanceof OverrideConfiguration) {
dc.getParentConfiguration()
Expand All @@ -52,13 +47,15 @@ private <S> void addDiagnosticsNode(S source) {
}
}

private <S> DiagnosticsNode<?, ?> getsDiagnosticsNode(S source, Class<S> sourceClass) {
DiagnosticsNode<?, ?> node = new MappingDiagnosticsNode<S, Object>(sourceClass, configuration.getTargetClass());
@SuppressWarnings({ "unchecked", "rawtypes" })
private <S, T> DiagnosticsNode<S, T> getsDiagnosticsNode(S source, Class<S> sourceClass) {
DiagnosticsNode<S, T> node = new MappingDiagnosticsNode<>(sourceClass, configuration.getTargetClass());
if (source instanceof Collection<?>) {
node = new CollectionMappingDiagnosticNode<>(source, sourceClass, configuration.getPreferredCollectionClass(), configuration.getTargetClass());
}
if (source instanceof Map<?, ?>) {
node = new MapMappingDiagnosticsNode<>((Map) source, (Class<Map>) sourceClass, configuration.getPreferredCollectionClass(), configuration.getTargetClass());
if (source instanceof Map<?, ?> map) {
node = new MapMappingDiagnosticsNode<>(map, (Class<Map>) sourceClass, configuration.getPreferredCollectionClass(),
configuration.getTargetClass());
}
return node;
}
Expand All @@ -67,7 +64,7 @@ public <S, T> T map(S source) {
addDiagnosticsNode(source);
if (source == null && !configuration.getUseNullValue()) {
// noinspection unchecked
return (T) this.getConfiguration().getDefaultValueForClass(this.getConfiguration().getTargetClass());
return (T) this.configuration().getDefaultValueForClass(this.configuration().getTargetClass());
}
T result = MapStrategyType.getStrategy(this, configuration).map(source);
if (this.configuration.getParentConfiguration().orElseThrow() instanceof DiagnosticsConfigurationImpl dc) {
Expand Down Expand Up @@ -101,6 +98,7 @@ public <S, T> T map(S source, T target) {
* @param <T> the instance to which the properties get copied
* @return the optional target instance containing all applicable properties
*/
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public <S, T> Optional<T> map(Optional<S> source, Class<T> targetClass) {
if (source.orElse(null) instanceof Optional<?> optional && !targetClass.isInstance(Optional.class)) {
return this.map(optional, targetClass);
Expand All @@ -122,15 +120,12 @@ public <S, T> Optional<T> map(Optional<S> source, Class<T> targetClass) {
* @return The result of the mapping.
*/
public <S, P extends ParameterizedType> Object map(S source, P target) {
if (source instanceof Collection<?> collection) {
return this.map(collection, (Class<?>) target.getActualTypeArguments()[0]);
} else if (source instanceof Map<?, ?> map) {
return this.map(map, (Class<?>) target.getActualTypeArguments()[1]);
} else if (source instanceof Optional<?> optional) {
return this.map(optional, (Class<?>) target.getActualTypeArguments()[0]);
} else {
return this.map(source, (Class<?>) target.getRawType());
}
return switch (source) {
case Collection<?> collection -> this.map(collection, (Class<?>) target.getActualTypeArguments()[0]);
case Map<?, ?> map -> this.map(map, (Class<?>) target.getActualTypeArguments()[1]);
case Optional<?> optional -> this.map(optional, (Class<?>) target.getActualTypeArguments()[0]);
default -> this.map(source, (Class<?>) target.getRawType());
};
}

/**
Expand Down Expand Up @@ -158,6 +153,7 @@ public <S, T> T map(S source, Class<T> targetClass) {
* @param <T> The type of the elements in the target array.
* @return A newly constructed array of the type of the target class.
*/
@SuppressWarnings("unchecked")
public <S, T> T[] map(S[] sourceArray, Class<T> targetClass) {
return Arrays.stream(sourceArray)
.map(element -> this.wrap().setConverterChoosable(true).build().map(element, targetClass))
Expand Down Expand Up @@ -248,8 +244,4 @@ public BeanMapperBuilder wrap() {
public BeanMapperBuilder wrap(DiagnosticsDetailLevel detailLevel) {
return new BeanMapperBuilder(configuration, detailLevel);
}

public Configuration getConfiguration() {
return configuration;
}
}
4 changes: 2 additions & 2 deletions src/main/java/io/beanmapper/annotations/BeanCollection.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
* type will be used by BeanMapper to pass the target class type.
* @return the class type of a target element collection
*/
Class elementType() default void.class;
Class<?> elementType() default void.class;

/**
* Determines how BeanMapper must deal with the target collection. The default option
Expand All @@ -40,7 +40,7 @@
* instead of the default one provided by the collection handler
* @return the class to instantiate instead of the one provided by the collection handler
*/
Class preferredCollectionClass() default void.class;
Class<?> preferredCollectionClass() default void.class;

/**
* When usage is CLEAR and the target collection is being managed by, eg, Hibernate's
Expand Down
18 changes: 9 additions & 9 deletions src/main/java/io/beanmapper/config/BeanMapperBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

public class BeanMapperBuilder {

private static final List<CollectionHandler> DEFAULT_COLLECTION_HANDLERS =
private static final List<CollectionHandler<?>> DEFAULT_COLLECTION_HANDLERS =
List.of(
new MapCollectionHandler(),
new SetCollectionHandler(),
Expand All @@ -45,7 +45,7 @@ public class BeanMapperBuilder {

private final List<BeanConverter> customBeanConverters = new ArrayList<>();

private final List<CollectionHandler> customCollectionHandlers = new ArrayList<>();
private final List<CollectionHandler<?>> customCollectionHandlers = new ArrayList<>();

public BeanMapperBuilder() {
this.configuration = new CoreConfiguration();
Expand Down Expand Up @@ -79,7 +79,7 @@ public BeanMapperBuilder addConverter(BeanConverter converter) {
return this;
}

public BeanMapperBuilder addCollectionHandler(CollectionHandler handler) {
public BeanMapperBuilder addCollectionHandler(CollectionHandler<?> handler) {
this.customCollectionHandlers.add(handler);
return this;
}
Expand Down Expand Up @@ -114,7 +114,7 @@ public BeanMapperBuilder addAfterClearFlusher(AfterClearFlusher afterClearFlushe
return this;
}

public BeanMapperBuilder addLogicSecuredCheck(LogicSecuredCheck logicSecuredCheck) {
public BeanMapperBuilder addLogicSecuredCheck(LogicSecuredCheck<?, ?> logicSecuredCheck) {
this.configuration.addLogicSecuredCheck(logicSecuredCheck);
return this;
}
Expand Down Expand Up @@ -151,12 +151,12 @@ public BeanMapperBuilder downsizeTarget(List<String> includeFields) {
return this;
}

public BeanMapperBuilder setCollectionClass(Class collectionClass) {
public BeanMapperBuilder setCollectionClass(Class<?> collectionClass) {
this.configuration.setCollectionClass(collectionClass);
return this;
}

public BeanMapperBuilder setTargetClass(Class targetClass) {
public BeanMapperBuilder setTargetClass(Class<?> targetClass) {
this.configuration.setTargetClass(targetClass);
return this;
}
Expand Down Expand Up @@ -247,8 +247,8 @@ public BeanMapper build() {
return beanMapper;
}

private void addCollectionHandlers(List<CollectionHandler> collectionHandlers) {
for (CollectionHandler collectionHandler : collectionHandlers) {
private void addCollectionHandlers(List<CollectionHandler<?>> collectionHandlers) {
for (CollectionHandler<?> collectionHandler : collectionHandlers) {
attachCollectionHandler(collectionHandler);
}
}
Expand Down Expand Up @@ -277,7 +277,7 @@ private void addDefaultConverters() {
}
}

private void attachCollectionHandler(CollectionHandler collectionHandler) {
private void attachCollectionHandler(CollectionHandler<?> collectionHandler) {
configuration.addCollectionHandler(collectionHandler);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public abstract class AbstractCollectionHandler<C> implements CollectionHandler<
private final Class<C> type;
private final DefaultBeanInitializer beanInitializer = new DefaultBeanInitializer();

@SuppressWarnings("unchecked")
protected AbstractCollectionHandler() {
this.type = (Class<C>) Classes.getParameteredTypes(getClass())[0];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public final <U, R> R convert(BeanMapper beanMapper, U source, Class<R> targetCl
this.beanMapper = beanMapper;
if (source == null) {
if (beanMapper != null)
beanMapper.getConfiguration().getDefaultValueForClass(targetClass);
beanMapper.configuration().getDefaultValueForClass(targetClass);
return null;
}
Check.argument(isMatchingSource(source.getClass()), "Unsupported source class.");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package io.beanmapper.core.converter.collections;

public class AnnotationClass {
public record AnnotationClass(Class<?> annotationClass) {

public static final AnnotationClass EMPTY_ANNOTATION_CLASS = new AnnotationClass(null);

private final Class<?> annotationClass;

public AnnotationClass(Class<?> annotationClass) {
this.annotationClass = determineClass(annotationClass);
}
Expand All @@ -16,10 +14,6 @@ private Class<?> determineClass(Class<?> collectionElementType) {
collectionElementType;
}

public Class<?> getAnnotationClass() {
return this.annotationClass;
}

public boolean isEmpty() {
return this.annotationClass == null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public <R, U> U convert(
return BeanMapperPerformanceLogger.runTimed(() -> beanMapper.wrap()
.setCollectionClass(collectionHandler.getType())
.setCollectionUsage(beanPropertyMatch.getCollectionInstructions().getBeanCollectionUsage())
.setPreferredCollectionClass(beanPropertyMatch.getCollectionInstructions().getPreferredCollectionClass().getAnnotationClass())
.setPreferredCollectionClass(beanPropertyMatch.getCollectionInstructions().getPreferredCollectionClass().annotationClass())
.setFlushAfterClear(beanPropertyMatch.getCollectionInstructions().getFlushAfterClear())
.setTargetClass(beanPropertyMatch.getCollectionInstructions().getCollectionElementType().getType())
.setTarget(beanPropertyMatch.getTargetObject())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ public class CollectionElementType {
public static final CollectionElementType EMPTY_COLLECTION_ELEMENT_TYPE =
new CollectionElementType(null, true);

private final AnnotationClass collectionElementType;
private final AnnotationClass classOfCollectionElement;

private final boolean derived;

private CollectionElementType(Class<?> collectionElementType, boolean derived) {
this.collectionElementType = new AnnotationClass(collectionElementType);
private CollectionElementType(Class<?> classOfCollectionElement, boolean derived) {
this.classOfCollectionElement = new AnnotationClass(classOfCollectionElement);
this.derived = derived;
}

Expand All @@ -27,11 +27,11 @@ public boolean isDerived() {
}

public Class<?> getType() {
return collectionElementType.getAnnotationClass();
return classOfCollectionElement.annotationClass();
}

public boolean isEmpty() {
return collectionElementType.isEmpty();
return classOfCollectionElement.isEmpty();
}

}
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package io.beanmapper.core.converter.impl;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import io.beanmapper.BeanMapper;
import io.beanmapper.core.BeanPropertyMatch;
import io.beanmapper.core.converter.BeanConverter;
import io.beanmapper.utils.BeanMapperTraceLogger;
import io.beanmapper.utils.Classes;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
* This converter facilitates the conversion of an arbitrary amount of Optional wrappers, however, support for complex
* datastructures, such as Maps, Sets, List, etc. is limited to a single layer. As such, if the user requires support
Expand Down Expand Up @@ -101,6 +106,6 @@ private Object convertToCollection(BeanMapper beanMapper, Object source, BeanPro
Type[] genericTypes = Classes.getParameteredTypes(beanPropertyMatch.getTarget().getClass(), beanPropertyMatch);
return new HashMap<>(beanMapper.map((Map<?, ?>) map, (Class<?>) genericTypes[1]));
}
return beanMapper.getConfiguration().getDefaultValueForClass(source.getClass());
return beanMapper.configuration().getDefaultValueForClass(source.getClass());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import io.beanmapper.BeanMapper;
import io.beanmapper.core.BeanPropertyMatch;
import io.beanmapper.core.converter.BeanConverter;
import io.beanmapper.utils.Check;

/**
* Converter between boxed and primitive type.
Expand Down Expand Up @@ -41,7 +40,7 @@ private static void register(Class<?> boxedClass, Class<?> primitiveClass) {
*/
@Override
public <S, T> T convert(BeanMapper beanMapper, S source, Class<T> targetClass, BeanPropertyMatch beanPropertyMatch) {
return source != null ? (T) source : beanMapper.getConfiguration().getDefaultValueForClass(targetClass); // Value will automatically be boxed or unboxed
return source != null ? (T) source : beanMapper.configuration().getDefaultValueForClass(targetClass); // Value will automatically be boxed or unboxed
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class RecordToAnyConverter implements BeanConverter {
@Override
public <S, T> T convert(BeanMapper beanMapper, S source, Class<T> targetClass, BeanPropertyMatch beanPropertyMatch) {
try {
Class<?> intermediaryClass = beanMapper.getConfiguration().getClassStore().getOrCreateGeneratedClass(source.getClass(),
Class<?> intermediaryClass = beanMapper.configuration().getClassStore().getOrCreateGeneratedClass(source.getClass(),
List.of(Records.getRecordFieldNames(
(Class<? extends Record>) source.getClass())), null);
Object intermediaryObject = intermediaryClass.cast(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.beanmapper.exceptions;

public abstract class BeanMappingException extends MappingException {
public abstract class BeanMappingException extends RuntimeException {

protected BeanMappingException(String message) {
super(message);
Expand Down
13 changes: 0 additions & 13 deletions src/main/java/io/beanmapper/exceptions/MappingException.java

This file was deleted.

Loading

0 comments on commit 5b5c702

Please sign in to comment.