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.
}
});
}
}