Skip to content

Commit

Permalink
Add default transformer to field mapper (#98)
Browse files Browse the repository at this point in the history
  • Loading branch information
Idane committed May 10, 2021
1 parent dab6a8e commit ab57e02
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import com.antelopesystem.crudframework.fieldmapper.transformer.base.FieldTransformer;
import com.antelopesystem.crudframework.utils.utils.ReflectionUtils;
import kotlin.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.util.*;
Expand All @@ -15,10 +17,14 @@

public class FieldMapper {

private Logger log = LoggerFactory.getLogger(getClass());

private Map<String, FieldTransformer> fieldTransformersByRef = new HashMap<>();

private Map<Class<? extends FieldTransformer>, FieldTransformer> fieldTransformersByType = new HashMap<>();

private Map<Pair<Class<?>, Class<?>>, FieldTransformer> defaultTransformers = new HashMap();

private static Map<Pair<Class<?>, Class<?>>, EntityStructureDTO> entityStructures = new HashMap<>();

private static Map<Class<?>, Map<String, Field>> entityFieldMaps = new HashMap<>();
Expand All @@ -28,10 +34,16 @@ public class FieldMapper {
public void registerTransformer(String ref, FieldTransformer transformer) {
fieldTransformersByRef.put(ref, transformer);
fieldTransformersByType.put(transformer.getClass(), transformer);
if(transformer.isDefault()) {
registerDefaultTransformer(transformer);
}
}

public void registerTransformer(Class<? extends FieldTransformer> clazz, FieldTransformer transformer) {
fieldTransformersByType.put(clazz, transformer);
if(transformer.isDefault()) {
registerDefaultTransformer(transformer);
}
}

public <T> T processMappedFields(Object object, Class<T> toClazz) {
Expand Down Expand Up @@ -94,7 +106,7 @@ private void processMappedField(MappedField annotation, Object fromObject, Objec
toPath = toPath.isEmpty() ? fromPair.getField().getName() : toPath;
ObjectFieldPair toPair = getFieldByPath(toPath, toObject, SourceType.TO);

FieldTransformer transformer = getTransformer(annotation);
FieldTransformer transformer = getTransformer(annotation, fromPair, toPair);
mapField(fromPair, toPair, transformer);
}

Expand Down Expand Up @@ -147,19 +159,36 @@ private void mapField(ObjectFieldPair fromPair, ObjectFieldPair toPair, FieldTra
try {
ReflectionUtils.setField(toPair.getField(), toPair.getObject(), value);
} catch(Exception e) {
throw new RuntimeException("Could not map value " + fromPair.getField().getName() + " of class " + fromPair.getObject().getClass().getSimpleName() + " to " + toPair.getField().getName() + " of class" + toPair.getObject().getClass().getSimpleName());
IllegalStateException newException = new IllegalStateException("Could not map value " + fromPair.getField().getName() + " of class " + fromPair.getObject().getClass().getSimpleName() + " to " + toPair.getField().getName() + " of class" + toPair.getObject().getClass().getSimpleName());
newException.initCause(e);
throw newException;
}
}
}

private FieldTransformer getTransformer(MappedField annotation) {
private FieldTransformer getTransformer(MappedField annotation, ObjectFieldPair fromPair, ObjectFieldPair toPair) {
Pair transformationPair = new Pair(fromPair.getField().getType(), toPair.getField().getType());
log.trace("Attempting to find transformer for transformation pair [ " + transformationPair + " ]");
FieldTransformer transformer = null;
log.trace("Checking transformerRef field");
if(!annotation.transformerRef().isEmpty()) {
log.trace("transformerRef is not empty with value [ " + annotation.transformerRef() + " ]");
transformer = fieldTransformersByRef.get(annotation.transformerRef());
log.trace("Found transformer of type [ " + transformer.getClass().getName() + " ]");
}

if(transformer == null) {
log.trace("Checking transformer field");
if(annotation.transformer() == DefaultTransformer.class) {
log.trace("Transformer is DefaultTransformer, attempting to find a default transformer");
Pair key = new Pair(fromPair.getField().getType(), toPair.getField().getType());

FieldTransformer defaultTransformer = defaultTransformers.get(key);
if(defaultTransformer != null) {
log.trace("Found a default transformer of type [ " + defaultTransformer.getClass().getName() + " ]");
return defaultTransformer;
}

return null;
}

Expand Down Expand Up @@ -260,6 +289,18 @@ private boolean isOfType(Class<?> defaultFromClass, Class<?> fromClass, Class<?>
return fromClass.isAssignableFrom(toClass);
}

private void registerDefaultTransformer(FieldTransformer transformer) {
if(transformer.isDefault()) {
Pair key = new Pair<>(transformer.fromType(), transformer.toType());
FieldTransformer existing = defaultTransformers.get(key);
if(existing != null) {
throw new IllegalStateException("Cannot register default transformer for pair [ " + key + " ] - already registered by [ " + existing.getClass().getName() + " ]");
}

defaultTransformers.put(key, transformer);
}
}

private enum SourceType {
TO, FROM
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import java.util.Date;

public class DateToLongTransformer extends FieldTransformerBase<Date, Long> {

@Override
protected Long innerTransform(Field fromField, Field toField, Date originalValue, Object fromObject, Object toObject) {
return originalValue == null ? null : originalValue.getTime();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
package com.antelopesystem.crudframework.fieldmapper.transformer.base;

import org.springframework.core.GenericTypeResolver;

import java.lang.reflect.Field;

public interface FieldTransformer<FromType, ToType> {

/**
* Whether or not the transformer is considered default for the [FromType]-[ToType] pair
*/
default boolean isDefault() {
return false;
}

default Class<FromType> fromType() {
return (java.lang.Class<FromType>) GenericTypeResolver.resolveTypeArguments(getClass(), FieldTransformer.class)[0];
}

default Class<ToType> toType() {
return (java.lang.Class<ToType>) GenericTypeResolver.resolveTypeArguments(getClass(), FieldTransformer.class)[1];
}

ToType transform(Field fromField, Field toField, FromType originalValue, Object fromObject, Object toObject);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.antelopesystem.crudframework.fieldmapper.transformer

import com.antelopesystem.crudframework.fieldmapper.FieldMapper
import com.antelopesystem.crudframework.fieldmapper.annotation.DefaultMappingTarget
import com.antelopesystem.crudframework.fieldmapper.annotation.MappedField
import com.antelopesystem.crudframework.fieldmapper.annotation.MappedFields
import org.junit.Assert
import org.junit.Test
import java.lang.IllegalStateException
import java.util.*

class FieldMapperTests {

@Test(expected = IllegalStateException::class)
fun `verify exception is thrown if a default transformer pair is registered twice`() {
val fieldMapper = FieldMapper()
val defaultTransformer = object : DateToLongTransformer() {
override fun isDefault(): Boolean {
return true
}
}
fieldMapper
.registerTransformer(defaultTransformer::class.java, defaultTransformer)
fieldMapper
.registerTransformer(defaultTransformer::class.java, defaultTransformer)
}

@Test
internal fun `test implicit default transformer`() {
val fieldMapper = FieldMapper()
val defaultTransformer = object : DateToLongTransformer() {
override fun isDefault(): Boolean {
return true
}
}
fieldMapper
.registerTransformer(defaultTransformer::class.java, defaultTransformer)

val sourceClass = ExampleSourceClass(Date(1))
val destinationClass = ExampleDestinationClass()
fieldMapper.processMappedFields(sourceClass, destinationClass)

Assert.assertEquals(1L, destinationClass.dateAsLong)
}
}

@DefaultMappingTarget(ExampleDestinationClass::class)
private class ExampleSourceClass(
@MappedField(mapTo = "dateAsLong")
var date: Date
)

private class ExampleDestinationClass {
var dateAsLong: Long? = null
}

0 comments on commit ab57e02

Please sign in to comment.