package com.markjmind.uni.mapper;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.view.View;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
/**
* <br>捲土重來<br>
* @author 오재웅(JaeWoong-Oh)
* @email markjmind@gmail.com
* @since 2016-01-28
*/
public class Mapper {
protected Context context;
protected Finder finder;
protected Object targetObject;
protected Class<?> targetClass;
protected HashMap<Integer, View> viewHash = new HashMap<>();
// private HashMap<Class<?>, InjectAdapter<? extends Annotation>> adapterHashMap = new HashMap<>();
private ArrayList<AccessibleInfo<Class<?>>> classAnnotations;
private ArrayList<AccessibleInfo<? extends AccessibleObject>> fieldAnnotations;
private ArrayList<AccessibleInfo<? extends AccessibleObject>> methodAnnotations;
private Method[] methods;
private Field[] fields;
private Class<?> parentsClass;
protected Mapper(){
}
public Mapper(View finder){
this(finder, finder);
}
public Mapper(Activity finder) {
this(finder, finder);
}
public Mapper(Dialog finder){
this(finder, finder);
}
public Mapper(View finder, Object targetObject){
reset(finder, targetObject);
}
public Mapper(Activity finder, Object targetObject){
reset(finder, targetObject);
}
public Mapper(Dialog finder, Object targetObject){
reset(finder, targetObject);
}
public void reset(View finder, Object targetObject){
this.context = finder.getContext();
this.targetObject = targetObject;
this.finder = new Finder(finder);
this.targetClass = targetObject.getClass();
viewHash.clear();
classAnnotations = null;
fieldAnnotations = null;
methodAnnotations = null;
}
public void reset(Activity finder, Object targetObject){
this.context = finder;
this.targetObject = targetObject;
this.finder = new Finder(finder);
this.targetClass = targetObject.getClass();
viewHash.clear();
classAnnotations = null;
fieldAnnotations = null;
methodAnnotations = null;
}
public void reset(Dialog finder, Object targetObject){
this.context = finder.getContext();
this.targetObject = targetObject;
this.finder = new Finder(finder);
this.targetClass = targetObject.getClass();
viewHash.clear();
classAnnotations = null;
fieldAnnotations = null;
methodAnnotations = null;
}
class AccessibleInfo<T>{
T accessibleObject;
Annotation annotation;
public AccessibleInfo(T accessibleObject, Annotation annotation){
this.accessibleObject = accessibleObject;
this.annotation = annotation;
}
}
public void inject(InjectAdapter<? extends Annotation>... adapter){
injectClass(adapter);
injectField(adapter);
injectMethod(adapter);
}
private void injectClass(InjectAdapter<? extends Annotation>... adapters){
ArrayList<InjectListener<? extends Annotation, Class>> injectListeners = new ArrayList<>();
for(InjectAdapter<? extends Annotation> adapter : adapters){
if(adapter!=null && adapter.getClassInjector()!=null) {
adapter.setMapper(this);
injectListeners.add(adapter.getClassInjector());
}
}
injectClass(injectListeners);
}
private void injectClass(ArrayList<InjectListener<? extends Annotation, Class>> injectListeners){
if(classAnnotations==null) {
classAnnotations = new ArrayList<>();
ArrayList<Class<?>> classArrayList = getClasses(targetClass);
for (Class<?> ab : classArrayList) {
Annotation[] annotations = ab.getDeclaredAnnotations();
ArrayList<AccessibleInfo<Class<?>>> info = new ArrayList<>();
for(Annotation annotation : annotations){
info.add(new AccessibleInfo<Class<?>>(ab, annotation));
}
if(info.size()>0){
classAnnotations.addAll(info);
}
}
}
if (injectListeners!=null && classAnnotations.size() > 0) {
for (AccessibleInfo<Class<?>> info: classAnnotations) {
for (InjectListener listener : injectListeners) {
if (info.annotation.annotationType().equals(listener.getAnnotationType())) {
listener.inject(info.annotation, info.accessibleObject, targetObject);
}
}
}
}
}
private void injectField(InjectAdapter<? extends Annotation>... adapters){
if (fields == null) {
fields = getFields(targetClass);
}
ArrayList<InjectListener<? extends Annotation, ? extends AccessibleObject>> injectListeners = new ArrayList<>();
for(InjectAdapter<? extends Annotation> adapter : adapters){
if(adapter!=null && adapter.getFieldInjector()!=null) {
adapter.setMapper(this);
injectListeners.add(adapter.getFieldInjector());
}
}
if(injectListeners.size()>0) {
fieldAnnotations = injectAdapter(fields, fieldAnnotations, injectListeners);
}
}
private void injectMethod(InjectAdapter<? extends Annotation>... adapters){
if (methods == null) {
methods = getMethods(targetClass);
}
ArrayList<InjectListener<? extends Annotation, ? extends AccessibleObject>> injectListeners = new ArrayList<>();
for(InjectAdapter<? extends Annotation> adapter : adapters){
if(adapter!=null && adapter.getMethodInjector()!=null) {
adapter.setMapper(this);
injectListeners.add(adapter.getMethodInjector());
}
}
if(injectListeners.size()>0) {
methodAnnotations = injectAdapter(methods, methodAnnotations, injectListeners);
}
}
private ArrayList<AccessibleInfo<? extends AccessibleObject>> injectAdapter(AccessibleObject[] abs, ArrayList<AccessibleInfo<? extends AccessibleObject>> outPut, ArrayList<InjectListener<? extends Annotation, ? extends AccessibleObject>> injectListeners){
if(outPut == null) {
outPut = new ArrayList<>();
if (abs != null) {
for (AccessibleObject ab : abs) {
Annotation[] annotations = ab.getDeclaredAnnotations();
ArrayList<AccessibleInfo<? extends AccessibleObject>> info = new ArrayList<>();
for(Annotation annotation : annotations){
info.add(new AccessibleInfo<>(ab, annotation));
}
if (info.size()>0) {
outPut.addAll(info);
}
}
}
}
if (injectListeners!=null && outPut.size() > 0) {
for (AccessibleInfo info : outPut) {
for (InjectListener listener : injectListeners) {
if (info.annotation.annotationType().equals(listener.getAnnotationType())) {
listener.inject(info.annotation, info.accessibleObject, targetObject);
}
}
}
}
return outPut;
}
public View findViewById(int id){
return finder.findViewById(id);
}
public void setInjectParents(Class<?> parentsClass){
this.parentsClass = parentsClass;
}
private ArrayList<Class<?>> getClasses(Class<?> targetClass){
ArrayList<Class<?>> classesList = new ArrayList();
if(parentsClass!=null){
Class<?> tempClass = targetClass;
Class<?> superClass = tempClass.getSuperclass();
while(parentsClass.equals(superClass) || !Object.class.equals(superClass)){
classesList.add(0, superClass);
tempClass = superClass;
superClass = tempClass.getSuperclass();
}
}
classesList.add(targetClass);
return classesList;
}
private Method[] getMethods(Class<?> targetClass){
if(parentsClass!=null){
Class<?> superClass = targetClass.getSuperclass();
if(!parentsClass.equals(superClass) || Object.class.equals(superClass)){
Method[] superMethods = getMethods(superClass);
Method[] targetMethods = targetClass.getDeclaredMethods();
Method[] allArray = new Method[targetMethods.length+superMethods.length];
System.arraycopy(targetMethods, 0, allArray, 0, targetMethods.length);
System.arraycopy(superMethods, 0, allArray, targetMethods.length, superMethods.length);
return allArray;
}
}
return targetClass.getDeclaredMethods();
}
private Field[] getFields(Class<?> targetClass){
if(parentsClass!=null){
Class<?> superClass = targetClass.getSuperclass();
if(!superClass.equals(parentsClass) || Object.class.equals(superClass)) {
Field[] superMethods = getFields(superClass);
Field[] targetMethods = targetClass.getDeclaredFields();
Field[] allArray = new Field[targetMethods.length + superMethods.length];
System.arraycopy(targetMethods, 0, allArray, 0, targetMethods.length);
System.arraycopy(superMethods, 0, allArray, targetMethods.length, superMethods.length);
return allArray;
}
}
return targetClass.getDeclaredFields();
}
}