package com.kelin.mvvmlight.messenger;
import com.google.repacked.antlr.v4.runtime.misc.Nullable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import rx.functions.Action0;
import rx.functions.Action1;
/**
* Created by kelin on 15-8-14.
*/
public class Messenger {
private static Messenger defaultInstance;
private HashMap<Type, List<WeakActionAndToken>> recipientsOfSubclassesAction;
private HashMap<Type, List<WeakActionAndToken>> recipientsStrictAction;
public static Messenger getDefault() {
if (defaultInstance == null) {
defaultInstance = new Messenger();
}
return defaultInstance;
}
public static void overrideDefault(Messenger newMessenger) {
defaultInstance = newMessenger;
}
public static void reset() {
defaultInstance = null;
}
/**
*
* @param recipient the receiver,if register in activity the recipient always set "this",
* and "Messenger.getDefault().unregister(this)" in onDestroy,if in ViewModel,
* you can also register with Activity context and also in onDestroy to unregister.
* @param action do something on message received
*/
public void register(Object recipient, Action0 action) {
register(recipient, null, false, action);
}
/**
*
* @param recipient the receiver,if register in activity the recipient always set "this",
* and "Messenger.getDefault().unregister(this)" in onDestroy,if in ViewModel,
* you can also register with Activity context and also in onDestroy to unregister.
* @param receiveDerivedMessagesToo whether Derived class of recipient can receive the message
* @param action do something on message received
*/
public void register(Object recipient, boolean receiveDerivedMessagesToo, Action0 action) {
register(recipient, null, receiveDerivedMessagesToo, action);
}
/**
*
* @param recipient the receiver,if register in activity the recipient always set "this",
* and "Messenger.getDefault().unregister(this)" in onDestroy,if in ViewModel,
* you can also register with Activity context and also in onDestroy to unregister.
* @param token register with a unique token,when a messenger send a msg with same token,it will receive this msg
* @param action do something on message received
*/
public void register(Object recipient, Object token, Action0 action) {
register(recipient, token, false, action);
}
/**
*
* @param recipient the receiver,if register in activity the recipient always set "this",
* and "Messenger.getDefault().unregister(this)" in onDestroy,if in ViewModel,
* you can also register with Activity context and also in onDestroy to unregister.
* @param token register with a unique token,when a messenger send a msg with same token,it will receive this msg
* @param receiveDerivedMessagesToo whether Derived class of recipient can receive the message
* @param action do something on message received
*/
public void register(Object recipient, Object token, boolean receiveDerivedMessagesToo, Action0 action) {
Type messageType = NotMsgType.class;
HashMap<Type, List<WeakActionAndToken>> recipients;
if (receiveDerivedMessagesToo) {
if (recipientsOfSubclassesAction == null) {
recipientsOfSubclassesAction = new HashMap<Type, List<WeakActionAndToken>>();
}
recipients = recipientsOfSubclassesAction;
} else {
if (recipientsStrictAction == null) {
recipientsStrictAction = new HashMap<Type, List<WeakActionAndToken>>();
}
recipients = recipientsStrictAction;
}
List<WeakActionAndToken> list;
if (!recipients.containsKey(messageType)) {
list = new ArrayList<WeakActionAndToken>();
recipients.put(messageType, list);
} else {
list = recipients.get(messageType);
}
WeakAction weakAction = new WeakAction(recipient, action);
WeakActionAndToken item = new WeakActionAndToken(weakAction, token);
list.add(item);
cleanup();
}
/**
*
* @param recipient {@link com.kelin.mvvmlight.messenger.Messenger#register(Object, Action0)}
* @param tClass class of T
* @param action this action has one params that type of tClass
* @param <T> message data type
*/
public <T> void register(Object recipient, Class<T> tClass, Action1<T> action) {
register(recipient, null, false, action, tClass);
}
/**
* see {@link com.kelin.mvvmlight.messenger.Messenger#register(Object, Class, Action1)}
* @param recipient receiver of message
* @param receiveDerivedMessagesToo whether derived class of recipient can receive the message
* @param tClass class of T
* @param action this action has one params that type of tClass
* @param <T> message data type
*/
public <T> void register(Object recipient, boolean receiveDerivedMessagesToo, Class<T> tClass, Action1<T> action) {
register(recipient, null, receiveDerivedMessagesToo, action, tClass);
}
/**
* see {@link com.kelin.mvvmlight.messenger.Messenger#register(Object, Object, Action0)}
* @param recipient receiver of message
* @param token register with a unique token,when a messenger send a msg with same token,it will receive this msg
* @param tClass class of T for Action1
* @param action this action has one params that type of tClass
* @param <T> message data type
*/
public <T> void register(Object recipient, Object token, Class<T> tClass, Action1<T> action) {
register(recipient, token, false, action, tClass);
}
/**
* see {@link com.kelin.mvvmlight.messenger.Messenger#register(Object, Object, Class, Action1)}
* @param recipient receiver of message
* @param token register with a unique token,when a messenger send a msg with same token,it will receive this msg
* @param receiveDerivedMessagesToo whether derived class of recipient can receive the message
* @param action this action has one params that type of tClass
* @param tClass class of T for Action1
* @param <T> message data type
*/
public <T> void register(Object recipient, Object token, boolean receiveDerivedMessagesToo, Action1<T> action, Class<T> tClass) {
Type messageType = tClass;
HashMap<Type, List<WeakActionAndToken>> recipients;
if (receiveDerivedMessagesToo) {
if (recipientsOfSubclassesAction == null) {
recipientsOfSubclassesAction = new HashMap<Type, List<WeakActionAndToken>>();
}
recipients = recipientsOfSubclassesAction;
} else {
if (recipientsStrictAction == null) {
recipientsStrictAction = new HashMap<Type, List<WeakActionAndToken>>();
}
recipients = recipientsStrictAction;
}
List<WeakActionAndToken> list;
if (!recipients.containsKey(messageType)) {
list = new ArrayList<WeakActionAndToken>();
recipients.put(messageType, list);
} else {
list = recipients.get(messageType);
}
WeakAction weakAction = new WeakAction<T>(recipient, action);
WeakActionAndToken item = new WeakActionAndToken(weakAction, token);
list.add(item);
cleanup();
}
private void cleanup() {
cleanupList(recipientsOfSubclassesAction);
cleanupList(recipientsStrictAction);
}
/**
*
* @param token send with a unique token,when a receiver has register with same token,it will receive this msg
*/
public void sendNoMsg(Object token) {
sendToTargetOrType(null, token);
}
/**
* send to recipient directly with has not any message
* @param target Messenger.getDefault().register(this, ..) in a activity,if target set this activity
* it will receive the message
*/
public void sendNoMsgToTarget(Object target) {
sendToTargetOrType(target.getClass(), null);
}
/**
* send message to target with token,when a receiver has register with same token,it will receive this msg
* @param token send with a unique token,when a receiver has register with same token,it will receive this msg
* @param target send to recipient directly with has not any message,
* Messenger.getDefault().register(this, ..) in a activity,if target set this activity
* it will receive the message
*/
public void sendNoMsgToTargetWithToken(Object token, Object target) {
sendToTargetOrType(target.getClass(), token);
}
/**
* send the message type of T, all receiver can receive the message
* @param message any object can to be a message
* @param <T> message data type
*/
public <T> void send(T message) {
sendToTargetOrType(message, null, null);
}
/**
* send the message type of T, all receiver can receive the message
* @param message any object can to be a message
* @param token send with a unique token,when a receiver has register with same token,it will receive this message
* @param <T> message data type
*/
public <T> void send(T message, Object token) {
sendToTargetOrType(message, null, token);
}
/**
* send message to recipient directly
* @param message any object can to be a message
* @param target send to recipient directly with has not any message,
* Messenger.getDefault().register(this, ..) in a activity,if target set this activity
* it will receive the message
* @param <T> message data type
* @param <R> target
*/
public <T, R> void sendToTarget(T message, R target) {
sendToTargetOrType(message, target.getClass(), null);
}
/**
* Unregister the receiver such as:
* Messenger.getDefault().unregister(this)" in onDestroy in the Activity is required avoid to memory leak!
* @param recipient receiver of message
*/
public void unregister(Object recipient) {
unregisterFromLists(recipient, recipientsOfSubclassesAction);
unregisterFromLists(recipient, recipientsStrictAction);
cleanup();
}
public <T> void unregister(Object recipient, Object token) {
unregisterFromLists(recipient, token, null, recipientsStrictAction);
unregisterFromLists(recipient, token, null, recipientsOfSubclassesAction);
cleanup();
}
private static <T> void sendToList(
T message,
Collection<WeakActionAndToken> list,
Type messageTargetType,
Object token) {
if (list != null) {
// Clone to protect from people registering in a "receive message" method
// Bug correction Messaging BL0004.007
ArrayList<WeakActionAndToken> listClone = new ArrayList<>();
listClone.addAll(list);
for (WeakActionAndToken item : listClone) {
WeakAction executeAction = item.getAction();
if (executeAction != null
&& item.getAction().isLive()
&& item.getAction().getTarget() != null
&& (messageTargetType == null
|| item.getAction().getTarget().getClass() == messageTargetType
|| classImplements(item.getAction().getTarget().getClass(), messageTargetType))
&& ((item.getToken() == null && token == null)
|| item.getToken() != null && item.getToken().equals(token))) {
executeAction.execute(message);
}
}
}
}
private static void unregisterFromLists(Object recipient, HashMap<Type, List<WeakActionAndToken>> lists) {
if (recipient == null
|| lists == null
|| lists.size() == 0) {
return;
}
synchronized (lists) {
for (Type messageType : lists.keySet()) {
for (WeakActionAndToken item : lists.get(messageType)) {
WeakAction weakAction = item.getAction();
if (weakAction != null
&& recipient == weakAction.getTarget()) {
weakAction.markForDeletion();
}
}
}
}
cleanupList(lists);
}
private static <T> void unregisterFromLists(
Object recipient,
Action1<T> action,
HashMap<Type, List<WeakActionAndToken>> lists,
Class<T> tClass) {
Type messageType = tClass;
if (recipient == null
|| lists == null
|| lists.size() == 0
|| !lists.containsKey(messageType)) {
return;
}
synchronized (lists) {
for (WeakActionAndToken item : lists.get(messageType)) {
WeakAction<T> weakActionCasted = (WeakAction<T>) item.getAction();
if (weakActionCasted != null
&& recipient == weakActionCasted.getTarget()
&& (action == null
|| action == weakActionCasted.getAction1())) {
item.getAction().markForDeletion();
}
}
}
}
private static void unregisterFromLists(
Object recipient,
Action0 action,
HashMap<Type, List<WeakActionAndToken>> lists
) {
Type messageType = NotMsgType.class;
if (recipient == null
|| lists == null
|| lists.size() == 0
|| !lists.containsKey(messageType)) {
return;
}
synchronized (lists) {
for (WeakActionAndToken item : lists.get(messageType)) {
WeakAction weakActionCasted = (WeakAction) item.getAction();
if (weakActionCasted != null
&& recipient == weakActionCasted.getTarget()
&& (action == null
|| action == weakActionCasted.getAction())) {
item.getAction().markForDeletion();
}
}
}
}
private static <T> void unregisterFromLists(
Object recipient,
Object token,
Action1<T> action,
HashMap<Type, List<WeakActionAndToken>> lists, Class<T> tClass) {
Type messageType = tClass;
if (recipient == null
|| lists == null
|| lists.size() == 0
|| !lists.containsKey(messageType)) {
return;
}
synchronized (lists) {
for (WeakActionAndToken item : lists.get(messageType)) {
WeakAction<T> weakActionCasted = (WeakAction<T>) item.getAction();
if (weakActionCasted != null
&& recipient == weakActionCasted.getTarget()
&& (action == null
|| action == weakActionCasted.getAction1())
&& (token == null
|| token.equals(item.getToken()))) {
item.getAction().markForDeletion();
}
}
}
}
private static void unregisterFromLists(
Object recipient,
Object token,
Action0 action,
HashMap<Type, List<WeakActionAndToken>> lists) {
Type messageType = NotMsgType.class;
if (recipient == null
|| lists == null
|| lists.size() == 0
|| !lists.containsKey(messageType)) {
return;
}
synchronized (lists) {
for (WeakActionAndToken item : lists.get(messageType)) {
WeakAction weakActionCasted = (WeakAction) item.getAction();
if (weakActionCasted != null
&& recipient == weakActionCasted.getTarget()
&& (action == null
|| action == weakActionCasted.getAction())
&& (token == null
|| token.equals(item.getToken()))) {
item.getAction().markForDeletion();
}
}
}
}
private static boolean classImplements(Type instanceType, Type interfaceType) {
if (interfaceType == null
|| instanceType == null) {
return false;
}
Class[] interfaces = ((Class) instanceType).getInterfaces();
for (Class currentInterface : interfaces) {
if (currentInterface == interfaceType) {
return true;
}
}
return false;
}
private static void cleanupList(HashMap<Type, List<WeakActionAndToken>> lists) {
if (lists == null) {
return;
}
for (Iterator it = lists.entrySet().iterator(); it.hasNext(); ) {
Object key = it.next();
List<WeakActionAndToken> itemList = lists.get(key);
if (itemList != null) {
for (WeakActionAndToken item : itemList) {
if (item.getAction() == null
|| !item.getAction().isLive()) {
itemList.remove(item);
}
}
if (itemList.size() == 0) {
lists.remove(key);
}
}
}
}
private void sendToTargetOrType(Type messageTargetType, Object token) {
Class messageType = NotMsgType.class;
if (recipientsOfSubclassesAction != null) {
// Clone to protect from people registering in a "receive message" method
// Bug correction Messaging BL0008.002
// var listClone = recipientsOfSubclassesAction.Keys.Take(_recipientsOfSubclassesAction.Count()).ToList();
List<Type> listClone = new ArrayList<>();
listClone.addAll(recipientsOfSubclassesAction.keySet());
for (Type type : listClone) {
List<WeakActionAndToken> list = null;
if (messageType == type
|| ((Class) type).isAssignableFrom(messageType)
|| classImplements(messageType, type)) {
list = recipientsOfSubclassesAction.get(type);
}
sendToList(list, messageTargetType, token);
}
}
if (recipientsStrictAction != null) {
if (recipientsStrictAction.containsKey(messageType)) {
List<WeakActionAndToken> list = recipientsStrictAction.get(messageType);
sendToList(list, messageTargetType, token);
}
}
cleanup();
}
private static void sendToList(
Collection<WeakActionAndToken> list,
Type messageTargetType,
Object token) {
if (list != null) {
// Clone to protect from people registering in a "receive message" method
// Bug correction Messaging BL0004.007
ArrayList<WeakActionAndToken> listClone = new ArrayList<>();
listClone.addAll(list);
for (WeakActionAndToken item : listClone) {
WeakAction executeAction = item.getAction();
if (executeAction != null
&& item.getAction().isLive()
&& item.getAction().getTarget() != null
&& (messageTargetType == null
|| item.getAction().getTarget().getClass() == messageTargetType
|| classImplements(item.getAction().getTarget().getClass(), messageTargetType))
&& ((item.getToken() == null && token == null)
|| item.getToken() != null && item.getToken().equals(token))) {
executeAction.execute();
}
}
}
}
private <T> void sendToTargetOrType(T message, Type messageTargetType, Object token) {
Class messageType = message.getClass();
if (recipientsOfSubclassesAction != null) {
// Clone to protect from people registering in a "receive message" method
// Bug correction Messaging BL0008.002
// var listClone = recipientsOfSubclassesAction.Keys.Take(_recipientsOfSubclassesAction.Count()).ToList();
List<Type> listClone = new ArrayList<>();
listClone.addAll(recipientsOfSubclassesAction.keySet());
for (Type type : listClone) {
List<WeakActionAndToken> list = null;
if (messageType == type
|| ((Class) type).isAssignableFrom(messageType)
|| classImplements(messageType, type)) {
list = recipientsOfSubclassesAction.get(type);
}
sendToList(message, list, messageTargetType, token);
}
}
if (recipientsStrictAction != null) {
if (recipientsStrictAction.containsKey(messageType)) {
List<WeakActionAndToken> list = recipientsStrictAction.get(messageType);
sendToList(message, list, messageTargetType, token);
}
}
cleanup();
}
private class WeakActionAndToken {
private WeakAction action;
private Object token;
public WeakActionAndToken(WeakAction action, Object token) {
this.action = action;
this.token = token;
}
public WeakAction getAction() {
return action;
}
public void setAction(WeakAction action) {
this.action = action;
}
public Object getToken() {
return token;
}
public void setToken(Object token) {
this.token = token;
}
}
public static class NotMsgType {
}
}