package com.codepoetics.phantompojo.impl;
import com.codepoetics.navn.Name;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.AbstractMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public final class MethodSet {
public static MethodSet forClasses(Class<?> phantomClass, Class<?> builderClass) {
Map<String, Method> getterMethodsByName = gettersByName(phantomClass);
Map<String, List<Method>> builderMethodsByName = buildersByName(builderClass);
verifyMatchingFieldNames(getterMethodsByName.keySet(), builderMethodsByName.keySet());
return new MethodSet(getterMethodsByName, builderMethodsByName);
}
private static void verifyMatchingFieldNames(Set<String> getterFieldNames, Set<String> builderFieldNames) {
if (!getterFieldNames.equals(builderFieldNames)) {
throw new IllegalArgumentException(
String.format("Mismatch between pojo fields %s and builder fields %s",
getterFieldNames, builderFieldNames));
}
}
private static Map<String, Method> gettersByName(Class<?> phantomClass) {
return getBuilderAndGetterMethods(phantomClass)
.collect(Collectors.toMap(
MethodSet::getFieldName,
Function.identity()));
}
private static Stream<Method> getBuilderAndGetterMethods(Class<?> klass) {
return Stream.of(klass.getDeclaredMethods())
.filter(m -> !Modifier.isStatic(m.getModifiers()) && !m.isDefault());
}
private static Map<String, List<Method>> buildersByName(Class<?> builderClass) {
return getBuilderAndGetterMethods(builderClass)
.collect(Collectors.groupingBy(MethodSet::getFieldName));
}
private static String getFieldName(Method method) {
return Name.of(method.getName()).withoutFirst().toCamelCase();
}
private final Map<String, Method> getterMethodsByName;
private final Map<String, List<Method>> builderMethodsByName;
private MethodSet(Map<String, Method> getterMethodsByName, Map<String, List<Method>> builderMethodsByName) {
this.getterMethodsByName = getterMethodsByName;
this.builderMethodsByName = builderMethodsByName;
}
public PojoProperties getPojoProperties() {
return PojoProperties.forGetters(getterMethodsByName);
}
public MethodIndexLookup getMethodIndexLookup(Map<String, Integer> nameIndices) {
return MethodIndexLookup.create(getReadIndices(nameIndices), getWriteIndices(nameIndices));
}
private Map<Method, Integer> getWriteIndices(Map<String, Integer> nameIndices) {
return builderMethodsByName.entrySet().stream()
.flatMap(e -> e.getValue().stream().map(m -> new AbstractMap.SimpleEntry<>(e.getKey(), m)))
.collect(Collectors.toMap(Map.Entry::getValue, e -> nameIndices.get(e.getKey())));
}
private Map<Method, Integer> getReadIndices(Map<String, Integer> nameIndices) {
return getterMethodsByName.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getValue, e -> nameIndices.get(e.getKey())));
}
}