Skip to content

Instantly share code, notes, and snippets.

@OpenSrcerer
Last active April 29, 2023 13:55
Show Gist options
  • Save OpenSrcerer/0dfd9829a454d96088d925d67f29c2d7 to your computer and use it in GitHub Desktop.
Save OpenSrcerer/0dfd9829a454d96088d925d67f29c2d7 to your computer and use it in GitHub Desktop.
A mapper made in Java that moves contents of the source DTO to the target, if a method with the name of the source or a field with the name of the source field exists.
/*
So what does this code do? Here's an example:
A.java
class A {
int x = 3;
int y = 4;
}
B.java
class B {
int x;
int z;
int setY(int y) {
this.z = y;
}
}
Main.java
public class Main {
public static void main(String[] args) {
A a = new A();
B b = DtoMapper.mapTo(a, new B());
System.out.println(b.x); // 3
System.out.println(b.z); // 4
}
}
*/
import java.lang.reflect.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
public abstract class DtoMapper {
public static <S, T> T mapTo(S source, T target) throws InvocationTargetException, IllegalAccessException {
Map<String, Method> targetMethods = toMemberMap(target.getClass().getDeclaredMethods());
Map<String, Field>
originFields = toMemberMap(source.getClass().getDeclaredFields()),
targetFields = toMemberMap(target.getClass().getDeclaredFields());
Map<Field, Method> fieldsMappedByMethods = toMemberMap(
originFields,
field -> targetMethods.containsKey("set" + field.getName().toLowerCase()),
(acc, field) -> acc.put(field, targetMethods.get("set" + field.getName().toLowerCase()))
);
Map<Field, Field> directlyMappedFields = toMemberMap(
originFields,
field -> targetFields.containsKey(field.getName().toLowerCase()) &&
!fieldsMappedByMethods.containsKey(field),
(acc, field) -> acc.put(field, targetFields.get(field.getName().toLowerCase()))
);
// Map fields mapped by methods
populateFields(source, target, fieldsMappedByMethods.entrySet());
populateFields(source, target, directlyMappedFields.entrySet());
return target;
}
private static <
S,
T,
X extends AccessibleObject & Member,
Y extends AccessibleObject & Member
>
void populateFields(S source, T target, Set<Map.Entry<X, Y>> entries) throws
IllegalAccessException, InvocationTargetException
{
for (Map.Entry<X, Y> entry : entries) {
var originMember = entry.getKey();
var targetMember = entry.getValue();
// Make elements accessible
originMember.setAccessible(true);
targetMember.setAccessible(true);
// Set the fields through method or direct access
if (targetMember instanceof Method) {
((Method) targetMember).invoke(target, ((Field) originMember).get(source));
} else {
((Field) targetMember).set(target, ((Field) originMember).get(source));
}
}
}
private static <X extends Member> Map<String, X> toMemberMap(X[] xes) {
return Arrays.stream(xes)
.collect(HashMap::new, (acc, x) -> acc.put(x.getName().toLowerCase(), x), HashMap::putAll);
}
private static <X extends Member, Y extends Member> Map<X, Y> toMemberMap(
Map<String, X> xOrigin,
Predicate<X> xPredicate,
BiConsumer<HashMap<X, Y>, ? super X> accumulator
) {
return xOrigin.values().stream()
.filter(xPredicate)
.collect(HashMap::new, accumulator, HashMap::putAll);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment