new name, new functionality

This commit is contained in:
Shautvast 2023-07-26 17:34:55 +02:00
parent 8a8dc9d9de
commit 4d4ebe9c7f
36 changed files with 904 additions and 533 deletions

View file

@ -1,8 +1,15 @@
# Apples # reflective
Utility classes that use ASM for generating metaclasses as if it were standard java reflection, but without the performance overhead.
__nl.sander.reflective.compare.Compare__
* universal (deep) compare tool * universal (deep) compare tool
* compares [apple] to [orange] recursively and shows the diff * compares [apple] to [orange] recursively and shows the diff
* no reflection * no reflection
* compiles to bytecode version jdk11 * compiles to bytecode version jdk11
* but also handles records, if you run jdk16+ * but also handles records, if you run jdk16+
* Can optionally do 'structural comparison' (as opposed to _nominal_ like in the respective types of polymorphism). Let's say you have class Apple with property _color_ and a class Orange, also with property _color_. `Compare` provides `any` method with which you can compare the values disregarding the type that contains them.
__nl.sander.reflective.tomap.ToMap__
* turn any bean/record in a Map<String, Object>
* I have one more wish for this and that is 'structural comparison'. Let's say you have class Apple with property _color_ and a class Orange, also with property _color_. Right now `Apples` does the sensible thing, and that is saying: "classes don't match". But what what if you could compare these apples and oranges? Should be possible.

View file

@ -3,10 +3,10 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>nl.sander</groupId> <groupId>nl.sander</groupId>
<artifactId>Apples</artifactId> <artifactId>reflective</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<name>Comparator</name> <name>reflective</name>
<description>Comparator</description> <description>Reflective utils that don't use java.lang.reflect</description>
<dependencies> <dependencies>
<dependency> <dependency>

View file

@ -1,6 +0,0 @@
package nl.sander.apples;
public abstract class BaseApple {
public abstract Result compare(Object apple, Object orange);
}

View file

@ -0,0 +1,6 @@
package nl.sander.reflective.compare;
public abstract class AbstractComparator {
public abstract Result compare(Object apple, Object orange);
}

View file

@ -1,5 +1,6 @@
package nl.sander.apples; package nl.sander.reflective.compare;
import nl.sander.reflective.java.Java;
import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.tree.*; import org.objectweb.asm.tree.*;
@ -9,16 +10,14 @@ import java.util.UUID;
import static org.objectweb.asm.Opcodes.*; import static org.objectweb.asm.Opcodes.*;
class AppleFactory extends ClassVisitor { class ComparatorFactory extends ClassVisitor {
public static final String SUPER = javaName(BaseApple.class.getName()); public static final String SUPER = Java.internalName(AbstractComparator.class);
public static final String INIT = "<init>";
public static final String ZERO_ARGS_VOID = "()V";
private boolean isRecord = false; private boolean isRecord = false;
public AppleFactory() { public ComparatorFactory() {
super(ASM9); super(ASM9);
} }
@ -40,52 +39,41 @@ class AppleFactory extends ClassVisitor {
classNode.superName = SUPER; classNode.superName = SUPER;
classNode.version = V11; classNode.version = V11;
classNode.access = ACC_PUBLIC; classNode.access = ACC_PUBLIC;
MethodNode constructor = new MethodNode(ACC_PUBLIC, INIT, ZERO_ARGS_VOID, null, null); MethodNode constructor = new MethodNode(ACC_PUBLIC, Java.INIT, Java.ZERO_ARGS_VOID, null, null);
constructor.instructions.add(new VarInsnNode(ALOAD, 0)); constructor.instructions.add(new VarInsnNode(ALOAD, 0));
constructor.instructions.add(new MethodInsnNode(INVOKESPECIAL, SUPER, INIT, ZERO_ARGS_VOID)); constructor.instructions.add(new MethodInsnNode(INVOKESPECIAL, SUPER, Java.INIT, Java.ZERO_ARGS_VOID));
constructor.instructions.add(new InsnNode(RETURN)); constructor.instructions.add(new InsnNode(RETURN));
classNode.methods.add(constructor); classNode.methods.add(constructor);
compareMethod = new MethodNode(ACC_PUBLIC, compareMethod = new MethodNode(ACC_PUBLIC,
"compare", "(Ljava/lang/Object;Ljava/lang/Object;)Lnl/sander/apples/Result;", null, null); "compare", "(Ljava/lang/Object;Ljava/lang/Object;)L" + Java.internalName(Result.class) + ";", null, null);
classNode.methods.add(compareMethod); classNode.methods.add(compareMethod);
add(new VarInsnNode(ALOAD, 0)); add(new VarInsnNode(ALOAD, 0));
} }
@Override
public MethodVisitor visitMethod(int access, String methodname, public MethodVisitor visitMethod(int access, String methodname,
String desc, String signature, String[] exceptions) { String desc, String signature, String[] exceptions) {
if (!hasArgs(desc) && access == Modifier.PUBLIC && isRecord || if (!Java.hasArgs(desc) && access == Modifier.PUBLIC && isRecord ||
(methodname.startsWith("get") || (methodname.startsWith("is")) && desc.equals("()Z"))) { (methodname.startsWith("get") || (methodname.startsWith("is")) && desc.equals("()Z"))) {
visitGetter(methodname, asProperty(methodname, isRecord), getReturnType(desc)); visitGetter(methodname, Java.asProperty(methodname, isRecord), Java.getReturnType(desc));
} }
return null; return null;
} }
private String asProperty(String getter, boolean isRecord) {
if (isRecord){
return getter;
} else {
if (getter.startsWith("get")){
return getter.substring(3,4).toLowerCase()+getter.substring(4);
} else {
return getter.substring(2,3).toLowerCase()+getter.substring(3);
}
}
}
private void visitGetter(String getterMethodName, String propertyName, String returnType) { private void visitGetter(String getterMethodName, String propertyName, String returnType) {
add(new LdcInsnNode(propertyName)); add(new LdcInsnNode(propertyName));
add(new VarInsnNode(ALOAD, 1)); add(new VarInsnNode(ALOAD, 1));
add(new TypeInsnNode(CHECKCAST, javaName(classToMap))); add(new TypeInsnNode(CHECKCAST, Java.internalName(classToMap)));
add(new MethodInsnNode(INVOKEVIRTUAL, classToMap, getterMethodName, "()" + returnType)); add(new MethodInsnNode(INVOKEVIRTUAL, classToMap, getterMethodName, "()" + returnType));
add(new VarInsnNode(ALOAD, 2)); add(new VarInsnNode(ALOAD, 2));
add(new TypeInsnNode(CHECKCAST, javaName(classToMap))); add(new TypeInsnNode(CHECKCAST, Java.internalName(classToMap)));
add(new MethodInsnNode(INVOKEVIRTUAL, classToMap, getterMethodName, "()" + returnType)); add(new MethodInsnNode(INVOKEVIRTUAL, classToMap, getterMethodName, "()" + returnType));
add(new MethodInsnNode(INVOKESTATIC, "nl/sander/apples/Apples", "compare", "(" add(new MethodInsnNode(INVOKESTATIC, Java.internalName(Compare.class), "compare", "("
+ getSignature(returnType) + getSignature(returnType)
+ ")Lnl/sander/apples/Result;")); + ")L" + Java.internalName(Result.class) + ";"));
add(new VarInsnNode(ASTORE, 3 + (localVarIndex++))); add(new VarInsnNode(ASTORE, 3 + (localVarIndex++)));
} }
@ -96,7 +84,7 @@ class AppleFactory extends ClassVisitor {
} else { } else {
type = returnType; type = returnType;
} }
return "Ljava/lang/String;"+type + type; return "Ljava/lang/String;" + type + type;
} }
@Override @Override
@ -106,7 +94,7 @@ class AppleFactory extends ClassVisitor {
} else { } else {
add(new LdcInsnNode(localVarIndex)); add(new LdcInsnNode(localVarIndex));
} }
add(new TypeInsnNode(ANEWARRAY, "nl/sander/apples/Result")); add(new TypeInsnNode(ANEWARRAY, Java.internalName(Result.class)));
for (int i = 0; i < localVarIndex; i++) { for (int i = 0; i < localVarIndex; i++) {
add(new InsnNode(DUP)); add(new InsnNode(DUP));
@ -119,7 +107,7 @@ class AppleFactory extends ClassVisitor {
add(new InsnNode(AASTORE)); add(new InsnNode(AASTORE));
} }
add(new MethodInsnNode(INVOKESTATIC, "nl/sander/apples/Result", "merge", "([Lnl/sander/apples/Result;)Lnl/sander/apples/Result;")); add(new MethodInsnNode(INVOKESTATIC, Java.internalName(Result.class), "merge", "([L" + Java.internalName(Result.class) + ";)L" + Java.internalName(Result.class) + ";"));
add(new InsnNode(ARETURN)); add(new InsnNode(ARETURN));
} }
@ -127,16 +115,5 @@ class AppleFactory extends ClassVisitor {
compareMethod.instructions.add(ins); compareMethod.instructions.add(ins);
} }
private String getReturnType(String desc) {
return desc.substring(2);
}
private boolean hasArgs(String desc) {
return desc.charAt(1) != ')';
}
private static String javaName(String className) {
return className.replaceAll("\\.", "/");
}
} }

