package com.github.czyzby.autumn.processor.impl; import com.badlogic.gdx.utils.Disposable; import com.badlogic.gdx.utils.reflect.Field; import com.github.czyzby.autumn.annotation.Dispose; 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.context.error.ContextInitiationException; import com.github.czyzby.autumn.processor.AbstractAnnotationProcessor; import com.github.czyzby.kiwi.util.common.Exceptions; import com.github.czyzby.kiwi.util.gdx.asset.lazy.Lazy; import com.github.czyzby.kiwi.util.gdx.collection.disposable.DisposableArray; import com.github.czyzby.kiwi.util.gdx.reflection.Reflection; /** Disposes of {@link Disposable} fields and components. * * @author MJ */ public class DisposeAnnotationProcessor extends AbstractAnnotationProcessor<Dispose> { private final DisposableArray<Disposable> disposables = DisposableArray.newArray(); @Override public Class<Dispose> getSupportedAnnotationType() { return Dispose.class; } @Override public boolean isSupportingFields() { return true; } @Override public void processField(final Field field, final Dispose annotation, final Object component, final Context context, final ContextInitializer initializer, final ContextDestroyer contextDestroyer) { disposables.add(new DisposableField(field, component)); } @Override public boolean isSupportingTypes() { return true; } @Override public void processType(final Class<?> type, final Dispose annotation, final Object component, final Context context, final ContextInitializer initializer, final ContextDestroyer contextDestroyer) { if (component instanceof Disposable) { disposables.add((Disposable) component); } else { throw new ContextInitiationException( "Dispose annotation should annotate only disposable components and fields. " + type + " is annotated, but does not seem to implement Disposable interface."); } } @Override public void doAfterScanning(final ContextInitializer initializer, final Context context, final ContextDestroyer destroyer) { destroyer.addAction(new DisposingAction(disposables)); } /** Disposes of selected disposable objects. * * @author MJ */ public static class DisposingAction implements Runnable { private final DisposableArray<Disposable> disposables; public DisposingAction(final DisposableArray<Disposable> disposables) { this.disposables = disposables; } @Override public void run() { disposables.dispose(); disposables.clear(); } } /** Allows to dispose of a field's value. * * @author MJ */ public static class DisposableField implements Disposable { private final Field field; private final Object fieldOwner; public DisposableField(final Field field, final Object fieldOwner) { this.field = field; this.fieldOwner = fieldOwner; } @Override public void dispose() { try { final Object value = Reflection.getFieldValue(field, fieldOwner); if (value instanceof Disposable) { ((Disposable) value).dispose(); } else if (value instanceof Lazy<?>) { final Lazy<?> lazy = (Lazy<?>) value; if (lazy.isInitialized() && lazy.get() instanceof Disposable) { ((Disposable) lazy.get()).dispose(); } } } catch (final Exception exception) { Exceptions.ignore(exception); // Ignored. Closing the application, invalid objects can occur. } } } }