Skip to content

Commit

Permalink
Fix for issue #130
Browse files Browse the repository at this point in the history
- Added DefaultBeanInitializer#mapParameterizedArguments(Type[],
  Object[]), allowing BeanMapper to properly map constructor parameters
  with a type-parameter, when using the @BeanConstruct-annotation.
- Using @sptdevos' tests to confirm proper functionality.
- Updated CHANGELOG.md
  • Loading branch information
marcus-talbot42 committed Oct 28, 2022
1 parent 4b7af33 commit 7d3ca40
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 4 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

### Fixed

- Issue [#130](https://github.com/42BV/beanmapper/issues/130) **@BeanConstruct does not map collection constructor arguments**; Added
DefaultBeanInitializer#mapParameterizedArguments(Type[], Object[]), allowing BeanMapper to properly map constructor parameters with a type-parameter, when
using the @BeanConstruct-annotation.
- Issue [#152](https://github.com/42BV/beanmapper/issues/152) **Methods that return a Collection should never return null.**; All methods that return a
Collection, will return an empty Collection of the target type, rather than returning null.
- Issue [#153](https://github.com/42BV/beanmapper/issues/153) **https://github.com/42BV/beanmapper/issues/153**; Rather than using the Boolean-wrapper, all
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
*/
package io.beanmapper.core.constructor;

import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;

import io.beanmapper.BeanMapper;
import io.beanmapper.config.BeanMapperBuilder;
import io.beanmapper.exceptions.BeanConstructException;
import io.beanmapper.exceptions.BeanInstantiationException;
import io.beanmapper.strategy.ConstructorArguments;
Expand All @@ -17,13 +27,32 @@ public <T> T instantiate(Class<T> beanClass, ConstructorArguments arguments) {
try {
if (arguments == null) {
return beanClass.getConstructor().newInstance();
} else {
return beanClass.getConstructor(arguments.getTypes()).newInstance(arguments.getValues());
}
var constructor = beanClass.getConstructor(arguments.getTypes());
var constructorParameterTypes = Arrays.stream(constructor.getParameters()).map(Parameter::getParameterizedType).toArray(Type[]::new);
return beanClass.getConstructor(arguments.getTypes()).newInstance(mapParameterizedArguments(constructorParameterTypes, arguments.getValues()));
} catch (NoSuchMethodException e) {
throw new BeanConstructException(beanClass, e);
} catch (Exception e) {
throw new BeanInstantiationException(beanClass, e);
}
}

private Object[] mapParameterizedArguments(Type[] constructorParameterTypes, Object[] arguments) {
BeanMapper beanMapper = new BeanMapperBuilder().build();
Object[] mappedArguments = new Object[arguments.length];
for (var i = 0; i < arguments.length; ++i) {
var argument = arguments[i];
if (argument instanceof Collection<?> collection) {
argument = beanMapper.map(collection, (Class<?>) ((ParameterizedType) constructorParameterTypes[i]).getActualTypeArguments()[0]);
} else if (argument instanceof Map<?, ?> map) {
argument = beanMapper.map(map, (Class<?>) ((ParameterizedType) constructorParameterTypes[i]).getActualTypeArguments()[1]);
} else if (argument instanceof Optional<?> optional) {
argument = beanMapper.map(optional, (Class<?>) ((ParameterizedType) constructorParameterTypes[i]).getActualTypeArguments()[0]);
}
mappedArguments[i] = argument;
}
return mappedArguments;
}

}
31 changes: 31 additions & 0 deletions src/test/java/io/beanmapper/annotations/BeanConstructTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.beanmapper.annotations;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.List;

import io.beanmapper.annotations.model.bean_construct.NestedSource;
import io.beanmapper.annotations.model.bean_construct.Source;
import io.beanmapper.annotations.model.bean_construct.Target;
import io.beanmapper.config.BeanMapperBuilder;

import org.junit.jupiter.api.Test;

class BeanConstructTest {

@Test
void beanConstruct_shouldUse_argsConstructor() {
NestedSource nestedSource = new NestedSource();
nestedSource.nestedField = 42;

Source source = new Source();
source.field1 = 40;
source.field2 = 41;
source.nested = List.of(nestedSource);

Target target = new BeanMapperBuilder().build().map(source, Target.class);
assertEquals(Integer.valueOf(40), target.getField1());
assertEquals(Integer.valueOf(41), target.getField2());
assertEquals(Integer.valueOf(42), target.getNested().get(0).getNestedField());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.beanmapper.annotations.model.bean_construct;

public class NestedSource {
public Integer nestedField;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.beanmapper.annotations.model.bean_construct;

public class NestedTarget {

private Integer nestedField;

public Integer getNestedField() {
return nestedField;
}

public void setNestedField(Integer nestedField) {
this.nestedField = nestedField;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.beanmapper.annotations.model.bean_construct;

import java.util.List;

public class Source {
public Integer field1;
public Integer field2;
public List<NestedSource> nested;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.beanmapper.annotations.model.bean_construct;

import java.util.List;

import io.beanmapper.annotations.BeanConstruct;

@BeanConstruct({ "field1", "field2", "nested" })
public class Target {
private final Integer field1;
private final Integer field2;
private final List<NestedTarget> nested;

public Target(Integer field1, Integer field2, List<NestedTarget> nested) {
this.field1 = field1;
this.field2 = field2;
this.nested = nested;
}

public Integer getField1() {
return field1;
}

public Integer getField2() {
return field2;
}

public List<NestedTarget> getNested() {
return nested;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,7 @@ void strictMappingConventionForOverrideConfig() {
currentConfiguration.getStrictMappingProperties().getStrictSourceSuffix());
assertEquals("Result",
currentConfiguration.getStrictMappingProperties().getStrictTargetSuffix());
assertEquals(true,
currentConfiguration.getStrictMappingProperties().isApplyStrictMappingConvention());
assertTrue(currentConfiguration.getStrictMappingProperties().isApplyStrictMappingConvention());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package io.beanmapper.core.constructor;

public class DefaultBeanInitializerTest {
}

0 comments on commit 7d3ca40

Please sign in to comment.