Skip to content

Commit

Permalink
Version 2.2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
eye2web committed May 25, 2020
1 parent a988965 commit 9c8f11b
Show file tree
Hide file tree
Showing 19 changed files with 152 additions and 35 deletions.
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# model-mapper
Easy to use annotation based model mapper with advanced capabilities.

## Highlights 2.2.0
- Now supports custom mapping of each element in a Iterable field.
- Each ValueMapper class will now receive the ModelMapper Object to support mapping of nested Objects.

## Highlights 2.1.0
- Now supports Spring boot managed ValueMapper and MultiValueMapper beans! (check tests for examples)

Expand All @@ -24,7 +28,7 @@ Maven
<dependency>
<groupId>com.github.eye2web</groupId>
<artifactId>model-mapper</artifactId>
<version>2.1.0</version>
<version>2.2.0</version>
</dependency>
```

Expand All @@ -33,10 +37,22 @@ For advanced use, the following field annotations can be used.

Examples can be found in src/main/test.

Usage:
```java
final ModelMapper modelMapper = new ModelMapper();
final ModelAResponse modelAResponse = modelMapper.map(modelA, ModelAResponse.class);
```

```java
@MapValue(fieldName = "firstName")
private String firstName;
```
Iterates over each value in given Iterable. Currently only supports ArrayList<Object> as result map.
For each item the mapper class will be called.
```java
@MapValue(fieldName = "names", valueMapper = UpperCaseValueMapper.class, iterate = true)
private List<String> upperCaseNames;
```

Mapping multiple fields into one. The multi value mapper is custom and implements the 'MultiValueMapper' interface.
```java
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ ext {
lombokVersion = '1.18.10'
groupName = 'com.github.eye2web'
artifactIdentifier = 'model-mapper'
projectVersion = '2.1.0'
projectVersion = '2.2.0'
}

group groupName
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/eye2web/modelmapper/ModelMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@ public <D> D map(final Object source, final Class<D> destinationType)

final D destinationObj = destinationType.cast(createEmptyInstanceOfNoArgsClass(destinationType));

getCurrentStrategy().mapObjects(source, destinationObj);
getCurrentStrategy().mapObjects(source, destinationObj, this);

return destinationObj;
}

@Override
public <D> void map(final Object source, final D destinationObj)
throws Exception {
getCurrentStrategy().mapObjects(source, destinationObj);
getCurrentStrategy().mapObjects(source, destinationObj, this);
}

@Override
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/eye2web/modelmapper/annotations/MapValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,8 @@

Class<? extends ValueMapper> valueMapper() default DefaultValueMapper.class;

// Currently only supports arrayList as return type
boolean iterate() default false;

