From 15f6897c14e65a278ef67702dffb42f285fa3ce3 Mon Sep 17 00:00:00 2001 From: Mark Vainomaa Date: Wed, 18 Jan 2017 03:55:47 +0200 Subject: [PATCH] Add class validation utilities --- Reflect/pom.xml | 6 ++ .../eu/mikroskeem/utils/reflect/Validate.java | 97 +++++++++++++++++++ .../utils/test/reflect/TestValidate.java | 45 +++++++++ 3 files changed, 148 insertions(+) create mode 100644 Reflect/src/main/java/eu/mikroskeem/utils/reflect/Validate.java create mode 100644 Reflect/src/test/java/eu/mikroskeem/utils/test/reflect/TestValidate.java diff --git a/Reflect/pom.xml b/Reflect/pom.xml index faff0f6..29f5a90 100644 --- a/Reflect/pom.xml +++ b/Reflect/pom.xml @@ -17,6 +17,12 @@ annotations-java5 RELEASE + + org.projectlombok + lombok + 1.16.10 + provided + junit junit diff --git a/Reflect/src/main/java/eu/mikroskeem/utils/reflect/Validate.java b/Reflect/src/main/java/eu/mikroskeem/utils/reflect/Validate.java new file mode 100644 index 0000000..a750151 --- /dev/null +++ b/Reflect/src/main/java/eu/mikroskeem/utils/reflect/Validate.java @@ -0,0 +1,97 @@ +package eu.mikroskeem.utils.reflect; + +import lombok.Getter; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.stream.Stream; + +public class Validate { + /* Code got from Google Guava */ + private static T checkNotNull(T reference, @Nullable Object errorMessage) { + if(reference == null) { + throw new NullPointerException(String.valueOf(errorMessage)); + } else { + return reference; + } + } + + public static void checkMethods(ClassDescriptor classDescriptor, Validate.MethodDescriptor... methods) { + checkClass(classDescriptor); + Stream.of(methods).forEach(methodDescriptor -> + checkNotNull(Reflect.getMethod(classDescriptor.getClazz(), + methodDescriptor.getMethodName(), + methodDescriptor.getArguments()), + String.format("Method %s(%s) not found", + methodDescriptor.getMethodName(), + Arrays.toString(methodDescriptor.getArguments()) + )) + ); + } + + public static void checkConstructors(ClassDescriptor classDescriptor, + Validate.ConstructorDescriptor... constructors) { + checkClass(classDescriptor); + Stream.of(constructors).forEach(constructorDescriptor -> { + try { + checkNotNull(classDescriptor.getClazz().getConstructor(constructorDescriptor.getArguments()), + String.format("Constructor (%s) not found", + Arrays.toString(constructorDescriptor.getArguments()) + )); + } catch (Exception e){ + throw new NullPointerException(e.getMessage()); + } + }); + } + + public static void checkClass(ClassDescriptor classDescriptor){ + checkNotNull(classDescriptor.getClazz(), "Class is null"); + Stream.of(classDescriptor.getExtendingClasses()).forEach(clazz->{ + if(!clazz.isAssignableFrom(classDescriptor.getClazz())){ + throw new NullPointerException(String.format("Class doesn't extend %s", clazz.getSimpleName())); + } + }); + } + + public static @Getter class ClassDescriptor { + private final Class clazz; + private final Class[] extendingClasses; + private ClassDescriptor(Class clazz, Class... arguments){ + this.clazz = clazz; + this.extendingClasses = arguments; + } + + public static ClassDescriptor newClassDescriptor(Class clazz, Class... classes){ + return new ClassDescriptor(clazz, classes); + } + + public static ClassDescriptor newClassDescriptor(String clazzName, Class... classes){ + Class clazz = Reflect.getClass(clazzName); + return new ClassDescriptor(clazz, classes); + } + } + + public static @Getter class ConstructorDescriptor { + private final Class[] arguments; + private ConstructorDescriptor(Class... arguments){ + this.arguments = arguments; + } + + public static ConstructorDescriptor newConstructorDescriptor(Class... arguments){ + return new ConstructorDescriptor(arguments); + } + } + + public static @Getter class MethodDescriptor { + private final String methodName; + private final Class[] arguments; + private MethodDescriptor(String methodName, Class... arguments){ + this.methodName = methodName; + this.arguments = arguments; + } + + public static MethodDescriptor newMethodDescriptor(String methodName, Class... arguments){ + return new MethodDescriptor(methodName, arguments); + } + } +} diff --git a/Reflect/src/test/java/eu/mikroskeem/utils/test/reflect/TestValidate.java b/Reflect/src/test/java/eu/mikroskeem/utils/test/reflect/TestValidate.java new file mode 100644 index 0000000..c2624d4 --- /dev/null +++ b/Reflect/src/test/java/eu/mikroskeem/utils/test/reflect/TestValidate.java @@ -0,0 +1,45 @@ +package eu.mikroskeem.utils.test.reflect; + +import eu.mikroskeem.utils.reflect.Reflect; +import eu.mikroskeem.utils.reflect.Validate; +import org.junit.Test; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Type; + +import static eu.mikroskeem.utils.reflect.Validate.ClassDescriptor.newClassDescriptor; +import static eu.mikroskeem.utils.reflect.Validate.MethodDescriptor.newMethodDescriptor; +import static eu.mikroskeem.utils.reflect.Validate.checkClass; +import static eu.mikroskeem.utils.reflect.Validate.checkMethods; +import static org.objectweb.asm.Opcodes.*; + +public class TestValidate { + @Test + public void testMethodNoArguments() throws Exception { + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES); + cw.visit(V1_8, ACC_PUBLIC+ACC_SUPER, "eu/mikroskeem/utils/test/reflect/TestClass1", + null, Type.getInternalName(Object.class), null); + String desc = String.format("(%s)%s", "", "V"); + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", desc, null, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(Object.class), "", desc, false); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + desc = String.format("(%s)%s", "", Type.getDescriptor(String.class)); + mv = cw.visitMethod(ACC_PUBLIC, "toString", desc, null, null); + mv.visitLdcInsn("TestClass1 instance"); + mv.visitInsn(ARETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + cw.visitEnd(); + Class clazz = Reflect.defineClass( + ClassLoader.getSystemClassLoader(), + "eu.mikroskeem.utils.test.reflect.TestClass1", + cw.toByteArray()); + Validate.ClassDescriptor cd = newClassDescriptor(clazz, Object.class); + + checkClass(cd); + checkMethods(cd, newMethodDescriptor("toString")); + } +}