package com.entityreborn.socpuppet.extensions; import com.entityreborn.socpuppet.extensions.annotations.SocBotPlugin; import com.laytonsmith.PureUtilities.Common.ClassUtils; import java.lang.annotation.Annotation; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic.Kind; /** * Extension processor to assert certain properties in a given extension. At runtime, * another check will be made, incase the extension wasn't processed at compile-time, * and in the case of multiple MSExtension annotations, will default to the first * it finds, in terms of identification, yet in case of multiples, a warning will * be printed, and the system will call the appropriate methods on all lifecycle * classes it finds, so that extensions aren't left hanging. * * @author Jason Unger <entityreborn@gmail.com> */ @SupportedAnnotationTypes({"com.entityreborn.socpuppet.extensions.annotations.SocBotPlugin"}) @SupportedSourceVersion(SourceVersion.RELEASE_6) public class ExtensionAnnotationProcessor extends AbstractProcessor { int found = 0; /** * Shortcut to stop the build process with an error. * * @param message the message to print * @param element the element causing an issue */ private void error(String message, Element element) { processingEnv.getMessager().printMessage(Kind.ERROR, message, element); } /** * Process a given set of annotations. * @param annotations * @param roundEnv * @return */ @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { boolean isExtensionWithLifecycleClass = false; // Find all annotations in this environment with the MSExtension annotation. for (Element possible : roundEnv.getElementsAnnotatedWith(SocBotPlugin.class)) { System.out.println("Processing " + possible); // Make sure this compile unit exposes only one lifecycle class if (found > 0) { error("A given compile unit (IE, Jar file) may contain only" + " ONE lifecycle class!", possible); continue; } Class clazz; // Manually load the class, as the class provided by the element isn't sufficient. try { clazz = getClassFromName(possible.toString()); } catch (ClassNotFoundException ex) { Logger.getLogger(ExtensionAnnotationProcessor.class.getName()).log(Level.SEVERE, null, ex); continue; } Set<Modifier> modifiers = possible.getModifiers(); // The class must be static. if (!modifiers.contains(Modifier.PUBLIC)) { error("Lifecycle classes must be declared public!", possible); continue; } // If the class is an embedded class, it must be declared static as well as public. if (clazz.isMemberClass() && !modifiers.contains(Modifier.STATIC)) { error("Lifecycle class must be declared static when wrapped " + "by an outer class!", possible); continue; } // The class must extend Extension.class if (!Extension.class.isAssignableFrom(clazz)) { error("Lifecycle class must extend AbstractExtension!", possible); continue; } SocBotPlugin annotation = null; // Let's get the annotation instance pertaining to this class, so we // can get the name. for (Annotation a : clazz.getAnnotations()) { if (a instanceof SocBotPlugin) { annotation = (SocBotPlugin)a; } } // We really shouldn't ever get here, because of the call used in the // for loop above, but handle it anyway. if (annotation == null) { error("Lifecycle class must be annotated with MSExtension!", possible); continue; } found++; System.out.println("Extension '" + annotation.value() + "' checks out ok!"); isExtensionWithLifecycleClass = true; } return isExtensionWithLifecycleClass; } private static Class getClassFromName(String className) throws ClassNotFoundException { return ClassUtils.forCanonicalName(className, false, ExtensionAnnotationProcessor.class.getClassLoader()); } }