Skip to content

Commit

Permalink
Fixed #10 Implemented custom mappers
Browse files Browse the repository at this point in the history
  • Loading branch information
JoanZapata committed Dec 4, 2013
1 parent ead84c3 commit d7a7a62
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 49 deletions.
4 changes: 4 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 1.0.4

* Added custom mappers

# 1.0.3

* Managed Set
Expand Down
29 changes: 20 additions & 9 deletions src/main/java/com/joanzapata/mapper/CustomMapperWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,30 @@ class CustomMapperWrapper<S, D> {
this.customMapper = customMapper;
}

public D apply(Object source, Class destination) {
for (Method method : customMapper.getClass().getMethods()) {
public boolean isApplicable(Object source, Class destination) {
final Method[] methods = customMapper.getClass().getMethods();

for (Method method : methods) {
if ("map".equals(method.getName())) {
Class<?>[] parameterTypes = method.getParameterTypes();
Class<?> sourceClass = parameterTypes[0];
Class<?> destinationClass = method.getReturnType();
if (sourceClass.isAssignableFrom(source.getClass()) &&
destinationClass.isAssignableFrom(destination)) {
return applySafe((S) source);
Class<?> parameterType = method.getParameterTypes()[0];
Class<?> returnType = method.getReturnType();
// Ignore Object because it's too large
if (parameterType != Object.class && returnType != Object.class &&
// Parameter type of the user function will be assigned the source object
parameterType.isAssignableFrom(source.getClass()) &&
// The destination object will be assigned the object returned from user function
destination.isAssignableFrom(returnType)) {
return true;
}
}
}
return null;
return false;
}

public D apply(S source, Class<D> destination) {
if (isApplicable(source, destination)) {
return applySafe((S) source);
} else return null;
}

private D applySafe(S source) {
Expand Down
20 changes: 19 additions & 1 deletion src/main/java/com/joanzapata/mapper/Mapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ CD mapCollection(CU source, Class<D> destinationClass, MappingContext context) {
} else return null;
}
for (Object s : source) {
out.add(nominalMap(s, destinationClass, context));
final D mappedElement = nominalMap(s, destinationClass, context);
if (mappedElement != null) out.add(mappedElement);
}
return out;
}
Expand Down Expand Up @@ -229,6 +230,10 @@ private <D> D nominalMap(Object source, Type field, Class<D> destinationClass, M
return alreadyMapped;
}

// Try to find appropriate customMapper if any
CustomMapperResult<D> customMapperResult = MapperUtil.applyCustomMappers(customMappers, source, destinationClass);
if (customMapperResult.hasMatched) return customMapperResult.result;

// Map native types if possible
D nativeMapped = mapPrimitiveTypeOrNull(source);
if (nativeMapped != null) {
Expand Down Expand Up @@ -311,4 +316,17 @@ private <D> D nominalMap(Object source, Type field, Class<D> destinationClass, M
return destinationInstance;
}

public static class CustomMapperResult<T> {
boolean hasMatched = false;
T result = null;

// Success
public CustomMapperResult(T result) {
this.result = result;
this.hasMatched = true;
}

// Fail
public CustomMapperResult() {}
}
}
27 changes: 16 additions & 11 deletions src/main/java/com/joanzapata/mapper/MapperUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,11 @@

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

final class MapperUtil {

public static final Collection createCollectionLike(Collection source) {
return new ArrayList();
}
import static com.joanzapata.mapper.Mapper.CustomMapperResult;

public static final Map createMapLike(Map source) {
return new HashMap();
}
final class MapperUtil {

/**
* Find a getter on the source object for the given setter name.
Expand Down Expand Up @@ -153,4 +144,18 @@ public static void applyHooks(List<HookWrapper> hooks, Object source, Object des
hook.apply(source, destination);
}
}

@SuppressWarnings("unchecked")
public static <D> CustomMapperResult<D> applyCustomMappers(List<CustomMapperWrapper> customMappers, Object source, Class<D> destinationClass) {
for (CustomMapperWrapper customMapper : customMappers) {
if (customMapper.isApplicable(source, destinationClass)) {
// Mapped has applied
final D destination = (D) customMapper.apply(source, destinationClass);
return new CustomMapperResult(destination);
}
}
// Mapped has not applied
return new CustomMapperResult();

}
}
59 changes: 31 additions & 28 deletions src/test/java/com/joanzapata/mapper/MapperTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,24 @@ public class MapperTest {

@Test
public void singleObjectWithCustomMapperToNull() {
Mapper mapper = new Mapper().customMapper(new CustomMapperToNull<Book, BookDTO>());
Mapper mapper = new Mapper().customMapper(new CustomMapper<Book, BookDTO>() {
@Override
public BookDTO map(Book source) {
return null;
}
});
Book book = new Book(5L, "Book");
assertNull(mapper.map(book, BookDTO.class));
}

@Test
public void objectListWithCustomMapperToNull() {
Mapper mapper = new Mapper().customMapper(new CustomMapperToNull<Book, BookDTO>());
Mapper mapper = new Mapper().customMapper(new CustomMapper<Book, BookDTO>() {
@Override
public BookDTO map(Book source) {
return null;
}
});
Book b1 = new Book(1L, "Book1");
Book b2 = new Book(2L, "Book2");
Book b3 = new Book(3L, "Book3");
Expand All @@ -66,15 +76,29 @@ public void objectListWithCustomMapperToNull() {

@Test
public void singleObjectWithCustomMapperToFixed() {
Mapper mapper = new Mapper().customMapper(new CustomMapperToFixed<Book>("Fixed"));
Mapper mapper = new Mapper().customMapper(new CustomMapper<Book, BookDTO>() {
@Override
public BookDTO map(Book source) {
final BookDTO bookDTO = new BookDTO();
bookDTO.setName("Fixed");
return bookDTO;
}
});
Book book = new Book(5L, "Book");
final BookDTO bookDto = mapper.map(book, BookDTO.class);
assertEquals("Fixed", bookDto.getName());
}

@Test
public void objectListWithCustomMapperToFixed() {
Mapper mapper = new Mapper().customMapper(new CustomMapperToFixed<Book>("Fixed"));
Mapper mapper = new Mapper().customMapper(new CustomMapper<Book, BookDTO>() {
@Override
public BookDTO map(Book source) {
final BookDTO bookDTO = new BookDTO();
bookDTO.setName("Fixed");
return bookDTO;
}
});
Book b1 = new Book(1L, "Book1");
Book b2 = new Book(2L, "Book2");
Book b3 = new Book(3L, "Book3");
Expand Down Expand Up @@ -129,14 +153,14 @@ public void inheritanceWithUncalledCustomMapper() {
Mapper mapper = new Mapper()
.mapping(AddressEntry.class, AddressEntryDTO.class)
.mapping(PhoneEntry.class, PhoneEntryDTO.class)
.customMapper(new CustomMapper<PhoneEntry, AddressEntryDTO>() {
.customMapper(new CustomMapper<PhoneEntry, Object>() {
@Override
public AddressEntryDTO map(PhoneEntry source) {
public Object map(PhoneEntry source) {
fail("Shouldn't call this mapper");
return null;
}
});
BookDTO bookDTO = mapper.map(book, BookDTO.class);
mapper.map(book, BookDTO.class);
}

@Test
Expand Down Expand Up @@ -481,25 +505,4 @@ public void setTestOtherDTO(String testOtherDTO) {
}
}

public static class CustomMapperToNull<S, D> implements CustomMapper<S, D> {
@Override
public D map(S source) {
return null;
}
}

public class CustomMapperToFixed<S> implements CustomMapper<S, BookDTO> {
private String fixed;

public CustomMapperToFixed(String fixed) {
this.fixed = fixed;
}

@Override
public BookDTO map(S source) {
final BookDTO bookDTO = new BookDTO();
bookDTO.setName(fixed);
return bookDTO;
}
}
}

0 comments on commit d7a7a62

Please sign in to comment.