FieldProperties[] properties() default {};
}
3 changes: 3 additions & 0 deletions src/main/java/eye2web/modelmapper/model/FromField.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ public class FromField {
private final String fieldName;
private final Object fieldValue;

@Builder.Default
private final boolean isIterableItem = false;

public boolean containsValue() {
return Objects.nonNull(fieldValue);
}
Expand Down
36 changes: 23 additions & 13 deletions src/main/java/eye2web/modelmapper/strategy/FieldStrategy.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package eye2web.modelmapper.strategy;

import eye2web.modelmapper.ModelMapperI;
import eye2web.modelmapper.annotations.MapValue;
import eye2web.modelmapper.annotations.MapValues;
import eye2web.modelmapper.model.FromField;
Expand All @@ -23,17 +24,17 @@ public static Strategy getInstance() {

return strategy;
}

@Override
public void mapObjects(Object source, Object destinationObj) throws Exception {
public void mapObjects(final Object source, final Object destinationObj, final ModelMapperI modelMapper) throws Exception {
final List<Map.Entry<String, Object>> sourceFieldValues = StrategyUtil.getFieldValues(source);

for (final Field field : destinationObj.getClass().getDeclaredFields()) {

if (Objects.nonNull(field.getAnnotation(MapValues.class))) {
tryMapMultiValueField(field, sourceFieldValues, destinationObj);
} else {
tryMapSingleValueField(field, sourceFieldValues, destinationObj);
tryMapSingleValueField(field, sourceFieldValues, destinationObj, modelMapper);
}
}
}
Expand Down Expand Up @@ -69,7 +70,8 @@ private void tryMapMultiValueField(final Field field, final List<Map.Entry<Strin


private void tryMapSingleValueField(final Field field, final List<Map.Entry<String, Object>> sourceFieldValues,
final Object destinationObj) throws Exception {
final Object destinationObj,
ModelMapperI modelMapper) throws Exception {

final String fieldName = StrategyUtil.getFieldName(field);

Expand All @@ -78,13 +80,14 @@ private void tryMapSingleValueField(final Field field, final List<Map.Entry<Stri
.findFirst();

if (vOpt.isPresent()) {
mapFieldValue(destinationObj, field, fieldName, vOpt.get().getValue());
mapFieldValue(destinationObj, field, fieldName, vOpt.get().getValue(), modelMapper);
}
}


private void mapFieldValue(final Object destinationObj, final Field field, final String fieldName,
final Object fieldValue)
final Object fieldValue,
final ModelMapperI modelMapper)
throws Exception {

final Object value;
Expand All @@ -95,14 +98,22 @@ private void mapFieldValue(final Object destinationObj, final Field field, final
return;
}

final var mapFromField = FromField.builder()
.fieldName(fieldName)
.fieldValue(fieldValue)
.build();

final var objectValueMapper = StrategyUtil.getSingleValueMapper(field);

value = objectValueMapper.mapToValue(mapFromField);
if (Objects.isNull(mapValue)) {
value = fieldValue;
} else if (mapValue.iterate()) {
value = StrategyUtil.iterateElements(fieldValue, fieldName, objectValueMapper, modelMapper);

} else {
final var mapFromField = FromField.builder()
.fieldName(fieldName)
.fieldValue(fieldValue)
.build();

value = objectValueMapper.mapToValue(mapFromField, modelMapper);
}


boolean isPrivate = StrategyUtil.setFieldPublic(field, destinationObj);

Expand All @@ -113,7 +124,6 @@ private void mapFieldValue(final Object destinationObj, final Field field, final
}
}


private void mapFieldValues(final Object destinationObj, final Field field, final Set<FromField> fromFieldSet)
throws Exception {

Expand Down
40 changes: 31 additions & 9 deletions src/main/java/eye2web/modelmapper/strategy/MethodStrategy.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package eye2web.modelmapper.strategy;

import eye2web.modelmapper.ModelMapperI;
import eye2web.modelmapper.annotations.MapValue;
import eye2web.modelmapper.annotations.MapValues;
import eye2web.modelmapper.model.FromField;
Expand Down Expand Up @@ -28,7 +29,7 @@ public static Strategy getInstance() {
}

@Override
public void mapObjects(Object source, Object destinationObj) {
public void mapObjects(final Object source, final Object destinationObj, final ModelMapperI modelMapper) {

final var METHOD_GET_PREFIX = "get";
final var METHOD_SET_PREFIX = "set";
Expand Down Expand Up @@ -73,13 +74,14 @@ public void mapObjects(Object source, Object destinationObj) {
.map(Optional::get)
.collect(Collectors.toList());

mapGettersToSetters(source, getters, destinationObj, setters);
mapGettersToSetters(source, getters, destinationObj, setters, modelMapper);
}

private void mapGettersToSetters(final Object source,
final List<Pair<Field, Method>> getters,
final Object destination,
final List<Pair<Field, Method>> setters) {
final List<Pair<Field, Method>> setters,
final ModelMapperI modelMapper) {
setters.forEach(setter -> {

// Multimap many to one
Expand Down Expand Up @@ -135,17 +137,36 @@ private void mapGettersToSetters(final Object source,
try {
final var value = getter.getValue().invoke(source);

if (!StrategyUtil.shouldIgnoreFieldValue(setter.getKey().getAnnotation(MapValue.class), value)) {
final var annotation = setter.getKey().getAnnotation(MapValue.class);

if (Objects.isNull(annotation)) {
// set field values without annotation
setter.getValue().invoke(destination, value);
} else if (!StrategyUtil.shouldIgnoreFieldValue(annotation, value)) {

final Object resultValue;

final var
objectValueMapper = StrategyUtil.getSingleValueMapper(setter.getKey());

final var mapFromField = FromField.builder()
.fieldValue(value)
.fieldName(getter.getKey().getName())
.build();
// If iterable - value should be handled as iterable
if (annotation.iterate()) {
resultValue = StrategyUtil.iterateElements(value,
getter.getKey().getName(),
objectValueMapper,
modelMapper);

} else {
final var mapFromField = FromField.builder()
.fieldValue(value)
.fieldName(getter.getKey().getName())
.build();

setter.getValue().invoke(destination, objectValueMapper.mapToValue(mapFromField));
resultValue = objectValueMapper.mapToValue(mapFromField, modelMapper);
}

// Set value
setter.getValue().invoke(destination, resultValue);
}
} catch (Exception ex) {
System.out.println(
Expand All @@ -155,4 +176,5 @@ private void mapGettersToSetters(final Object source,
});
});
}

}
4 changes: 3 additions & 1 deletion src/main/java/eye2web/modelmapper/strategy/Strategy.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package eye2web.modelmapper.strategy;

import eye2web.modelmapper.ModelMapperI;

public interface Strategy {


void mapObjects(final Object source, final Object destinationObj) throws Exception;
void mapObjects(final Object source, final Object destinationObj, final ModelMapperI modelMapper) throws Exception;

}
24 changes: 23 additions & 1 deletion src/main/java/eye2web/modelmapper/strategy/StrategyUtil.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package eye2web.modelmapper.strategy;

import eye2web.modelmapper.model.FieldProperties;
import eye2web.modelmapper.ModelMapperI;
import eye2web.modelmapper.annotations.MapValue;
import eye2web.modelmapper.annotations.MapValues;
import eye2web.modelmapper.model.FieldProperties;
import eye2web.modelmapper.model.FromField;
import eye2web.modelmapper.value.map.*;

import java.lang.reflect.Field;
Expand Down Expand Up @@ -77,5 +79,25 @@ public static boolean setFieldPublic(final Field field, final Object object) {
return didSetPublic;
}

public static List<Object> iterateElements(final Object value,
final String fieldName,
final ValueMapper valueMapper,
final ModelMapperI modelMapper) {

final var results = new ArrayList<>();

for (Object val : (Iterable<Object>) value) {

final var mapFromField = FromField.builder()
.fieldValue(val)
.fieldName(fieldName)
.isIterableItem(true)
.build();

results.add(valueMapper.mapToValue(mapFromField, modelMapper));
}

return results;
}

}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package eye2web.modelmapper.value.map;

import eye2web.modelmapper.ModelMapperI;
import eye2web.modelmapper.model.FromField;

public class DefaultValueMapper implements ValueMapper {

@Override
public Object mapToValue(final FromField fromField) {
public Object mapToValue(final FromField fromField, final ModelMapperI modelMapper) {
return fromField.getFieldValue();
}
}
3 changes: 2 additions & 1 deletion src/main/java/eye2web/modelmapper/value/map/ValueMapper.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package eye2web.modelmapper.value.map;

import eye2web.modelmapper.ModelMapperI;
import eye2web.modelmapper.model.FromField;

public interface ValueMapper {

Object mapToValue(final FromField fromField);
Object mapToValue(final FromField fromField, final ModelMapperI modelMapper);

}
11 changes: 10 additions & 1 deletion src/test/java/eye2web/modelmapper/ModelMapperTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import java.time.LocalDate;
import java.time.Period;
import java.util.ArrayList;

@RunWith(MockitoJUnitRunner.class)
public class ModelMapperTest {
Expand All @@ -26,11 +27,18 @@ public void setup() {
public void shouldFullyMapModelAResponseToRequest() throws Exception {

final var age = Period.between(LocalDate.of(1990, 3, 14), LocalDate.now()).getYears();


final var names = new ArrayList<String>();

names.add("piet");
names.add("jan");
names.add("henk");

final ModelA modelA = ModelA.builder()
.id(1)
.isActive(true)
.valid(true)
.names(names)
.firstName("Remco")
.lastName("van der Heijden")
.birthday(LocalDate.of(1990, 3, 14))
Expand All @@ -42,6 +50,7 @@ public void shouldFullyMapModelAResponseToRequest() throws Exception {
final ModelAResponse modelAResponse = modelMapper.map(modelA, ModelAResponse.class);

Assert.assertEquals(1, modelAResponse.getId());
Assert.assertEquals(3, modelAResponse.getUpperCaseNames().size());
Assert.assertTrue(modelAResponse.isActive());
Assert.assertTrue(modelAResponse.isValid());
Assert.assertEquals("Remco", modelAResponse.getFirstName());
Expand Down
3 changes: 2 additions & 1 deletion src/test/java/eye2web/modelmapper/mapper/AgeValueMapper.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package eye2web.modelmapper.mapper;

import eye2web.modelmapper.ModelMapperI;
import eye2web.modelmapper.model.FromField;
import eye2web.modelmapper.value.map.ValueMapper;

Expand All @@ -9,7 +10,7 @@
public class AgeValueMapper implements ValueMapper {

@Override
public Object mapToValue(final FromField fromField) {
public Object mapToValue(final FromField fromField, final ModelMapperI modelMapper) {

if (!fromField.containsValue())
return 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class ConcatMultiValueMapper implements MultiValueMapper {

@Override
public Object mapToValue(final Set<FromField> fromFieldSet) {

return fromFieldSet.stream()
.filter(FromField::containsValue)
.filter(field -> field.getType().equals(String.class))
Expand Down

0 comments on commit 9c8f11b

Please sign in to comment.