package com.github.czyzby.autumn.processor.impl; import com.badlogic.gdx.utils.reflect.Field; import com.badlogic.gdx.utils.reflect.ReflectionException; import com.github.czyzby.autumn.annotation.Inject; 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.gdx.asset.lazy.Lazy; import com.github.czyzby.kiwi.util.gdx.asset.lazy.provider.ObjectProvider; import com.github.czyzby.kiwi.util.gdx.asset.lazy.provider.ReflectionObjectProvider; import com.github.czyzby.kiwi.util.gdx.reflection.Annotations; import com.github.czyzby.kiwi.util.gdx.reflection.Reflection; /** Processes {@link Inject}-annotated fields. Injects dependencies into components. * * @author MJ */ public class InjectAnnotationProcessor extends AbstractAnnotationProcessor<Inject> { @Override public Class<Inject> getSupportedAnnotationType() { return Inject.class; } @Override public boolean isSupportingFields() { return true; } @Override public void processField(final Field field, final Inject annotation, final Object component, final Context context, final ContextInitializer initializer, final ContextDestroyer contextDestroyer) { if (Annotations.isNotVoid(annotation.lazy())) { processLazyInjection(field, annotation, component, context); } else { processRegularInjection(field, annotation, component, context); } } /** @param field will have its value injected. * @param annotation used to determine dependency type. * @param component owner of the field. * @param context used to resolve dependencies. */ protected void processRegularInjection(final Field field, final Inject annotation, final Object component, final Context context) { final Class<?> dependencyClass = Annotations.isNotVoid(annotation.value()) ? annotation.value() : field.getType(); setFieldValue(field, component, context.provide(dependencyClass)); } /** @param field will be set. * @param component contains the field. * @param value will become field's value. */ protected void setFieldValue(final Field field, final Object component, final Object value) { try { Reflection.setFieldValue(field, component, value); } catch (final ReflectionException exception) { throw new ContextInitiationException( "Unable to inject value of field: " + field + " into component: " + component, exception); } } /** @param field needs a lazy wrapper injected. * @param annotation used to determine dependency and lazy types. * @param component owner of the field. * @param context used to resolve dependencies. */ protected void processLazyInjection(final Field field, final Inject annotation, final Object component, final Context context) { final Class<?> dependencyClass = annotation.lazy(); final ObjectProvider<?> provider = getLazyProvider(field, component, context, dependencyClass); setFieldValue(field, component, toLazy(provider, annotation)); } /** @param field needs to be injected with a lazy wrapped. * @param component contains the field. * @param context used to resolve dependencies. * @param dependencyClass required class. * @return provider that provides instances of dependency class. */ protected ObjectProvider<?> getLazyProvider(final Field field, final Object component, final Context context, final Class<?> dependencyClass) { if (context.isPresent(dependencyClass)) { return new ComponentProvider(context.getComponent(dependencyClass)); } else if (context.isProviderPresentFor(dependencyClass)) { return context.getProvider(dependencyClass); } else if (context.isCreatingMissingDependencies()) { return ReflectionObjectProvider.forClass(dependencyClass); } throw new ContextInitiationException( "Unable to inject lazy value of field: " + field + " in component: " + component); } /** @param provider provides instances for lazy wrapper. * @param annotation used to determine lazy type. * @return lazy wrapper with the selected provider. */ protected Lazy<Object> toLazy(final ObjectProvider<?> provider, final Inject annotation) { if (annotation.concurrentLazy()) { return Lazy.concurrentProvidedBy(provider); } return Lazy.providedBy(provider); } /** Provides component extracted from context. * * @author MJ */ public static class ComponentProvider implements ObjectProvider<Object> { private final Object component; public ComponentProvider(final Object component) { this.component = component; } @Override public Object provide() { return component; } } }