View file

@ -1,97 +1,167 @@
package nl.sander.apples; package nl.sander.reflective.compare;
import nl.sander.reflective.java.ByteClassLoader;
import nl.sander.reflective.java.Java;
import nl.sander.reflective.tomap.ToMap;
import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter; import org.objectweb.asm.ClassWriter;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.LongAdder; import java.util.concurrent.atomic.LongAdder;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@SuppressWarnings({"unchecked", "rawtypes"}) @SuppressWarnings({"unchecked", "rawtypes"})
public class Apples { /**
* Deep (recursive) comparison of two objects
* - floating point comparison with optional precision
* - objects of same type or differing types, allowing comparison of types that have the same data or subsets
*
* In case of maps or differing object types, every item in apple is expected in orange, so apple can be subset of orange.
*/
public class Compare {
private final static Map<Character, String> CHAR_ESCAPES = Map.of('\t', "\\t", '\b', "\\b", '\n', "\\n", '\r', "\\r", '\f', "\\f", '\\', "\\\\"); private final static Map<Character, String> CHAR_ESCAPES = Map.of('\t', "\\t", '\b', "\\b", '\n', "\\n", '\r', "\\r", '\f', "\\f", '\\', "\\\\");
private static final ByteClassLoader generatedClassesLoader = new ByteClassLoader(); private final static ConcurrentMap<Class<?>, AbstractComparator> cache = new ConcurrentHashMap<>();
/**
* Compares two objects. They are assumed to be of the same type, otherwise that is the difference
*
* @param apple first object for comparison
* @param orange second object for comparison
* @return A Result object with the outcome and some details
*
* throws nothing but Result can wrap an exception instead of an actual comparison (rust style exception handling)
*/
public static Result compare(Object apple, Object orange) { public static Result compare(Object apple, Object orange) {
return compare("", apple, orange); return compare("", apple, orange, false);
} }
public static Result compare(String property, Object apple, Object orange) { /**
if (apple == null) { * Compare two objects. They are not assumed to be of the same type.
return Result.from(property, orange == null, "null != " + asString(orange)); *
} * @param apple first object for comparison
* @param orange second object for comparison
* @return A Result object with the outcome and some details
* throws nothing but Result can wrap an exception instead of an actual comparison (rust style exception handling)
*/
public static Result any(Object apple, Object orange) {
return compare("", apple, orange, true);
}
if (orange == null) { /**
return Result.unequal(property, asString(apple) + " != null"); * Version of same compare method where you can specify a common property for the two objects to compare.
} * This is primarily useful for doing bean/record property comparison.
*
* @param property common property (key) for apple and orange
* @param apple first object for comparison
* @param orange second object for comparison
* @return A Result object with the outcome and some details
* throws nothing but Result can wrap an exception instead of an actual comparison (rust style exception handling)
*/
public static Result compare(String property, Object apple, Object orange) {
return compare(property, apple, orange, false);
}
if (apple == orange) {
return Result.SAME;
}
if (apple.getClass() != orange.getClass()) { static Result compare(String property, Object apple, Object orange, boolean allowDifferingTypes) {
return Result.unequal(property, asString(apple) + " != " + asString(orange));
}
if (apple instanceof String) {
return Result.from(property, apple.equals(orange), () -> asString(apple) + " != " + asString(orange));
}
if (apple instanceof Number) {
return Result.from(property, apple.equals(orange), () -> apple + " != " + orange);
}
if (apple instanceof Collection) {
return compareCollections(property, (Collection<?>) apple, (Collection<?>) orange);
}
if (apple instanceof Map) {
return compareMaps(property, (Map<?, ?>) apple, (Map<?, ?>) orange);
}
if (apple instanceof Comparable<?>) {
int comparison = ((Comparable) apple).compareTo(orange);
if (comparison == 0) {
return new Result(true, List.of());
} else {
return Result.from(property, false, apple + " != " + orange);
}
}
try { try {
ClassReader cr = new ClassReader(apple.getClass().getName()); if (apple == null) {
AppleFactory appleFactory = new AppleFactory(); return Result.from(property, orange == null, "null != " + asString(orange));
cr.accept(appleFactory, ClassReader.SKIP_FRAMES); }
if (orange == null) {
return Result.unequal(property, asString(apple) + " != null");
}
if (apple == orange) {
return Result.SAME;
}
if (apple.getClass() != orange.getClass()) {
if (allowDifferingTypes) {
// convert objects to maps and compare their keys/values
return compareMaps(property, ToMap.map(apple), (Map<?, ?>) ToMap.map(orange), allowDifferingTypes);
} else {
return Result.unequal(property, asString(apple) + " != " + asString(orange));
}
}
if (apple instanceof String) {
return Result.from(property, apple.equals(orange), () -> asString(apple) + " != " + asString(orange));
}
if (apple instanceof Number) {
return Result.from(property, apple.equals(orange), () -> apple + " != " + orange);
}
if (apple instanceof Collection) {
return compareCollections(property, (Collection<?>) apple, (Collection<?>) orange, allowDifferingTypes);
}
if (apple instanceof Map) {
return compareMaps(property, (Map<?, ?>) apple, (Map<?, ?>) orange, allowDifferingTypes);
}
if (apple instanceof Comparable<?>) {
int comparison = ((Comparable) apple).compareTo(orange);
if (comparison == 0) {
return new Result(true, List.of());
} else {
return Result.from(property, false, apple + " != " + orange);
}
}
return cache.computeIfAbsent(apple.getClass(), k -> createComparator(apple))
.compare(apple, orange);
} catch (Exception e) {
return Result.error(e.getCause());
}
}
private static AbstractComparator createComparator(Object apple) {
try {
ClassReader cr = Java.getClassReader(apple);
ComparatorFactory comparatorFactory = new ComparatorFactory();
cr.accept(comparatorFactory, ClassReader.SKIP_FRAMES);
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES); ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
appleFactory.classNode.accept(classWriter); comparatorFactory.classNode.accept(classWriter);
byte[] byteArray = classWriter.toByteArray(); byte[] byteArray = classWriter.toByteArray();
generatedClassesLoader.addClass(appleFactory.classNode.name, byteArray); ByteClassLoader.INSTANCE.addClass(comparatorFactory.classNode.name, byteArray);
BaseApple baseApple = (BaseApple) generatedClassesLoader.loadClass(appleFactory.classNode.name).getConstructor().newInstance(); return (AbstractComparator) ByteClassLoader.INSTANCE.loadClass(comparatorFactory.classNode.name).getConstructor().newInstance();
return baseApple.compare(apple, orange);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
private static Result compareCollections(String property, Collection<?> apple, Collection<?> orange) { private static Result compareCollections(String property, Collection<?> apple, Collection<?> orange, boolean allowDifferingTypes) {
List<String> diffs = List<String> diffs =
zipAndEnumerate(apple, orange) zipAndEnumerate(apple, orange)
.map(t -> new Tuple4<Integer, Object, Object, Result>(t.e1, apple, orange, Apples.compare(property, t.e2, t.e3))) .map(t -> {
try {
return new Tuple4<Integer, Object, Object, Result>(t.e1, apple, orange, Compare.compare(property, t.e2, t.e3, allowDifferingTypes));
} catch (Exception e) {
throw new RuntimeException(e);//meh
}
})
.filter(t -> !t.e4.areEqual()) .filter(t -> !t.e4.areEqual())
.map(t -> property + "[" + t.e1 + "]:" + t.e2 + " != " + t.e3) .map(t -> property + "[" + t.e1 + "]:" + t.e2 + " != " + t.e3)
.collect(Collectors.toList()); .collect(Collectors.toList());
return new Result(!diffs.isEmpty(), diffs); return new Result(!diffs.isEmpty(), diffs);
} }
private static Result compareMaps(String property, Map<?, ?> apple, Map<?, ?> orange) { private static Result compareMaps(String property, Map<?, ?> apple, Map<?, ?> orange, boolean allowDifferingTypes) throws Exception {
List<String> diffs = new ArrayList<>(); List<String> diffs = new ArrayList<>();
for (Map.Entry<?, ?> appleEntry : apple.entrySet()) { for (Map.Entry<?, ?> appleEntry : apple.entrySet()) {
Object appleValue = appleEntry.getValue(); Object appleValue = appleEntry.getValue();
Object orangeValue = orange.get(appleEntry.getKey()); Object orangeValue = orange.get(appleEntry.getKey());
Result result = Apples.compare(property + "[" + appleEntry.getKey() + "]", appleValue, orangeValue); Result result = Compare.compare(property + "[" + appleEntry.getKey() + "]", appleValue, orangeValue, allowDifferingTypes);
if (!result.areEqual()) { if (!result.areEqual()) {
diffs.addAll(result.getDiffs()); diffs.addAll(result.getDiffs());
} }

View file

@ -1,4 +1,4 @@
package nl.sander.apples; package nl.sander.reflective.compare;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -6,18 +6,30 @@ import java.util.Objects;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@SuppressWarnings("unused") // used by generated code
public class Result { public class Result {
private final boolean areEqual; private final boolean areEqual;
private final List<String> diffs; private final List<String> diffs;
private final Throwable e;
public Result(boolean areEqual, List<String> diffs) { public Result(boolean areEqual, List<String> diffs) {
this.areEqual = areEqual; this.areEqual = areEqual;
this.diffs = diffs; this.diffs = diffs;
this.e = null;
} }
public static Result SAME = new Result(true, List.of()); public static Result SAME = new Result(true, List.of());
public Result(Throwable e) {
this.areEqual = false;//meh
this.diffs = null;
this.e = e;
}
public static Result error(Throwable e) {
return new Result(e);
}
public static Result from(String property, boolean areEqual, String message) { public static Result from(String property, boolean areEqual, String message) {
if (!areEqual) { if (!areEqual) {
if (property.length() > 0) { if (property.length() > 0) {

View file

@ -1,11 +1,17 @@
package nl.sander.apples; package nl.sander.reflective.java;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
class ByteClassLoader extends ClassLoader { /*
* common util not for external use
*
* Loads the class into the jvm, after the user has generated some bytecode
*/
public class ByteClassLoader extends ClassLoader {
private final ConcurrentMap<String, Class<?>> classes = new ConcurrentHashMap<>(); private final ConcurrentMap<String, Class<?>> classes = new ConcurrentHashMap<>();
public final static ByteClassLoader INSTANCE = new ByteClassLoader();
@Override @Override
protected Class<?> findClass(String name) throws ClassNotFoundException { protected Class<?> findClass(String name) throws ClassNotFoundException {

View file

@ -0,0 +1,51 @@
package nl.sander.reflective.java;
import org.objectweb.asm.ClassReader;
import java.io.IOException;
/*
* common utils not for external use
*/
public class Java {
public static final String INIT = "<init>";
public static final String ZERO_ARGS_VOID = "()V";
public static final String OBJECT = "Ljava/lang/Object;";
public static String internalName(String className) {
return className.replaceAll("\\.", "/");
}
public static String internalName(Class<?> type) {
return internalName(type.getName());
}
public static boolean hasArgs(String desc) {
return desc.charAt(1) != ')';
}
public static String asProperty(String getter, boolean isRecord) {
if (isRecord) {
return getter;
} else {
if (getter.startsWith("get")) {
return getter.substring(3, 4).toLowerCase() + getter.substring(4);
} else {
return getter.substring(2, 3).toLowerCase() + getter.substring(3);
}
}
}
public static String getReturnType(String desc) {
return desc.substring(2);
}
public static ClassReader getClassReader(Object value) throws ClassNotFoundException {
ClassReader cr = null;
try {
cr = new ClassReader(value.getClass().getName());
} catch (IOException e) {
throw new ClassNotFoundException(value.getClass().getName());
}
return cr;
}
}

View file

@ -0,0 +1,37 @@
package nl.sander.reflective.tomap;
import java.util.HashMap;
import java.util.Map;
public abstract class AbstractToMap {
public abstract Map<String, Object> toMap(Object object);
protected void add(HashMap<String, Object> m, String key, byte b) {
m.put(key, b);
}
protected void add(HashMap<String, Object> m, String key, short s) {
m.put(key, s);
}
protected void add(HashMap<String, Object> m, String key, int i) {
m.put(key, i);
}
protected void add(HashMap<String, Object> m, String key, boolean b) {
m.put(key, b);
}
protected void add(HashMap<String, Object> m, String key, float f) {
m.put(key, f);
}
protected void add(HashMap<String, Object> m, String key, double b) {
m.put(key, b);
}
protected void add(HashMap<String, Object> m, String key, Object o) {
m.put(key, o);
}
}

View file

@ -0,0 +1,50 @@
package nl.sander.reflective.tomap;
import nl.sander.reflective.java.ByteClassLoader;
import nl.sander.reflective.java.Java;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Turns any class (beans with getters/setters and records) into a HashMap.
*/
public class ToMap {
private static final ConcurrentMap<Class<?>, AbstractToMap> cache = new ConcurrentHashMap<>();
/**
* @param value some bean or record (so use wisely if the class is not of that pattern)
* @return Map<String, Object>
* @throws Exception if the class that you want to mappify somehow can't be read from the classpath
*/
public static Map<String, Object> map(Object value) throws Exception {
try {
return cache.computeIfAbsent(value.getClass(), k ->
createNew(value)).toMap(value);
} catch (RuntimeException e) {
throw new Exception(e.getCause());
}
}
private static AbstractToMap createNew(Object value) {
try{
ClassReader cr = Java.getClassReader(value);
ToMapFactory factory = new ToMapFactory();
cr.accept(factory, ClassReader.SKIP_FRAMES);
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
factory.classNode.accept(classWriter);
byte[] byteArray = classWriter.toByteArray();
ByteClassLoader.INSTANCE.addClass(factory.classNode.name, byteArray);
return (AbstractToMap) ByteClassLoader.INSTANCE.loadClass(factory.classNode.name).getConstructor().newInstance();}
catch (Exception e){
throw new RuntimeException(e);
}
}
}

View file

@ -0,0 +1,89 @@
package nl.sander.reflective.tomap;
import nl.sander.reflective.java.Java;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.tree.*;
import java.lang.reflect.Modifier;
import java.util.UUID;
import static org.objectweb.asm.Opcodes.*;
class ToMapFactory extends ClassVisitor {
public static final String SUPER_NAME = Java.internalName(AbstractToMap.class.getName());
private boolean isRecord = false;
final ClassNode classNode = new ClassNode();
private String classToMap;
private MethodNode mappifyMethod;
public ToMapFactory() {
super(ASM9);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
if (superName.equals("java/lang/Record")) {
isRecord = true;
}
this.classToMap = name;
classNode.name = "ToMap" + UUID.randomUUID();
classNode.superName = SUPER_NAME;
classNode.version = V11;
classNode.access = ACC_PUBLIC;
MethodNode constructor = new MethodNode(ACC_PUBLIC, Java.INIT, Java.ZERO_ARGS_VOID, null, null);
constructor.instructions.add(new VarInsnNode(ALOAD, 0));
constructor.instructions.add(new MethodInsnNode(INVOKESPECIAL, SUPER_NAME, Java.INIT, Java.ZERO_ARGS_VOID));
constructor.instructions.add(new InsnNode(RETURN));
classNode.methods.add(constructor);
mappifyMethod = new MethodNode(ACC_PUBLIC,
"toMap", "(Ljava/lang/Object;)Ljava/util/Map;", null, null);
classNode.methods.add(mappifyMethod);
add(new TypeInsnNode(NEW, "java/util/HashMap"));
add(new InsnNode(DUP));
add(new MethodInsnNode(INVOKESPECIAL, "java/util/HashMap", Java.INIT, Java.ZERO_ARGS_VOID));
add(new VarInsnNode(ASTORE, 2));
}
@Override
public MethodVisitor visitMethod(int access, String methodname,
String desc, String signature, String[] exceptions) {
if (!Java.hasArgs(desc) && access == Modifier.PUBLIC && isRecord ||
(methodname.startsWith("get") || (methodname.startsWith("is")) && desc.equals("()Z"))) {
visitGetter(methodname, Java.asProperty(methodname, isRecord), Java.getReturnType(desc));
}
return null;
}
private void visitGetter(String getterMethodName, String propertyName, String returnType) {
add(new VarInsnNode(ALOAD, 0));
add(new VarInsnNode(ALOAD, 2));
add(new LdcInsnNode(propertyName));
add(new VarInsnNode(ALOAD, 1));
add(new TypeInsnNode(CHECKCAST, Java.internalName(classToMap)));
add(new MethodInsnNode(INVOKEVIRTUAL, classToMap, getterMethodName, "()" + returnType));
add(new MethodInsnNode(INVOKEVIRTUAL, classNode.name, "add", "(Ljava/util/HashMap;Ljava/lang/String;"+translate(returnType)+")V"));
}
private String translate(String typeDesc){
if (typeDesc.startsWith("L")){
return Java.OBJECT;
} else {
return typeDesc;
}
}
@Override
public void visitEnd() {
add(new VarInsnNode(ALOAD, 2));
add(new InsnNode(ARETURN));
}
private void add(AbstractInsnNode ins) {
mappifyMethod.instructions.add(ins);
}
}

View file

@ -0,0 +1,15 @@
import nl.sander.reflective.tomap.AbstractToMap;
import nl.sander.reflective.compare.PlumBean;
import java.util.HashMap;
import java.util.Map;
public class ExampleMappifier extends AbstractToMap {
public Map<String, Object> toMap(Object o) {
HashMap<String, Object> m = new HashMap<>();
add(m, "core", ((PlumBean) o).getCore());
add(m, "number", ((PlumBean) o).getNumber());
return m;
}
}

View file

@ -1,63 +0,0 @@
package nl.sander.apples;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class BytesTest {
@Test
void nilEqualsNil() {
assertEquals(Result.SAME, Apples.compare((byte) 0, (byte) 0));
}
@Test
void nilNotEqualsSome() {
assertEquals(Result.unequal("0 != 1"), Apples.compare((byte) 0, (byte) 1));
}
@Test
void OneNotEqualsNil() {
assertEquals(Result.unequal("1 != 0"), Apples.compare((byte) 1, (byte) 0));
}
@Test
void nilByteNotEqualsOne() {
assertEquals(Result.unequal("java.lang.Byte: 0 != 1"), Apples.compare(Byte.valueOf((byte) 0), (byte) 1));
}
@Test
void nilNotEqualsOneByte() {
assertEquals(Result.unequal("0 != java.lang.Byte: 1"), Apples.compare((byte) 0, Byte.valueOf((byte) 1)));
}
@Test
void nullNotEqualsNilByte() {
assertEquals(Result.unequal("null != java.lang.Byte: 0"), Apples.compare(null, Byte.valueOf((byte) 0)));
}
@Test
void nullNotEqualsNil() {
assertEquals(Result.unequal("null != 0"), Apples.compare(null, (byte) 0));
}
@Test
void nilByteNotEqualsNull() {
assertEquals(Result.unequal("java.lang.Byte: 0 != null"), Apples.compare(Byte.valueOf((byte) 0), null));
}
@Test
void nilNotEqualsNull() {
assertEquals(Result.unequal("0 != null"), Apples.compare((byte) 0, null));
}
@Test
void byteNotEqualsString() {
assertEquals(Result.unequal("0 != \"true\""), Apples.compare((byte) 0, "true"));
}
@Test
void StringNotEqualsByte() {
assertEquals(Result.unequal("\"false\" != 0"), Apples.compare("false", (byte) 0));
}
}

View file

@ -1,63 +0,0 @@
package nl.sander.apples;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class CharsTest {
@Test
void nilEqualsNil() {
assertTrue(Apples.compare((char) 0, (char) 0).areEqual());
}
@Test
void nilNotEqualsSome() {
assertEquals(Result.unequal("'A' != 'B'"),Apples.compare('A', 'B'));
}
@Test
void SomeNotEqualsNil() {
assertEquals(Result.unequal("'\\u0001' != '\\u0000'"),Apples.compare((char) 1, (char) 0));
}
@Test
void charEqualsCharacter() {
assertTrue(Apples.compare(Character.valueOf('X'), 'X').areEqual());
}
@Test
void nilNotEqualsOneCharacter() {
assertEquals(Result.unequal("'\\u0000' != '\\u0001'"),Apples.compare((char) 0, Character.valueOf((char) 1)));
}
@Test
void nullNotEqualsNilCharacter() {
assertEquals(Result.unequal("null != '\\u0000'"),Apples.compare(null, Character.valueOf((char) 0)));
}
@Test
void nullNotEqualsChar() {
assertEquals(Result.unequal("null != '\\u0000'"),Apples.compare(null, (char) 0));
}
@Test
void nilCharacterNotEqualsNull() {
assertEquals(Result.unequal("'\\u0000' != null"),Apples.compare(Character.valueOf((char) 0), null));
}
@Test
void charNotEqualsNull() {
assertEquals(Result.unequal("'\\u0000' != null"),Apples.compare((char) 0, null));
}
@Test
void charNotEqualsString() {
assertEquals(Result.unequal("'\\u0000' != \"true\""),Apples.compare((char)0, "true"));
}
@Test
void StringNotEqualsChar() {
assertEquals(Result.unequal("\"false\" != '\\u0000'"),Apples.compare("false", (char)0));
}
}

View file

@ -1,89 +0,0 @@
package nl.sander.apples;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
@SuppressWarnings("ConstantValue")
class FloatsTest {
@Test
void floatImprecisionLeft() {
assertTrue(Apples.compare(2 / 3F, 0.66F, 2).areEqual(), (2 / 3F) + " != " + 0.66F);
}
@Test
void floatImprecisionRight() {
assertTrue(Apples.compare(0.66F, 2 / 3F, 2).areEqual(), (2 / 3F) + " != " + 0.66F);
}
@Test
void nilFEqualsNilF() {
assertTrue(Apples.compare(0F, 0F).areEqual());
}
@Test
void nilFEqualsNilFloat() {
assertTrue(Apples.compare(0F, Float.valueOf(0)).areEqual());
}
@Test
void nilFEqualsNilL() {
assertTrue(Apples.compare(0F, 0L).areEqual());
}
@Test
void nilEqualsNilF() {
assertTrue(Apples.compare(0L, 0F).areEqual());
}
@Test
void nullNotEqualsSome() {
assertEquals(Result.unequal("0.0 != 1.0"), Apples.compare(0F, 1F));
}
@Test
void SomeNotEqualsNull() {
assertEquals(Result.unequal("1.0 != 0.0"), Apples.compare(1F, 0F));
}
@Test
void nilByteNotEqualsOne() {
assertEquals(Result.unequal("java.lang.Float: 0.0 != 1.0"), Apples.compare(Float.valueOf(0), 1F));
}
@Test
void nilNotEqualsOneByte() {
assertEquals(Result.unequal("0.0 != java.lang.Float: 1.0"), Apples.compare(0F, Float.valueOf(1)));
}
@Test
void nullNotEqualsNilFloat() {
assertEquals(Result.unequal("null != java.lang.Float: 0.0"), Apples.compare(null, Float.valueOf(0)));
}
@Test
void nullNotEqualsNil() {
assertEquals(Result.unequal("null != 0.0"), Apples.compare(null, 0F));
}
@Test
void nilFloatNotEqualsNull() {
assertEquals(Result.unequal("java.lang.Float: 0.0 != null"), Apples.compare(Float.valueOf(0), null));
}
@Test
void nilNotEqualsNull() {
assertEquals(Result.unequal("0.0 != null"), Apples.compare(0F, null));
}
@Test
void floatNotEqualsString() {
assertEquals(Result.unequal("0.0 != \"true\""), Apples.compare(0F, "true"));
}
@Test
void StringNotEqualsFloat() {
assertEquals(Result.unequal("\"false\" != 0.0"), Apples.compare("false", 0F));
}
}

View file

@ -1,68 +0,0 @@
package nl.sander.apples;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class LongsTest {
@Test
void nullEqualsNull() {
assertEquals(Result.SAME, Apples.compare(0, 0));
}
@Test
void nullNotEqualsSome() {
assertEquals(Result.unequal("0 != 1"), Apples.compare(0, 1));
}
@Test
void SomeNotEqualsNull() {
assertEquals(Result.unequal("1 != 0"), Apples.compare(1, 0));
}
@Test
void nilLongNotEqualsOne() {
assertEquals(Result.unequal("java.lang.Long: 0 != 1"), Apples.compare(Long.valueOf(0), 1));
}
@Test
void nilNotEqualsOneLong() {
assertEquals(Result.unequal("0 != java.lang.Long: 1"), Apples.compare(0, Long.valueOf(1)));
}
@Test
void nilEqualsNilLong() {
assertTrue(Apples.compare(0, Long.valueOf(0)).areEqual());
}
@Test
void nullNotEqualsNilLong() {
assertEquals(Result.unequal("null != java.lang.Long: 0"), Apples.compare(null, Long.valueOf(0)));
}
@Test
void nullNotEqualsNil() {
assertEquals(Result.unequal("null != 0"), Apples.compare(null, 0));
}
@Test
void nilLongNotEqualsNull() {
assertEquals(Result.unequal("java.lang.Long: 0 != null"), Apples.compare(Long.valueOf(0), null));
}
@Test
void nilNotEqualsNull() {
assertEquals(Result.unequal("0 != null"), Apples.compare(0, null));
}
@Test
void longNotEqualsString() {
assertEquals(Result.unequal("0 != \"0\""), Apples.compare(0, "0"));
}
@Test
void StringNotEqualsLong() {
assertEquals(Result.unequal("\"false\" != 0"), Apples.compare("false", 0));
}
}

View file

@ -1,73 +0,0 @@
package nl.sander.apples;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class ShortsTest {
@Test
void nullEqualsNull() {
assertTrue(Apples.compare((short) 0, (short) 0).areEqual());
}
@Test
void nullEqualsNullShort() {
assertTrue(Apples.compare((short) 0, Short.valueOf((short) 0)).areEqual());
}
@Test
void nullShortEqualsNull() {
assertTrue(Apples.compare(Short.valueOf((short) 0), (short) 0).areEqual());
}
@Test
void nullNotEqualsSome() {
assertEquals(Result.unequal("0 != 1"), Apples.compare((short) 0, (short) 1));
}
@Test
void SomeNotEqualsNull() {
assertEquals(Result.unequal("1 != 0"), Apples.compare((short) 1, (short) 0));
}
@Test
void nilshortNotEqualsOne() {
assertEquals(Result.unequal("java.lang.Short: 0 != 1"), Apples.compare(Short.valueOf((short) 0), (short) 1));
}
@Test
void nilNotEqualsOneshort() {
assertEquals(Result.unequal("0 != java.lang.Short: 1"), Apples.compare((short) 0, Short.valueOf((short) 1)));
}
@Test
void nullNotEqualsNilshort() {
assertEquals(Result.unequal("null != java.lang.Short: 0"), Apples.compare(null, Short.valueOf((short) 0)));
}
@Test
void nullNotEqualsNil() {
assertEquals(Result.unequal("null != 0"), Apples.compare(null, (short) 0));
}
@Test
void nilshortNotEqualsNull() {
assertEquals(Result.unequal("java.lang.Short: 0 != null"), Apples.compare(Short.valueOf((short) 0), null));
}
@Test
void nilNotEqualsNull() {
assertEquals(Result.unequal("0 != null"), Apples.compare((short) 0, null));
}
@Test
void shortNotEqualsString() {
assertEquals(Result.unequal("0 != \"true\""), Apples.compare((short) 0, "true"));
}
@Test
void StringNotEqualsshort() {
assertEquals(Result.unequal("\"false\" != 0"), Apples.compare("false", (short) 0));
}
}

View file

@ -1,4 +1,4 @@
package nl.sander.apples; package nl.sander.reflective.compare;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -11,7 +11,7 @@ public class BeansTest {
@Test @Test
void testBeans() { void testBeans() {
Result comparison = Apples.compare(new PlumBean("small", "red", true, 1, 1.0F, Storage.HIGH, (byte) 1, List.of(new Shop("tesco"))), Result comparison = Compare.compare(new PlumBean("small", "red", true, 1, 1.0F, Storage.HIGH, (byte) 1, List.of(new Shop("tesco"))),
new PlumBean("large", "green", true, 1, 1.0F, Storage.LOW, (byte) 1, List.of(new Shop("asda")))); new PlumBean("large", "green", true, 1, 1.0F, Storage.LOW, (byte) 1, List.of(new Shop("asda"))));
assertFalse(comparison.areEqual()); assertFalse(comparison.areEqual());

View file

@ -1,4 +1,4 @@
package nl.sander.apples; package nl.sander.reflective.compare;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -8,103 +8,103 @@ class BooleansTest {
@Test @Test
void falseEqualsFalse() { void falseEqualsFalse() {
assertTrue(Apples.compare(false, false).areEqual()); assertTrue(Compare.compare(false, false).areEqual());
} }
@Test @Test
void falseEqualsBooleanFalse() { void falseEqualsBooleanFalse() {
assertTrue(Apples.compare(false, Boolean.FALSE).areEqual()); assertTrue(Compare.compare(false, Boolean.FALSE).areEqual());
} }
@Test @Test
void trueEqualsBooleanTrue() { void trueEqualsBooleanTrue() {
assertTrue(Apples.compare(true, Boolean.TRUE).areEqual()); assertTrue(Compare.compare(true, Boolean.TRUE).areEqual());
} }
@Test @Test
void trueEqualsTrue() { void trueEqualsTrue() {
assertTrue(Apples.compare(true, true).areEqual()); assertTrue(Compare.compare(true, true).areEqual());
} }
@Test @Test
void falseNotEqualsTrue() { void falseNotEqualsTrue() {
assertEquals(Result.unequal("false != true"), Apples.compare(false, true)); assertEquals(Result.unequal("false != true"), Compare.compare(false, true));
} }
@Test @Test
void trueNotEqualsFalse() { void trueNotEqualsFalse() {
assertEquals(Result.unequal("true != false"), Apples.compare(true, false)); assertEquals(Result.unequal("true != false"), Compare.compare(true, false));
} }
@Test @Test
void falseNotEqualsTrueBoolean() { void falseNotEqualsTrueBoolean() {
assertEquals(Result.unequal("false != java.lang.Boolean: true"), Apples.compare(false, Boolean.valueOf(true))); assertEquals(Result.unequal("false != java.lang.Boolean: true"), Compare.compare(false, Boolean.valueOf(true)));
} }
@Test @Test
void trueNotEqualsFalseBoolean() { void trueNotEqualsFalseBoolean() {
assertEquals(Result.unequal("true != java.lang.Boolean: false"), Apples.compare(true, Boolean.valueOf(false))); assertEquals(Result.unequal("true != java.lang.Boolean: false"), Compare.compare(true, Boolean.valueOf(false)));
} }
@Test @Test
void falseBooleanNotEqualsTrue() { void falseBooleanNotEqualsTrue() {
assertEquals(Result.unequal("java.lang.Boolean: false != true"), Apples.compare(Boolean.valueOf(false), true)); assertEquals(Result.unequal("java.lang.Boolean: false != true"), Compare.compare(Boolean.valueOf(false), true));
} }
@Test @Test
void trueBooleanNotEqualsFalse() { void trueBooleanNotEqualsFalse() {
assertEquals(Result.unequal("java.lang.Boolean: true != false"), Apples.compare(Boolean.valueOf(true), false)); assertEquals(Result.unequal("java.lang.Boolean: true != false"), Compare.compare(Boolean.valueOf(true), false));
} }
@Test @Test
void nullNotEqualsTrue() { void nullNotEqualsTrue() {
assertEquals(Result.unequal("null != true"), Apples.compare(null, true)); assertEquals(Result.unequal("null != true"), Compare.compare(null, true));
} }
@Test @Test
void nullNotEqualsFalse() { void nullNotEqualsFalse() {
assertEquals(Result.unequal("null != false"), Apples.compare(null, false)); assertEquals(Result.unequal("null != false"), Compare.compare(null, false));
} }
@Test @Test
void trueNotEqualsNull() { void trueNotEqualsNull() {
assertEquals(Result.unequal("true != null"), Apples.compare(true, null)); assertEquals(Result.unequal("true != null"), Compare.compare(true, null));
} }
@Test @Test
void falseNotEqualsNull() { void falseNotEqualsNull() {
assertEquals(Result.unequal("false != null"), Apples.compare(false, null)); assertEquals(Result.unequal("false != null"), Compare.compare(false, null));
} }
@Test @Test
void trueNotEqualsString() { void trueNotEqualsString() {
assertEquals(Result.unequal("true != \"true\""), Apples.compare(true, "true")); assertEquals(Result.unequal("true != \"true\""), Compare.compare(true, "true"));
} }
@Test @Test
void StringNotEqualsFalse() { void StringNotEqualsFalse() {
assertEquals(Result.unequal("\"false\" != false"), Apples.compare("false", false)); assertEquals(Result.unequal("\"false\" != false"), Compare.compare("false", false));
} }
@Test @Test
void nullNotEqualsTrueBoolean() { void nullNotEqualsTrueBoolean() {
assertEquals(Result.unequal("null != java.lang.Boolean: true"), Apples.compare(null, Boolean.TRUE)); assertEquals(Result.unequal("null != java.lang.Boolean: true"), Compare.compare(null, Boolean.TRUE));
} }
@Test @Test
void nullNotEqualsFalseBoolean() { void nullNotEqualsFalseBoolean() {
assertEquals(Result.unequal("null != java.lang.Boolean: false"), Apples.compare(null, Boolean.FALSE)); assertEquals(Result.unequal("null != java.lang.Boolean: false"), Compare.compare(null, Boolean.FALSE));
} }
@Test @Test
void trueBooleanNotEqualsNull() { void trueBooleanNotEqualsNull() {
assertEquals(Result.unequal("java.lang.Boolean: true != null"), Apples.compare(Boolean.TRUE, null)); assertEquals(Result.unequal("java.lang.Boolean: true != null"), Compare.compare(Boolean.TRUE, null));
} }
@Test @Test
void falseBooleanNotEqualsNull() { void falseBooleanNotEqualsNull() {
assertEquals(Result.unequal("java.lang.Boolean: false != null"), Apples.compare(Boolean.FALSE, null)); assertEquals(Result.unequal("java.lang.Boolean: false != null"), Compare.compare(Boolean.FALSE, null));
} }

View file

@ -0,0 +1,63 @@
package nl.sander.reflective.compare;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class BytesTest {
@Test
void nilEqualsNil() {
assertEquals(Result.SAME, Compare.compare((byte) 0, (byte) 0));
}
@Test
void nilNotEqualsSome() {
assertEquals(Result.unequal("0 != 1"), Compare.compare((byte) 0, (byte) 1));
}
@Test
void OneNotEqualsNil() {
assertEquals(Result.unequal("1 != 0"), Compare.compare((byte) 1, (byte) 0));
}
@Test
void nilByteNotEqualsOne() {
assertEquals(Result.unequal("java.lang.Byte: 0 != 1"), Compare.compare(Byte.valueOf((byte) 0), (byte) 1));
}
@Test
void nilNotEqualsOneByte() {
assertEquals(Result.unequal("0 != java.lang.Byte: 1"), Compare.compare((byte) 0, Byte.valueOf((byte) 1)));
}
@Test
void nullNotEqualsNilByte() {
assertEquals(Result.unequal("null != java.lang.Byte: 0"), Compare.compare(null, Byte.valueOf((byte) 0)));
}
@Test
void nullNotEqualsNil() {
assertEquals(Result.unequal("null != 0"), Compare.compare(null, (byte) 0));
}
@Test
void nilByteNotEqualsNull() {
assertEquals(Result.unequal("java.lang.Byte: 0 != null"), Compare.compare(Byte.valueOf((byte) 0), null));
}
@Test
void nilNotEqualsNull() {
assertEquals(Result.unequal("0 != null"), Compare.compare((byte) 0, null));
}
@Test
void byteNotEqualsString() {
assertEquals(Result.unequal("0 != \"true\""), Compare.compare((byte) 0, "true"));
}
@Test
void StringNotEqualsByte() {
assertEquals(Result.unequal("\"false\" != 0"), Compare.compare("false", (byte) 0));
}
}

View file

@ -0,0 +1,63 @@
package nl.sander.reflective.compare;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class CharsTest {
@Test
void nilEqualsNil() {
assertTrue(Compare.compare((char) 0, (char) 0).areEqual());
}
@Test
void nilNotEqualsSome() {
assertEquals(Result.unequal("'A' != 'B'"), Compare.compare('A', 'B'));
}
@Test
void SomeNotEqualsNil() {
assertEquals(Result.unequal("'\\u0001' != '\\u0000'"), Compare.compare((char) 1, (char) 0));
}
@Test
void charEqualsCharacter() {
assertTrue(Compare.compare(Character.valueOf('X'), 'X').areEqual());
}
@Test
void nilNotEqualsOneCharacter() {
assertEquals(Result.unequal("'\\u0000' != '\\u0001'"), Compare.compare((char) 0, Character.valueOf((char) 1)));
}
@Test
void nullNotEqualsNilCharacter() {
assertEquals(Result.unequal("null != '\\u0000'"), Compare.compare(null, Character.valueOf((char) 0)));
}
@Test
void nullNotEqualsChar() {
assertEquals(Result.unequal("null != '\\u0000'"), Compare.compare(null, (char) 0));
}
@Test
void nilCharacterNotEqualsNull() {
assertEquals(Result.unequal("'\\u0000' != null"), Compare.compare(Character.valueOf((char) 0), null));
}
@Test
void charNotEqualsNull() {
assertEquals(Result.unequal("'\\u0000' != null"), Compare.compare((char) 0, null));
}
@Test
void charNotEqualsString() {
assertEquals(Result.unequal("'\\u0000' != \"true\""), Compare.compare((char) 0, "true"));
}
@Test
void StringNotEqualsChar() {
assertEquals(Result.unequal("\"false\" != '\\u0000'"), Compare.compare("false", (char) 0));
}
}

View file

@ -0,0 +1,28 @@
package nl.sander.reflective.compare;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class DifferentTypesTest {
@Test
void testAppleAndOrange() {
assertTrue(Compare.any(new Apple("orange"), new Orange("orange")).areEqual());
}
class Apple {
final String color;
Apple(String color) {
this.color = color;
}
}
class Orange {
final String color;
Orange(String color) {
this.color = color;
}
}
}

View file

@ -1,4 +1,4 @@
package nl.sander.apples; package nl.sander.reflective.compare;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -8,77 +8,77 @@ class DoublesTest {
@Test @Test
void floatImprecisionLeft() { void floatImprecisionLeft() {
assertTrue(Apples.compare(2 / 3.0, 0.66, 2).areEqual(), (2 / 3) + " != " + 0.66); assertTrue(Compare.compare(2 / 3.0, 0.66, 2).areEqual(), (2 / 3) + " != " + 0.66);
} }
@Test @Test
void floatImprecisionRight() { void floatImprecisionRight() {
assertTrue(Apples.compare(0.66, 2 / 3D, 2).areEqual(), (2 / 3D) + " != " + 0.66); assertTrue(Compare.compare(0.66, 2 / 3D, 2).areEqual(), (2 / 3D) + " != " + 0.66);
} }
@Test @Test
void nilFEqualsNilF() { void nilFEqualsNilF() {
assertTrue(Apples.compare(0.0, .0).areEqual()); assertTrue(Compare.compare(0.0, .0).areEqual());
} }
@Test @Test
void nilFEqualsNilL() { void nilFEqualsNilL() {
assertTrue(Apples.compare(0.0, 0L).areEqual()); assertTrue(Compare.compare(0.0, 0L).areEqual());
} }
@Test @Test
void nilEqualsNilF() { void nilEqualsNilF() {
assertTrue(Apples.compare(0L, 0.0).areEqual()); assertTrue(Compare.compare(0L, 0.0).areEqual());
} }
@Test @Test
void nullNotEqualsSome() { void nullNotEqualsSome() {
assertEquals(Result.unequal("0.0 != 1.0"), Apples.compare(0.0, 1.0F)); assertEquals(Result.unequal("0.0 != 1.0"), Compare.compare(0.0, 1.0F));
} }
@Test @Test
void SomeNotEqualsNull() { void SomeNotEqualsNull() {
assertEquals(Result.unequal("1.0 != 0.0"), Apples.compare(1.0, .0)); assertEquals(Result.unequal("1.0 != 0.0"), Compare.compare(1.0, .0));
} }
@Test @Test
void nilDoubleNotEqualsOne() { void nilDoubleNotEqualsOne() {
assertEquals(Result.unequal("java.lang.Double: 0.0 != 1.0"), Apples.compare(Double.valueOf(0), 1F)); assertEquals(Result.unequal("java.lang.Double: 0.0 != 1.0"), Compare.compare(Double.valueOf(0), 1F));
} }
@Test @Test
void nilNotEqualsOneDouble() { void nilNotEqualsOneDouble() {
assertEquals(Result.unequal("0.0 != java.lang.Double: 1.0"), Apples.compare(0F, Double.valueOf(1))); assertEquals(Result.unequal("0.0 != java.lang.Double: 1.0"), Compare.compare(0F, Double.valueOf(1)));
} }
@Test @Test
void nullNotEqualsNilDouble() { void nullNotEqualsNilDouble() {
assertEquals(Result.unequal("null != java.lang.Double: 0.0"), Apples.compare(null, Double.valueOf(0))); assertEquals(Result.unequal("null != java.lang.Double: 0.0"), Compare.compare(null, Double.valueOf(0)));
} }
@Test @Test
void nullNotEqualsNil() { void nullNotEqualsNil() {
assertEquals(Result.unequal("null != 0.0"), Apples.compare(null, .0)); assertEquals(Result.unequal("null != 0.0"), Compare.compare(null, .0));
} }
@Test @Test
void nilDoubleNotEqualsNull() { void nilDoubleNotEqualsNull() {
assertEquals(Result.unequal("java.lang.Double: 0.0 != null"), Apples.compare(Double.valueOf(0), null)); assertEquals(Result.unequal("java.lang.Double: 0.0 != null"), Compare.compare(Double.valueOf(0), null));
} }
@Test @Test
void nilNotEqualsNull() { void nilNotEqualsNull() {
assertEquals(Result.unequal("0.0 != null"), Apples.compare(.0, null)); assertEquals(Result.unequal("0.0 != null"), Compare.compare(.0, null));
} }
@Test @Test
void doubleNotEqualsString() { void doubleNotEqualsString() {
assertEquals(Result.unequal("0.0 != \"0\""), Apples.compare(.0, "0")); assertEquals(Result.unequal("0.0 != \"0\""), Compare.compare(.0, "0"));
} }
@Test @Test
void StringNotEqualsDouble() { void StringNotEqualsDouble() {
assertEquals(Result.unequal("\"0\" != 0.0"), Apples.compare("0", .0)); assertEquals(Result.unequal("\"0\" != 0.0"), Compare.compare("0", .0));
} }
} }

View file

@ -0,0 +1,88 @@
package nl.sander.reflective.compare;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class FloatsTest {
@Test
void floatImprecisionLeft() {
assertTrue(Compare.compare(2 / 3F, 0.66F, 2).areEqual(), (2 / 3F) + " != " + 0.66F);
}
@Test
void floatImprecisionRight() {
assertTrue(Compare.compare(0.66F, 2 / 3F, 2).areEqual(), (2 / 3F) + " != " + 0.66F);
}
@Test
void nilFEqualsNilF() {
assertTrue(Compare.compare(0F, 0F).areEqual());
}
@Test
void nilFEqualsNilFloat() {
assertTrue(Compare.compare(0F, Float.valueOf(0)).areEqual());
}
@Test
void nilFEqualsNilL() {
assertTrue(Compare.compare(0F, 0L).areEqual());
}
@Test
void nilEqualsNilF() {
assertTrue(Compare.compare(0L, 0F).areEqual());
}
@Test
void nullNotEqualsSome() {
assertEquals(Result.unequal("0.0 != 1.0"), Compare.compare(0F, 1F));
}
@Test
void SomeNotEqualsNull() {
assertEquals(Result.unequal("1.0 != 0.0"), Compare.compare(1F, 0F));
}
@Test
void nilByteNotEqualsOne() {
assertEquals(Result.unequal("java.lang.Float: 0.0 != 1.0"), Compare.compare(Float.valueOf(0), 1F));
}
@Test
void nilNotEqualsOneByte() {
assertEquals(Result.unequal("0.0 != java.lang.Float: 1.0"), Compare.compare(0F, Float.valueOf(1)));
}
@Test
void nullNotEqualsNilFloat() {
assertEquals(Result.unequal("null != java.lang.Float: 0.0"), Compare.compare(null, Float.valueOf(0)));
}
@Test
void nullNotEqualsNil() {
assertEquals(Result.unequal("null != 0.0"), Compare.compare(null, 0F));
}
@Test
void nilFloatNotEqualsNull() {
assertEquals(Result.unequal("java.lang.Float: 0.0 != null"), Compare.compare(Float.valueOf(0), null));
}
@Test
void nilNotEqualsNull() {
assertEquals(Result.unequal("0.0 != null"), Compare.compare(0F, null));
}
@Test
void floatNotEqualsString() {
assertEquals(Result.unequal("0.0 != \"true\""), Compare.compare(0F, "true"));
}
@Test
void StringNotEqualsFloat() {
assertEquals(Result.unequal("\"false\" != 0.0"), Compare.compare("false", 0F));
}
}

View file

@ -1,4 +1,4 @@
package nl.sander.apples; package nl.sander.reflective.compare;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -8,66 +8,66 @@ class IntsTest {
@Test @Test
void nilEqualsNil() { void nilEqualsNil() {
assertEquals(Result.SAME, Apples.compare(0, 0)); assertEquals(Result.SAME, Compare.compare(0, 0));
} }
@Test @Test
void nilEqualsNilInteger() { void nilEqualsNilInteger() {
assertEquals(Result.SAME, Apples.compare(0, Integer.valueOf(0))); assertEquals(Result.SAME, Compare.compare(0, Integer.valueOf(0)));
} }
@Test @Test
void nilIntegerEqualsNil() { void nilIntegerEqualsNil() {
assertEquals(Result.SAME, Apples.compare(Integer.valueOf(0), 0)); assertEquals(Result.SAME, Compare.compare(Integer.valueOf(0), 0));
} }
@Test @Test
void nullNotEqualsSome() { void nullNotEqualsSome() {
assertEquals(Result.unequal("0 != 1"), Apples.compare(0, 1)); assertEquals(Result.unequal("0 != 1"), Compare.compare(0, 1));
} }
@Test @Test
void SomeNotEqualsNull() { void SomeNotEqualsNull() {
assertEquals(Result.unequal("1 != 0"), Apples.compare(1, 0)); assertEquals(Result.unequal("1 != 0"), Compare.compare(1, 0));
} }
@Test @Test
void nilByteNotEqualsOne() { void nilByteNotEqualsOne() {
assertEquals(Result.unequal("java.lang.Integer: 0 != 1"), Apples.compare(Integer.valueOf(0), 1)); assertEquals(Result.unequal("java.lang.Integer: 0 != 1"), Compare.compare(Integer.valueOf(0), 1));
} }
@Test @Test
void nilNotEqualsOneByte() { void nilNotEqualsOneByte() {
assertEquals(Result.unequal("0 != java.lang.Integer: 1"), Apples.compare(0, Integer.valueOf(1))); assertEquals(Result.unequal("0 != java.lang.Integer: 1"), Compare.compare(0, Integer.valueOf(1)));
} }
@Test @Test
void nullNotEqualsNilByte() { void nullNotEqualsNilByte() {
assertEquals(Result.unequal("null != java.lang.Integer: 0"), Apples.compare(null, Integer.valueOf(0))); assertEquals(Result.unequal("null != java.lang.Integer: 0"), Compare.compare(null, Integer.valueOf(0)));
} }
@Test @Test
void nullNotEqualsNil() { void nullNotEqualsNil() {
assertEquals(Result.unequal("null != 0"), Apples.compare(null, 0)); assertEquals(Result.unequal("null != 0"), Compare.compare(null, 0));
} }
@Test @Test
void nilByteNotEqualsNull() { void nilByteNotEqualsNull() {
assertEquals(Result.unequal("java.lang.Integer: 0 != null"), Apples.compare(Integer.valueOf(0), null)); assertEquals(Result.unequal("java.lang.Integer: 0 != null"), Compare.compare(Integer.valueOf(0), null));
} }
@Test @Test
void nilNotEqualsNull() { void nilNotEqualsNull() {
assertEquals(Result.unequal("0 != null"), Apples.compare(0, null)); assertEquals(Result.unequal("0 != null"), Compare.compare(0, null));
} }
@Test @Test
void intNotEqualsString() { void intNotEqualsString() {
assertEquals(Result.unequal("0 != \"true\""), Apples.compare(0, "true")); assertEquals(Result.unequal("0 != \"true\""), Compare.compare(0, "true"));
} }
@Test @Test
void StringNotEqualsInt() { void StringNotEqualsInt() {
assertEquals(Result.unequal("\"0\" != 0"), Apples.compare("0", 0)); assertEquals(Result.unequal("\"0\" != 0"), Compare.compare("0", 0));
} }
} }

View file

@ -0,0 +1,68 @@
package nl.sander.reflective.compare;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class LongsTest {
@Test
void nullEqualsNull() {
assertEquals(Result.SAME, Compare.compare(0, 0));
}
@Test
void nullNotEqualsSome() {
assertEquals(Result.unequal("0 != 1"), Compare.compare(0, 1));
}
@Test
void SomeNotEqualsNull() {
assertEquals(Result.unequal("1 != 0"), Compare.compare(1, 0));
}
@Test
void nilLongNotEqualsOne() {
assertEquals(Result.unequal("java.lang.Long: 0 != 1"), Compare.compare(Long.valueOf(0), 1));
}
@Test
void nilNotEqualsOneLong() {
assertEquals(Result.unequal("0 != java.lang.Long: 1"), Compare.compare(0, Long.valueOf(1)));
}
@Test
void nilEqualsNilLong() {
assertTrue(Compare.compare(0, Long.valueOf(0)).areEqual());
}
@Test
void nullNotEqualsNilLong() {
assertEquals(Result.unequal("null != java.lang.Long: 0"), Compare.compare(null, Long.valueOf(0)));
}
@Test
void nullNotEqualsNil() {
assertEquals(Result.unequal("null != 0"), Compare.compare(null, 0));
}
@Test
void nilLongNotEqualsNull() {
assertEquals(Result.unequal("java.lang.Long: 0 != null"), Compare.compare(Long.valueOf(0), null));
}
@Test
void nilNotEqualsNull() {
assertEquals(Result.unequal("0 != null"), Compare.compare(0, null));
}
@Test
void longNotEqualsString() {
assertEquals(Result.unequal("0 != \"0\""), Compare.compare(0, "0"));
}
@Test
void StringNotEqualsLong() {
assertEquals(Result.unequal("\"false\" != 0"), Compare.compare("false", 0));
}
}

View file

@ -1,4 +1,4 @@
package nl.sander.apples; package nl.sander.reflective.compare;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -11,32 +11,32 @@ class ObjectsTest {
@Test @Test
void nullEqualsNull() { void nullEqualsNull() {
assertTrue(Apples.compare(null, null).areEqual()); assertTrue(Compare.compare(null, null).areEqual());
} }
@Test @Test
void nullNotEqualsSome() { void nullNotEqualsSome() {
assertEquals(Result.unequal("null != \"some\""), Apples.compare(null, "some")); assertEquals(Result.unequal("null != \"some\""), Compare.compare(null, "some"));
} }
@Test @Test
void SomeNotEqualsNull() { void SomeNotEqualsNull() {
assertEquals(Result.unequal("\"some\" != null"), Apples.compare("some", null)); assertEquals(Result.unequal("\"some\" != null"), Compare.compare("some", null));
} }
@Test @Test
void differentClass() { void differentClass() {
assertEquals(Result.unequal("\"1\" != java.lang.Integer: 1"), Apples.compare("1", Integer.valueOf(1))); assertEquals(Result.unequal("\"1\" != java.lang.Integer: 1"), Compare.compare("1", Integer.valueOf(1)));
} }
@Test @Test
void sameKeysAndValues() { void sameKeysAndValues() {
assertTrue(Apples.compare("map", Map.of("a", 1, "b", 2), Map.of("b", 2, "a", 1)).areEqual()); assertTrue(Compare.compare("map", Map.of("a", 1, "b", 2), Map.of("b", 2, "a", 1)).areEqual());
} }
@Test @Test
void differentKeysAndValues() { void differentKeysAndValues() {
Result result = Apples.compare("map", Map.of("a", 2, "b", 1), Map.of("b", 2, "a", 1)); Result result = Compare.compare("map", Map.of("a", 2, "b", 1), Map.of("b", 2, "a", 1));
assertFalse(result.areEqual()); assertFalse(result.areEqual());
assertTrue(result.getDiffs().contains("for map[b]: 1 != 2")); assertTrue(result.getDiffs().contains("for map[b]: 1 != 2"));
assertTrue(result.getDiffs().contains("for map[a]: 2 != 1")); assertTrue(result.getDiffs().contains("for map[a]: 2 != 1"));
@ -44,14 +44,14 @@ class ObjectsTest {
@Test @Test
void bigDecimals() { void bigDecimals() {
Result result = Apples.compare(BigDecimal.valueOf(0), BigDecimal.valueOf(1)); Result result = Compare.compare(BigDecimal.valueOf(0), BigDecimal.valueOf(1));
assertFalse(result.areEqual()); assertFalse(result.areEqual());
assertEquals("0 != 1", result.getDiffs().get(0)); assertEquals("0 != 1", result.getDiffs().get(0));
} }
@Test @Test
void enums() { void enums() {
Result result = Apples.compare(Storage.HIGH, Storage.LOW); Result result = Compare.compare(Storage.HIGH, Storage.LOW);
assertFalse(result.areEqual()); assertFalse(result.areEqual());
assertEquals("HIGH != LOW", result.getDiffs().get(0)); assertEquals("HIGH != LOW", result.getDiffs().get(0));
} }

View file

@ -1,4 +1,4 @@
package nl.sander.apples; package nl.sander.reflective.compare;
import java.util.List; import java.util.List;

View file

@ -1,4 +1,4 @@
package nl.sander.apples; package nl.sander.reflective.compare;
public class Shop { public class Shop {
private final String name; private final String name;

View file

@ -0,0 +1,73 @@
package nl.sander.reflective.compare;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class ShortsTest {
@Test
void nullEqualsNull() {
assertTrue(Compare.compare((short) 0, (short) 0).areEqual());
}
@Test
void nullEqualsNullShort() {
assertTrue(Compare.compare((short) 0, Short.valueOf((short) 0)).areEqual());
}
@Test
void nullShortEqualsNull() {
assertTrue(Compare.compare(Short.valueOf((short) 0), (short) 0).areEqual());
}
@Test
void nullNotEqualsSome() {
assertEquals(Result.unequal("0 != 1"), Compare.compare((short) 0, (short) 1));
}
@Test
void SomeNotEqualsNull() {
assertEquals(Result.unequal("1 != 0"), Compare.compare((short) 1, (short) 0));
}
@Test
void nilshortNotEqualsOne() {
assertEquals(Result.unequal("java.lang.Short: 0 != 1"), Compare.compare(Short.valueOf((short) 0), (short) 1));
}
@Test
void nilNotEqualsOneshort() {
assertEquals(Result.unequal("0 != java.lang.Short: 1"), Compare.compare((short) 0, Short.valueOf((short) 1)));
}
@Test
void nullNotEqualsNilshort() {
assertEquals(Result.unequal("null != java.lang.Short: 0"), Compare.compare(null, Short.valueOf((short) 0)));
}
@Test
void nullNotEqualsNil() {
assertEquals(Result.unequal("null != 0"), Compare.compare(null, (short) 0));
}
@Test
void nilshortNotEqualsNull() {
assertEquals(Result.unequal("java.lang.Short: 0 != null"), Compare.compare(Short.valueOf((short) 0), null));
}
@Test
void nilNotEqualsNull() {
assertEquals(Result.unequal("0 != null"), Compare.compare((short) 0, null));
}
@Test
void shortNotEqualsString() {
assertEquals(Result.unequal("0 != \"true\""), Compare.compare((short) 0, "true"));
}
@Test
void StringNotEqualsshort() {
assertEquals(Result.unequal("\"false\" != 0"), Compare.compare("false", (short) 0));
}
}

View file

@ -1,4 +1,4 @@
package nl.sander.apples; package nl.sander.reflective.compare;
public enum Storage { public enum Storage {
HIGH, HIGH,

View file

@ -1,4 +1,4 @@
package nl.sander.apples; package nl.sander.reflective.compare;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -7,7 +7,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
class StringTest { class StringTest {
@Test @Test
void test() { void test() {
assertTrue(Apples.compare("left", "left").areEqual()); assertTrue(Compare.compare("left", "left").areEqual());
} }
} }

View file

@ -0,0 +1,30 @@
package nl.sander.reflective.tomap;
import nl.sander.reflective.compare.PlumBean;
import nl.sander.reflective.compare.Shop;
import nl.sander.reflective.compare.Storage;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
class ToMapTest {
@Test
void testBeans() throws Exception {
Map<String,Object> map = ToMap.map(new PlumBean("small", "red", true, 1, 1.0F, Storage.HIGH, (byte) 1, List.of(new Shop("tesco"))));
assertEquals("small", map.get("core"));
assertEquals("red", map.get("peel"));
assertEquals(true, map.get("juicy"));
assertEquals(1, map.get("number"));
assertEquals(1.0F, map.get("price"));
assertEquals(Storage.HIGH, map.get("storage"));
assertEquals((byte)1, map.get("cores"));
Object shops = map.get("shops");
assertTrue(shops instanceof List);
List<Shop> shopsList = (List<Shop>) shops;
assertEquals(1, shopsList.size());
assertEquals("tesco", shopsList.get(0).getName());
}
}