/* 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 #include #include "operation.hpp" #include "record.hpp" #include "jupiter_algorithm.hpp" #include "jupiter_undo.hpp" namespace obby { /** Jupiter server implementation. */ template class jupiter_server: private net6::non_copyable { public: typedef Document document_type; typedef jupiter_algorithm algorithm_type; typedef jupiter_undo undo_type; typedef operation operation_type; typedef record record_type; typedef sigc::signal 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 from. 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 from. record_event * will be emitted for each client except from 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 from. 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 client_map; client_map m_clients; document_type& m_document; undo_type m_undo; signal_record_type m_signal_record; }; template jupiter_server::jupiter_server(document_type& doc): m_document(doc), m_undo(doc) { } template jupiter_server::~jupiter_server() { for(typename client_map::iterator iter = m_clients.begin(); iter != m_clients.end(); ++ iter) { delete iter->second; } } template void jupiter_server::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 void jupiter_server::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 void jupiter_server::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 rec = iter->second->local_op(op); m_signal_record.emit(*rec, *iter->first, from); } } template void jupiter_server::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 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 rec = iter->second->local_op(*op); m_signal_record.emit(*rec, *iter->first, from); } } } template void jupiter_server::undo_op(const user* from) { std::auto_ptr 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 rec = iter->second->local_op(*op); m_signal_record.emit(*rec, *iter->first, from); } } template typename jupiter_server::signal_record_type jupiter_server::record_event() const { return m_signal_record; } } // namespace obby #endif // _OBBY_JUPITER_SERVER_HPP_