/*
* This file is part of gitg
*
* Copyright (C) 2012 - Jesse van den Kieboom
*
* gitg is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* gitg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with gitg. If not, see .
*/
namespace GitgExt
{
public delegate void MessageCallback(GitgExt.Message message);
public class MessageBus : Object
{
class Listener
{
public uint id;
public bool blocked;
public MessageCallback callback;
public Listener(uint id, owned MessageCallback callback)
{
this.id = id;
// TODO: destroy notify is lost...
this.callback = (owned)callback;
this.blocked = false;
}
}
class Message
{
public MessageId id;
public List listeners;
public Message(MessageId id)
{
this.id = id.copy();
this.listeners = new List();
}
}
class IdMap
{
public Message message;
public unowned List listener;
public IdMap(Message message)
{
this.message = message;
}
}
private HashTable d_messages;
private HashTable d_idmap;
private HashTable d_types;
private static MessageBus? s_instance;
private static uint s_next_id;
public signal void registered(MessageId id);
public signal void unregistered(MessageId id);
public virtual signal void dispatch(GitgExt.Message message)
{
Message? msg = lookup_message(message.id, false);
if (msg != null)
{
dispatch_message_real(msg, message);
}
}
public MessageBus()
{
d_messages = new HashTable(MessageId.hash, MessageId.equal);
d_idmap = new HashTable(direct_hash, direct_equal);
d_types = new HashTable(MessageId.hash, MessageId.equal);
}
public static MessageBus get_default()
{
if (s_instance == null)
{
s_instance = new MessageBus();
s_instance.add_weak_pointer(&s_instance);
}
return s_instance;
}
private void dispatch_message_real(Message msg, GitgExt.Message message)
{
foreach (Listener l in msg.listeners)
{
if (!l.blocked)
{
l.callback(message);
}
}
}
public Type lookup(MessageId id)
{
Type ret;
if (!d_types.lookup_extended(id, null, out ret))
{
return Type.INVALID;
}
else
{
return ret;
}
}
public void register(Type message_type, MessageId id)
{
if (is_registered(id))
{
warning("Message type for `%s' is already registered", id.id);
return;
}
var cp = id.copy();
d_types.insert(cp, message_type);
registered(cp);
}
private void unregister_real(MessageId id, bool remove_from_store)
{
var cp = id;
if (!remove_from_store || d_types.remove(cp))
{
unregistered(cp);
}
}
public void unregister(MessageId id)
{
unregister_real(id, true);
}
public void unregister_all(string object_path)
{
d_types.foreach_remove((key, val) => {
if (key.object_path == object_path)
{
unregister_real(key, true);
return true;
}
else
{
return false;
}
});
}
public bool is_registered(MessageId id)
{
return d_types.lookup_extended(id, null, null);
}
private Message new_message(MessageId id)
{
var ret = new Message(id);
d_messages.insert(id, ret);
return ret;
}
private Message? lookup_message(MessageId id, bool create)
{
var message = d_messages.lookup(id);
if (message == null && !create)
{
return null;
}
if (message == null)
{
message = new_message(id);
}
return message;
}
private uint add_listener(Message message, owned MessageCallback callback)
{
var listener = new Listener(++s_next_id, (owned)callback);
message.listeners.append(listener);
var idmap = new IdMap(message);
idmap.listener = message.listeners.last();
d_idmap.insert(listener.id, idmap);
return listener.id;
}
private void remove_listener(Message message, List listener)
{
unowned Listener lst = listener.data;
d_idmap.remove(lst.id);
message.listeners.delete_link(listener);
if (message.listeners == null)
{
d_messages.remove(message.id);
}
}
private void block_listener(Message message, List listener)
{
listener.data.blocked = true;
}
private void unblock_listener(Message message, List listener)
{
listener.data.blocked = false;
}
public new uint connect(MessageId id, owned MessageCallback callback)
{
var message = lookup_message(id, true);
return add_listener(message, (owned)callback);
}
private delegate void MatchCallback(Message message, List listeners);
private void process_by_id(uint id, MatchCallback processor)
{
IdMap? idmap = d_idmap.lookup(id);
if (idmap == null)
{
return;
}
processor(idmap.message, idmap.listener);
}
public new void disconnect(uint id)
{
process_by_id(id, remove_listener);
}
public void block(uint id)
{
process_by_id(id, block_listener);
}
public void unblock(uint id)
{
process_by_id(id, unblock_listener);
}
private void dispatch_message(GitgExt.Message message)
{
dispatch(message);
}
public GitgExt.Message send_message(GitgExt.Message message)
{
dispatch_message(message);
return message;
}
public GitgExt.Message? send(MessageId id, string? firstprop, ...)
{
Type type = lookup(id);
if (type == Type.INVALID)
{
warning("Could not find message type for `%s'", id.id);
return null;
}
GitgExt.Message? msg = (GitgExt.Message?)Object.new_valist(type, firstprop, va_list());
if (msg != null)
{
msg.id = id;
}
dispatch_message(msg);
return msg;
}
}
}
// ex:set ts=4 noet: