package com.lonepulse.icklebot.injector.resolver;
/*
* #%L
* IckleBot
* %%
* Copyright (C) 2013 Lonepulse
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.lang.reflect.Field;
import android.animation.AnimatorSet;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.telephony.TelephonyManager;
import android.view.View;
import android.view.animation.Animation;
import com.lonepulse.icklebot.annotation.inject.ApplicationContract;
import com.lonepulse.icklebot.annotation.inject.IckleService;
import com.lonepulse.icklebot.annotation.inject.IgnoreInjection;
import com.lonepulse.icklebot.annotation.inject.InjectAll;
import com.lonepulse.icklebot.annotation.inject.Layout;
import com.lonepulse.icklebot.annotation.inject.Pojo;
import com.lonepulse.icklebot.event.resolver.EventResolver;
import com.lonepulse.icklebot.util.ContextUtils;
import com.lonepulse.icklebot.util.ReflectiveR;
/**
* <p>An implementation of {@link EventResolver} which caters to
* <b>Implicit Injections</b> activated via {@link InjectAll}.</p>
*
* @version 1.1.2
* <br><br>
* @author <a href="mailto:lahiru@lonepulse.com">Lahiru Sahan Jayasinghe</a>
*/
class ImplicitInjectionResolver implements InjectionResolver {
/**
* {@inheritDoc}
*/
@Override
public InjectionCategory resolve(Object context, Field field) {
Context baseContext = ContextUtils.discover(context);
if(field.isAnnotationPresent(IgnoreInjection.class)) return InjectionCategory.NONE;
if(field.isAnnotationPresent(Layout.class)) return InjectionCategory.LAYOUT;
if(isCategoryApplication(field)) return InjectionCategory.APPLICATION;
if(isCategoryResourceView(field)) return InjectionCategory.RESOURCE_VIEW;
if(isCategoryResourceInteger(baseContext, field)) return InjectionCategory.RESOURCE_INTEGER;
if(isCategoryResourceString(field)) return InjectionCategory.RESOURCE_STRING;
if(isCategoryResourceDrawable(field)) return InjectionCategory.RESOURCE_DRAWABLE;
if(isCategoryResourceColor(baseContext, field)) return InjectionCategory.RESOURCE_COLOR;
if(isCategoryResourceDimension(field)) return InjectionCategory.RESOURCE_DIMENSION;
if(isCategoryResourceBoolean(field)) return InjectionCategory.RESOURCE_BOOLEAN;
if(isCategoryResourceArray(field)) return InjectionCategory.RESOURCE_ARRAY;
if(isCategoryResourceAnimation(field)) return InjectionCategory.RESOURCE_ANIMATION;
if(isCategoryResourceAnimator(field)) return InjectionCategory.RESOURCE_ANIMATOR;
if(isCategoryPojo(field)) return InjectionCategory.POJO;
if(isCategorySystemService(field)) return InjectionCategory.SYSTEM_SERVICE;
if(isCategoryIckleService(field)) return InjectionCategory.ICKLE_SERVICE;
return InjectionCategory.NONE;
}
/**
* <p>Determines if this {@link Field} injection falls into
* {@link InjectionCategory#APPLICATION}.</p>
*
* @param field
* the {@link Field} whose {@link InjectionCategory} is to
* be resolved
* <br><br>
* @return {@code true} if it's {@link InjectionCategory#APPLICATION},
* <br><br>
* @since 1.1.0
*/
private boolean isCategoryApplication(Field field) {
return field.getType().isAnnotationPresent(ApplicationContract.class);
}
/**
* <p>Determines if this {@link Field} injection falls into
* {@link InjectionCategory#RESOURCE_VIEW}.</p>
*
* @param field
* the {@link Field} whose {@link InjectionCategory} is to
* be resolved
* <br><br>
* @return {@code true} if it's {@link InjectionCategory#RESOURCE_VIEW},
* <br><br>
* @since 1.1.0
*/
private boolean isCategoryResourceView(Field field) {
return (View.class.isAssignableFrom(field.getType())
&& !field.isAnnotationPresent(Layout.class))? true :false;
}
/**
* <p>Determines if this {@link Field} injection falls into
* {@link InjectionCategory#RESOURCE_INTEGER}.</p>
*
* @param context
* the context from which the injection resolution is requested
*
* @param field
* the {@link Field} whose {@link InjectionCategory} is to
* be resolved
* <br><br>
* @return {@code true} if it's {@link InjectionCategory#RESOURCE_INTEGER},
* <br><br>
* @since 1.1.0
*/
private boolean isCategoryResourceInteger(Context context, Field field) {
return ((Integer.class.isAssignableFrom(field.getType())
|| int.class.isAssignableFrom(field.getType()))
&& ((ReflectiveR.integer(context, field.getName())) != 0))? true : false;
}
/**
* <p>Determines if this {@link Field} injection falls into
* {@link InjectionCategory#RESOURCE_STRING}.</p>
*
* @param field
* the {@link Field} whose {@link InjectionCategory} is to
* be resolved
* <br><br>
* @return {@code true} if it's {@link InjectionCategory#RESOURCE_STRING},
* <br><br>
* @since 1.1.0
*/
private boolean isCategoryResourceString(Field field) {
return (String.class.isAssignableFrom(field.getType()))? true :false;
}
/**
* <p>Determines if this {@link Field} injection falls into
* {@link InjectionCategory#RESOURCE_DRAWABLE}.</p>
*
* @param field
* the {@link Field} whose {@link InjectionCategory} is to
* be resolved
* <br><br>
* @return {@code true} if it's {@link InjectionCategory#RESOURCE_DRAWABLE},
* <br><br>
* @since 1.1.0
*/
private boolean isCategoryResourceDrawable(Field field) {
return (Drawable.class.isAssignableFrom(field.getType()))? true :false;
}
/**
* <p>Determines if this {@link Field} injection falls into
* {@link InjectionCategory#RESOURCE_COLOR}.</p>
*
* @param context
* the context from which the injection resolution is requested
*
* @param field
* the {@link Field} whose {@link InjectionCategory} is to
* be resolved
* <br><br>
* @return {@code true} if it's {@link InjectionCategory#RESOURCE_COLOR},
* <br><br>
* @since 1.1.0
*/
private boolean isCategoryResourceColor(Context context, Field field) {
return ((Integer.class.isAssignableFrom(field.getType())
|| int.class.isAssignableFrom(field.getType()))
&& ((ReflectiveR.color(context, field.getName())) != 0))? true : false;
}
/**
* <p>Determines if this {@link Field} injection falls into
* {@link InjectionCategory#RESOURCE_DIMENSION}.</p>
*
* @param field
* the {@link Field} whose {@link InjectionCategory} is to
* be resolved
* <br><br>
* @return {@code true} if it's {@link InjectionCategory#RESOURCE_DRAWABLE},
* <br><br>
* @since 1.1.1
*/
private boolean isCategoryResourceDimension(Field field) {
return (Float.class.isAssignableFrom(field.getType())
|| float.class.isAssignableFrom(field.getType()))? true : false;
}
/**
* <p>Determines if this {@link Field} injection falls into
* {@link InjectionCategory#RESOURCE_BOOLEAN}.</p>
*
* @param field
* the {@link Field} whose {@link InjectionCategory} is to
* be resolved
* <br><br>
* @return {@code true} if it's {@link InjectionCategory#RESOURCE_BOOLEAN},
* <br><br>
* @since 1.1.1
*/
private boolean isCategoryResourceBoolean(Field field) {
return (Boolean.class.isAssignableFrom(field.getType())
|| boolean.class.isAssignableFrom(field.getType()))? true : false;
}
/**
* <p>Determines if this {@link Field} injection falls into
* {@link InjectionCategory#RESOURCE_ARRAY}.</p>
*
* @param field
* the {@link Field} whose {@link InjectionCategory} is to
* be resolved
* <br><br>
* @return {@code true} if it's {@link InjectionCategory#RESOURCE_ARRAY},
* <br><br>
* @since 1.1.1
*/
private boolean isCategoryResourceArray(Field field) {
return (String[].class.isAssignableFrom(field.getType())
|| int[].class.isAssignableFrom(field.getType())
|| Integer[].class.isAssignableFrom(field.getType()))? true : false;
}
/**
* <p>Determines if this {@link Field} injection falls into
* {@link InjectionCategory#RESOURCE_ANIMATION}.</p>
*
* @param field
* the {@link Field} whose {@link InjectionCategory} is to
* be resolved
* <br><br>
* @return {@code true} if it's {@link InjectionCategory#RESOURCE_ANIMATION},
* <br><br>
* @since 1.1.1
*/
private boolean isCategoryResourceAnimation(Field field) {
return (Animation.class.isAssignableFrom(field.getType()))? true : false;
}
/**
* <p>Determines if this {@link Field} injection falls into
* {@link InjectionCategory#RESOURCE_ANIMATOR}.</p>
*
* @param field
* the {@link Field} whose {@link InjectionCategory} is to
* be resolved
* <br><br>
* @return {@code true} if it's {@link InjectionCategory#RESOURCE_ANIMATOR},
* <br><br>
* @since 1.1.1
*/
private boolean isCategoryResourceAnimator(Field field) {
return (AnimatorSet.class.isAssignableFrom(field.getType()))? true : false;
}
/**
* <p>Determines if this {@link Field} injection falls into
* {@link InjectionCategory#POJO}.</p>
*
* @param field
* the {@link Field} whose {@link InjectionCategory} is to
* be resolved
* <br><br>
* @return {@code true} if it's {@link InjectionCategory#POJO},
* <br><br>
* @since 1.1.0
*/
private boolean isCategoryPojo(Field field) {
return field.getType().isAnnotationPresent(Pojo.class);
}
/**
* <p>Determines if this {@link Field} injection falls into
* {@link InjectionCategory#SYSTEM_SERVICE}.</p>
*
* <p>The {@link Field}'s <i>declared name</i> must match the
* respective constant in {@link Context} (case insensitive).
* For example, if an instance of {@link TelephonyManager} is
* to be injected, the field declaration should be:</p>
*
* <br><br>
*
* {@code TelephonyManager telephony_service;}
*
* @param field
* the {@link Field} whose {@link InjectionCategory} is to
* be resolved
* <br><br>
* @return {@code true} if it's {@link InjectionCategory#SYSTEM_SERVICE},
* <br><br>
* @since 1.1.0
*/
private boolean isCategorySystemService(Field field) {
if(!(field.getName().endsWith("_service")
|| field.getName().endsWith("_SERVICE"))) {
return false;
}
Field[] declaredFields = Context.class.getDeclaredFields();
for (Field declaredField : declaredFields) {
if(String.class.isAssignableFrom(declaredField.getType())
&& declaredField.getName().equalsIgnoreCase(field.getName())) {
return true;
}
}
return false;
}
/**
* <p>Determines if this {@link Field} injection falls into
* {@link InjectionCategory#ICKLE_SERVICE}.</p>
*
* <p>There are no restrictions to the {@link Field}'s <i> declared
* name</i>. Simply use a service contract with any field name and
* if the contract is an Ickle Service an instance of it will be
* injected. For example, the following declaration is acceptable:</p>
*
* <br><br>
*
* {@code BindManager myBinder;}
*
* @param field
* the {@link Field} whose {@link InjectionCategory} is to
* be resolved
* <br><br>
* @return {@code true} if it's {@link InjectionCategory#ICKLE_SERVICE},
* <br><br>
* @since 1.1.2
*/
private boolean isCategoryIckleService(Field field) {
Class<?> type = field.getType();
return (type.getName().startsWith("com.lonepulse.icklebot")
&& type.isAnnotationPresent(IckleService.class));
}
}