diff --git a/icklebot/src/main/java/com/lonepulse/icklebot/annotation/IckleInherited.java b/icklebot/src/main/java/com/lonepulse/icklebot/annotation/IckleInherited.java new file mode 100644 index 0000000..faa48d5 --- /dev/null +++ b/icklebot/src/main/java/com/lonepulse/icklebot/annotation/IckleInherited.java @@ -0,0 +1,33 @@ +package com.lonepulse.icklebot.annotation; + +/* + * #%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.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by jonwillis on 8/29/13. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface IckleInherited {} \ No newline at end of file diff --git a/icklebot/src/main/java/com/lonepulse/icklebot/injector/Injector.java b/icklebot/src/main/java/com/lonepulse/icklebot/injector/Injector.java index 2aadb10..c99f8a0 100644 --- a/icklebot/src/main/java/com/lonepulse/icklebot/injector/Injector.java +++ b/icklebot/src/main/java/com/lonepulse/icklebot/injector/Injector.java @@ -20,17 +20,11 @@ * #L% */ -import java.lang.reflect.Field; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - import android.app.Activity; import android.app.Fragment; import android.content.Context; +import com.lonepulse.icklebot.annotation.IckleInherited; import com.lonepulse.icklebot.annotation.inject.InjectAll; import com.lonepulse.icklebot.event.resolver.EventCategory; import com.lonepulse.icklebot.injector.resolver.InjectionCategory; @@ -38,6 +32,13 @@ import com.lonepulse.icklebot.injector.resolver.InjectionResolvers; import com.lonepulse.icklebot.util.ContextUtils; +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + /** *

This is the common contract which all injectors must implement.

* @@ -145,18 +146,21 @@ private Configuration(Object context) { else this.injectionMode = InjectionMode.EXPLICIT; - - Field[] fields = context.getClass().getDeclaredFields(); InjectionResolver injectionResolver = (this.injectionMode == InjectionMode.EXPLICIT)? InjectionResolvers.EXPLICIT :InjectionResolvers.IMPLICIT; - - for (Field field : fields) { - - InjectionCategory injectionCategory = injectionResolver.resolve(context, field); - injectionTargets.get(injectionCategory).add(field); - } + + Class currentClass = context.getClass(); + + do { + Field[] fields = currentClass.getDeclaredFields(); + for (Field field : fields) { + InjectionCategory injectionCategory = injectionResolver.resolve(context, field); + injectionTargets.get(injectionCategory).add(field); + } + currentClass = currentClass.getSuperclass(); + } while(currentClass != null && currentClass.isAnnotationPresent(IckleInherited.class)); } /** diff --git a/icklebot/src/main/java/com/lonepulse/icklebot/util/FieldUtils.java b/icklebot/src/main/java/com/lonepulse/icklebot/util/FieldUtils.java index 98d264b..3a99d0a 100644 --- a/icklebot/src/main/java/com/lonepulse/icklebot/util/FieldUtils.java +++ b/icklebot/src/main/java/com/lonepulse/icklebot/util/FieldUtils.java @@ -20,74 +20,78 @@ * #L% */ +import android.app.Activity; +import android.util.Log; + +import com.lonepulse.icklebot.annotation.IckleInherited; +import com.lonepulse.icklebot.injector.DuplicateInjectionException; + import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashSet; import java.util.Set; -import android.app.Activity; -import android.util.Log; - -import com.lonepulse.icklebot.injector.DuplicateInjectionException; - /** *

A utility class which performs some common operations involved * in discovering {@link Field} metadata.

- * + * * @version 1.0.0 *

* @author Lahiru Sahan Jayasinghe */ public final class FieldUtils { - + /** - *

Constructor visibility is restricted to prevent + *

Constructor visibility is restricted to prevent * nonsensical instantiation.

*/ private FieldUtils() { } - - + + /** - *

Takes the {@link Activity} and discovers which of - * it's instance {@link Field}s are annotated with the given + *

Takes the {@link Activity} and discovers which of + * it's instance {@link Field}s are annotated with the given * annotation (represented by supplied {@link Class}).

- * - * @param context + * + * @param context * the context whose {@link Field}s are to be scanned *

* @param annotation * the {@link Class} of the {@link Annotation} to look for *

- * @return all the {@link Field}s which are annotated with + * @return all the {@link Field}s which are annotated with * the passed {@link Annotation} *

* @since 1.0.0 */ public static Set getAllFields(Object context, Class annotation) { - - Set annotatedFields = new HashSet(); - - Field[] fields = context.getClass().getDeclaredFields(); - - for (Field field : fields) { - - if(field.isAnnotationPresent(annotation)) { - - annotatedFields.add(field); - } - } - + + Set annotatedFields = new HashSet(); + + Class currentClass = context.getClass(); + + do { + Field[] fields = currentClass.getDeclaredFields(); + for (Field field : fields) { + if(field.isAnnotationPresent(annotation)) { + + annotatedFields.add(field); + } + } + currentClass = currentClass.getSuperclass(); + } while(currentClass != null && currentClass.isAnnotationPresent(IckleInherited.class)); + return annotatedFields; } - + /** - *

Takes the target {@link Activity} and finds the only + *

Takes the target {@link Activity} and finds the only * field which is marked for injection with the given annotation.

- * + * * @param context * the context whose {@link Field}s are to be scanned *

@@ -103,31 +107,31 @@ public static Set getAllFields(Object context, */ public static Field getUniqeField(Object context, Class annotation) { - + Set fields = FieldUtils.getAllFields(context, annotation); - + if(fields.isEmpty()) { - + return null; } else if(fields.size() > 1) { - + throw new DuplicateInjectionException(context.getClass(), annotation); } - + return new ArrayList(fields).get(0); } - + /** - *

Takes a {@link Field} and retrieves it's value on the - * given {@link Activity} if it's type is compatible with the + *

Takes a {@link Field} and retrieves it's value on the + * given {@link Activity} if it's type is compatible with the * expected type referred to by the given {@link Class}.

- * - * @param context + * + * @param context * the context whose {@link Field}s are to be scanned *

* @param expectedType - * the {@link Class} of the field type which is legal + * the {@link Class} of the field type which is legal * for the annotation *

* @param field @@ -135,8 +139,8 @@ else if(fields.size() > 1) { *

* @return the value of the {@link Field} *

- * @throws ClassCastException - * when {@link Field} value cannot be cast to the + * @throws ClassCastException + * when {@link Field} value cannot be cast to the * expected type *

* @since 1.0.0 @@ -144,50 +148,50 @@ else if(fields.size() > 1) { public static T getFieldValue(Object context, Class expectedType, Field field) { - + if (!field.isAccessible()) { - + StringBuilder msg = new StringBuilder(); msg.append("Forcing accessibility for field "); msg.append(field.getName()); msg.append(" on "); msg.append(context.getClass().getName()); msg.append(". "); - + Log.w(FieldUtils.class.getName(), msg.toString()); - + field.setAccessible(true); } Object valueObject = null; - + try { - + valueObject = field.get(context); - } + } catch (IllegalArgumentException iae) { - + StringBuilder msg = new StringBuilder(); msg.append("Activity "); msg.append(context.getClass().getName()); msg.append(" is incompatible with the field "); msg.append(field.getName()); msg.append(". "); - + Log.e(FieldUtils.class.getName(), msg.toString(), iae); - } + } catch (IllegalAccessException iae) { - + StringBuilder msg = new StringBuilder(); msg.append("Field "); msg.append(field.getName()); msg.append(" on "); msg.append(context.getClass().getName()); msg.append(" cannot be accessed. "); - + Log.e(FieldUtils.class.getName(), msg.toString(), iae); } - + return expectedType.cast(valueObject); } }