de.skuzzle.test.snapshots.normalize.MethodObjectMembers Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of snapshot-tests-normalize Show documentation
Show all versions of snapshot-tests-normalize Show documentation
Travers/Clean/Normalize object instances
package de.skuzzle.test.snapshots.normalize;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Provides reflection based {@link ObjectMember} implementation that consists of a
* matching pair of a getter and a setter methods. Getter and Setter methods are matched
* using an implementation of {@link PropertyConventions}. A default implementation is
* provided which follows the well known java beans convention for naming properties.
*
* @author Simon Taddiken
*/
class MethodObjectMembers implements ObjectMembers {
private final Function conventionsConstructor;
private MethodObjectMembers(Function conventionsConstructor) {
this.conventionsConstructor = Objects.requireNonNull(conventionsConstructor,
"conventionsConstructor must not be null");
}
public static ObjectMembers usingJavaBeansConventions() {
return new MethodObjectMembers(JavaBeansPropertyConventions::new);
}
public static ObjectMembers using(Function conventionsConstructor) {
return new MethodObjectMembers(conventionsConstructor);
}
@Override
public Stream directMembersOf(Object root, Object collectionParent, VisitorContext visitorContext) {
final Class extends Object> valueType = root.getClass();
return Reflection.superClassHierarchy(valueType)
.map(Class::getDeclaredMethods)
.flatMap(Arrays::stream)
.filter(method -> !Modifier.isStatic(method.getModifiers()))
.map(conventionsConstructor)
.filter(PropertyConventions::includeInTraversal)
.collect(Collectors.groupingBy(PropertyConventions::canonicalName,
toMethodObjectMember(root, collectionParent)))
.values().stream()
.map(ObjectMember.class::cast);
}
public interface PropertyConventions {
Method method();
boolean isGetter();
boolean isSetter();
default boolean includeInTraversal() {
return isGetter() || isSetter();
}
String canonicalName();
}
private static Collector toMethodObjectMember(Object root,
Object collectionParent) {
final class GetterAndSetter {
private String canonicalName;
private Method getter;
private Method setter;
}
return Collector.of(GetterAndSetter::new,
(getterAndSetter, property) -> {
getterAndSetter.canonicalName = property.canonicalName();
if (property.isGetter())
getterAndSetter.getter = property.method();
else
getterAndSetter.setter = property.method();
},
(gas1, gas2) -> gas1,
gas -> new MethodObjectMember(gas.canonicalName, root, collectionParent, gas.getter, gas.setter));
}
private static class JavaBeansPropertyConventions implements PropertyConventions {
private final Method method;
public JavaBeansPropertyConventions(Method method) {
this.method = method;
}
@Override
public Method method() {
return this.method;
}
@Override
public boolean isGetter() {
final var name = method.getName();
return method.getParameterCount() == 0 &&
(name.startsWith("get") && name.length() > 3 && !name.equals("getClass") ||
name.startsWith("is") && name.length() > 2);
}
@Override
public boolean isSetter() {
final var name = method.getName();
return method.getParameterCount() == 1 &&
name.startsWith("set") && name.length() > 3;
}
private int prefixLength() {
final var name = method.getName();
return name.startsWith("is")
? 2
: 3;
}
@Override
public String canonicalName() {
return lowerCaseFirstChar(method.getName().substring(prefixLength()));
}
private String lowerCaseFirstChar(String s) {
final StringBuilder builder = new StringBuilder(s);
builder.setCharAt(0, Character.toLowerCase(s.charAt(0)));
return builder.toString();
}
}
private static final class MethodObjectMember implements ObjectMember {
private final String name;
private final Object parent;
private final Object collectionParent;
// At least one of these are non-null
private final Method getter;
private final Method setter;
private MethodObjectMember(String name, Object parent, Object collectionParent, Method getter, Method setter) {
this.name = Objects.requireNonNull(name, "member name must not be null");
this.parent = Objects.requireNonNull(parent, "member's parent must not be null");
this.collectionParent = collectionParent;
this.getter = getter;
this.setter = setter;
}
@Override
public Object parent() {
return parent;
}
@Override
public Optional
© 2015 - 2025 Weber Informatics LLC | Privacy Policy