Last active
April 29, 2023 13:55
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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