content: /*
   * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)
   *
   * 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.
   */
  
  package com.lidroid.xutils.view;
  
  import android.view.View;
  import com.lidroid.xutils.util.LogUtils;
  import com.lidroid.xutils.util.DoubleKeyValueMap;
  import com.lidroid.xutils.view.annotation.event.EventBase;
  
  import java.lang.annotation.Annotation;
  import java.lang.ref.WeakReference;
  import java.lang.reflect.InvocationHandler;
  import java.lang.reflect.Method;
  import java.lang.reflect.Proxy;
  import java.util.HashMap;
  
  public class EventListenerManager {
  
      private EventListenerManager() {
      }
  
      /**
       * k1: viewInjectInfo
       * k2: interface Type
       * value: listener
       */
      private final static DoubleKeyValueMap<ViewInjectInfo, Class<?>, Object> listenerCache =
              new DoubleKeyValueMap<ViewInjectInfo, Class<?>, Object>();
  
      public static void addEventMethod(
              ViewFinder finder,
              ViewInjectInfo info,
              Annotation eventAnnotation,
              Object handler,
              Method method) {
          try {
              View view = finder.findViewByInfo(info);
              if (view != null) {
                  EventBase eventBase = eventAnnotation.annotationType().getAnnotation(EventBase.class);
                  Class<?> listenerType = eventBase.listenerType();
                  String listenerSetter = eventBase.listenerSetter();
                  String methodName = eventBase.methodName();
  
                  boolean addNewMethod = false;
                  Object listener = listenerCache.get(info, listenerType);
                  DynamicHandler dynamicHandler = null;
                  if (listener != null) {
                      dynamicHandler = (DynamicHandler) Proxy.getInvocationHandler(listener);
                      addNewMethod = handler.equals(dynamicHandler.getHandler());
                      if (addNewMethod) {
                          dynamicHandler.addMethod(methodName, method);
                      }
                  }
                  if (!addNewMethod) {
                      dynamicHandler = new DynamicHandler(handler);
                      dynamicHandler.addMethod(methodName, method);
                      listener = Proxy.newProxyInstance(
                              listenerType.getClassLoader(),
                              new Class<?>[]{listenerType},
                              dynamicHandler);
  
                      listenerCache.put(info, listenerType, listener);
                  }
  
                  Method setEventListenerMethod = view.getClass().getMethod(listenerSetter, listenerType);
                  setEventListenerMethod.invoke(view, listener);
              }
          } catch (Throwable e) {
              LogUtils.e(e.getMessage(), e);
          }
      }
  
      /**
       * @author Caij
       *涉及到动态代理
       */
      public static class DynamicHandler implements InvocationHandler {
          private WeakReference<Object> handlerRef;
          private final HashMap<String, Method> methodMap = new HashMap<String, Method>(1);
  
          public DynamicHandler(Object handler) {
              this.handlerRef = new WeakReference<Object>(handler);
          }
  
          public void addMethod(String name, Method method) {
              methodMap.put(name, method);
          }
  
          public Object getHandler() {
              return handlerRef.get();
          }
  
          public void setHandler(Object handler) {
              this.handlerRef = new WeakReference<Object>(handler);
          }
  
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              Object handler = handlerRef.get();
              if (handler != null) {
                  String methodName = method.getName();
                  method = methodMap.get(methodName);
                  if (method != null) {
                      return method.invoke(handler, args);
                  }
              }
              return null;
          }
      }
  }
  