package com.github.czyzby.context.processor; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.reflect.Field; import com.badlogic.gdx.utils.reflect.Method; import com.badlogic.gdx.utils.reflect.ReflectionException; import com.github.czyzby.autumn.annotation.Processor; import com.github.czyzby.autumn.context.Context; import com.github.czyzby.autumn.context.ContextDestroyer; import com.github.czyzby.autumn.context.ContextInitializer; import com.github.czyzby.autumn.processor.AbstractAnnotationProcessor; import com.github.czyzby.kiwi.log.Logger; import com.github.czyzby.kiwi.log.LoggerService; import com.github.czyzby.kiwi.util.gdx.collection.GdxArrays; /** This is an advanced Autumn example. {@link MyCustomProcessor} represents an annotation processor which handles * {@link MyCustomAnnotation} occurrences. This API allows to register new, custom annotations and highly extend Autumn * functionalities. * * @author MJ */ @Processor public class MyCustomProcessor extends AbstractAnnotationProcessor<MyCustomAnnotation> { /** Kiwi logger for this class. */ private static final Logger LOGGER = LoggerService.forClass(MyCustomProcessor.class); /** This array stores instances of all found classes annotated with MyCustomAnnotation. */ private final Array<Object> annotatedComponents = GdxArrays.newArray(); @Override public Class<MyCustomAnnotation> getSupportedAnnotationType() { return MyCustomAnnotation.class; } @Override public void doBeforeScanning(final ContextInitializer initializer) { LOGGER.info("Initiating MyCustomProcessor..."); // Telling the ContextInitializer to look for classes annotated with MyCustomAnnotation: initializer.scanFor(MyCustomAnnotation.class); } // Our annotation supports fields, methods and classes. If you want to support less targets, just override less // methods - they default to false. @Override public boolean isSupportingFields() { return true; } @Override public boolean isSupportingMethods() { return true; } @Override public boolean isSupportingTypes() { return true; } // Actual annotation processing methods: @Override public void processField(final Field field, final MyCustomAnnotation annotation, final Object component, final Context context, final ContextInitializer initializer, final ContextDestroyer contextDestroyer) { LOGGER.info("{0} has field '{1}' annotated with @MyCustomAnnotation. Injecting '{2}'.", component, field.getName(), annotation.value()); // Setting field to value from annotation: try { field.setAccessible(true); field.set(component, annotation.value()); } catch (final ReflectionException exception) { LOGGER.error(exception, "Unable to set field value."); } } @Override public void processMethod(final Method method, final MyCustomAnnotation annotation, final Object component, final Context context, final ContextInitializer initializer, final ContextDestroyer contextDestroyer) { // Invoking annotated method with ID from annotation as parameter: Object result = null; try { method.setAccessible(true); result = method.invoke(component, annotation.id()); } catch (final ReflectionException exception) { LOGGER.error(exception, "Unable to invoke method."); } LOGGER.info("{0} had method '{1}' annotated with @MyCustomAnnotation. Method invocation result: {2}.", component, method.getName(), result); } @Override public void processType(final Class<?> type, final MyCustomAnnotation annotation, final Object component, final Context context, final ContextInitializer initializer, final ContextDestroyer contextDestroyer) { LOGGER.info("Found {0} annotated with @MyCustomAnnotation.", component); // Storing scanned class instance: annotatedComponents.add(component); } @Override public void doAfterScanning(final ContextInitializer initializer, final Context context, final ContextDestroyer destroyer) { // By storing instances of classes with a particular annotation, you can perform some operations en masse in // this method. This is why we maintain a list of components: annotatedComponents. See processType method! // Printing instances of all annotated classes: LOGGER.info("Scanning is finished. Instances of classes annotated with @MyCustomAnnotation: {0}.", annotatedComponents); // Adding an additional action invoked upon context disposing: destroyer.addAction(new Runnable() { @Override public void run() { LOGGER.info("Disposing..."); // This is just a proof of concept - you can add additional disposing actions invoked during context // destruction. Performing disposing with a single method might be more efficient that registering a // separate action for each component during annotation scanning. This is how some of the official // processors work: see DisposeAnnotationProcessor, for example. } }); } }