package com.laytonsmith.PureUtilities.Common.Annotations; import com.laytonsmith.PureUtilities.ClassLoading.ClassDiscovery; import com.laytonsmith.PureUtilities.Common.StringUtils; import java.lang.reflect.Constructor; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; /** * This class is run by maven at compile time, and checks to ensure that the various * annotations referenced here are checked, and fail if any of the parameters are missing. */ public class AnnotationChecks { public static void checkForceImplementation() throws Exception{ Set<String> uhohs = new HashSet<>(); Set<Constructor> set = ClassDiscovery.getDefaultInstance().loadConstructorsWithAnnotation(ForceImplementation.class); for(Constructor cons : set){ Class superClass = cons.getDeclaringClass(); Set<Class> s = ClassDiscovery.getDefaultInstance().loadClassesThatExtend(superClass); checkImplements: for(Class c : s){ // c is the class we want to check to make sure it implements cons for(Constructor cCons : c.getDeclaredConstructors()){ if(Arrays.equals(cons.getParameterTypes(), cCons.getParameterTypes())){ continue checkImplements; } } if(c.isMemberClass() && (c.getModifiers() & Modifier.STATIC) == 0){ // Ok, so, an inner, non static class actually passes the super class's reference to the constructor as // the first parameter, at a byte code level. So this is a different type of error, or at least, a different // error message will be helpful. uhohs.add(c.getName() + " must be static."); } else { uhohs.add(c.getName() + " must implement the constructor with signature (" + getSignature(cons) + "), but doesn't."); } } } Set<Method> set2 = ClassDiscovery.getDefaultInstance().loadMethodsWithAnnotation(ForceImplementation.class); for(Method cons : set2){ Class superClass = cons.getDeclaringClass(); Set<Class> s = ClassDiscovery.getDefaultInstance().loadClassesThatExtend(superClass); checkImplements: for(Class c : s){ // c is the class we want to check to make sure it implements cons for(Method cCons : c.getDeclaredMethods()){ if(cCons.getName().equals(cons.getName()) && Arrays.equals(cons.getParameterTypes(), cCons.getParameterTypes())){ continue checkImplements; } } uhohs.add(c.getName() + " must implement the method with signature " + cons.getName() + "(" + getSignature(cons) + "), but doesn't."); } } if(!uhohs.isEmpty()){ List<String> uhohsList = new ArrayList<>(uhohs); Collections.sort(uhohsList); throw new Exception("There " + StringUtils.PluralHelper(uhohs.size(), "error") + ". The following classes need to implement various methods:\n" + StringUtils.Join(uhohs, "\n")); } } private static String getSignature(Member executable){ List<String> l = new ArrayList<>(); // for(Class cc : executable.getParameterTypes()){ // l.add(cc.getName()); // } if(executable instanceof Method){ for(Class cc : ((Method)executable).getParameterTypes()){ l.add(cc.getName()); } } else if(executable instanceof Constructor){ for(Class cc : ((Constructor)executable).getParameterTypes()){ l.add(cc.getName()); } } else { throw new Error("Unexpected executable type"); } return StringUtils.Join(l, ", "); } }