/* libobby - Network text editing library
* Copyright (C) 2005, 2006 0x539 dev group
*
* This program 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.
*
* This program 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 this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _OBBY_JUPITER_SERVER_HPP_
#define _OBBY_JUPITER_SERVER_HPP_
#include <map>
#include <net6/non_copyable.hpp>
#include "operation.hpp"
#include "record.hpp"
#include "jupiter_algorithm.hpp"
#include "jupiter_undo.hpp"
namespace obby
{
/** Jupiter server implementation.
*/
template<typename Document>
class jupiter_server: private net6::non_copyable
{
public:
typedef Document document_type;
typedef jupiter_algorithm<document_type> algorithm_type;
typedef jupiter_undo<document_type> undo_type;
typedef operation<document_type> operation_type;
typedef record<document_type> record_type;
typedef sigc::signal<void, const record_type&, const user&, const user*>
signal_record_type;
/** Creates a new jupiter_server which uses the given document.
* Local and remote changes are applied to this document.
*/
jupiter_server(document_type& doc);
~jupiter_server();
/** Adds a new client to the server.
*/
void client_add(const user& client);
/** Removes a client from the server.
*/
void client_remove(const user& client);
/** Performs a local operation by the user <em>from</em>. record_event
* will be emitted for each client with a corresponding
* record that may be transmitted to it.
*/
void local_op(const operation_type& op, const user* from);
/** Performs a remote operation by the user <em>from</em>. record_event
* will be emitted for each client except <em>from</em> with a record
* that may be transmitted to it.
*/
void remote_op(const record_type& rec, const user* from);
/** Undoes the last operation by the user <em>from</em>. record_event
* will be emitted for each client with a corresponding record that
* may be transmitted to it.
*/
void undo_op(const user* from);
/** Signal which will be emitted when a local operation has been
* applied.
*/
signal_record_type record_event() const;
protected:
typedef std::map<const user*, algorithm_type*> client_map;
client_map m_clients;
document_type& m_document;
undo_type m_undo;
signal_record_type m_signal_record;
};
template<typename Document>
jupiter_server<Document>::jupiter_server(document_type& doc):
m_document(doc), m_undo(doc)
{
}
template<typename Document>
jupiter_server<Document>::~jupiter_server()
{
for(typename client_map::iterator iter = m_clients.begin();
iter != m_clients.end();
++ iter)
{
delete iter->second;
}
}
template<typename Document>
void jupiter_server<Document>::client_add(const user& client)
{
if(m_clients.find(&client) != m_clients.end() )
{
throw std::logic_error(
"obby::jupiter_server::client_add:\n"
"Client has already been added"
);
}
m_clients[&client] = new algorithm_type;
}
template<typename Document>
void jupiter_server<Document>::client_remove(const user& client)
{
typename client_map::iterator iter = m_clients.find(&client);
if(iter == m_clients.end() )
{
throw std::logic_error(
"obby::jupiter_server::client_remove:\n"
"Client has not been added"
);
}
delete iter->second;
m_clients.erase(iter);
}
template<typename Document>
void jupiter_server<Document>::local_op(const operation_type& op,
const user* from)
{
op.apply(m_document, from);
m_undo.local_op(op, from);
for(typename client_map::iterator iter = m_clients.begin();
iter != m_clients.end();
++ iter)
{
std::auto_ptr<record_type> rec = iter->second->local_op(op);
m_signal_record.emit(*rec, *iter->first, from);
}
}
template<typename Document>
void jupiter_server<Document>::remote_op(const record_type& rec,
const user* from)
{
typename client_map::iterator iter = m_clients.find(from);
if(iter == m_clients.end() )
{
throw std::logic_error(
"obby::jupiter_server::remote_op:\n"
"Client has not been added"
);
}
std::auto_ptr<operation_type> op = iter->second->remote_op(rec);
op->apply(m_document, from);
m_undo.remote_op(*op, from);
for(iter = m_clients.begin(); iter != m_clients.end(); ++ iter)
{
if(iter->first != from)
{
std::auto_ptr<record_type> rec =
iter->second->local_op(*op);
m_signal_record.emit(*rec, *iter->first, from);
}
}
}
template<typename Document>
void jupiter_server<Document>::undo_op(const user* from)
{
std::auto_ptr<operation_type> op = m_undo.undo();
op->apply(m_document, from);
for(typename client_map::iterator iter = m_clients.begin();
iter != m_clients.end();
++ iter)
{
std::auto_ptr<record_type> rec = iter->second->local_op(*op);
m_signal_record.emit(*rec, *iter->first, from);
}
}
template<typename Document>
typename jupiter_server<Document>::signal_record_type
jupiter_server<Document>::record_event() const
{
return m_signal_record;
}
} // namespace obby
#endif // _OBBY_JUPITER_SERVER_HPP_
syntax highlighted by Code2HTML, v. 0.9.1