00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #ifndef _OBBY_CLIENT_DOCUMENT_INFO_HPP_
00020 #define _OBBY_CLIENT_DOCUMENT_INFO_HPP_
00021
00022 #include <net6/client.hpp>
00023 #include "format_string.hpp"
00024 #include "no_operation.hpp"
00025 #include "split_operation.hpp"
00026 #include "insert_operation.hpp"
00027 #include "delete_operation.hpp"
00028 #include "record.hpp"
00029 #include "jupiter_client.hpp"
00030 #include "local_document_info.hpp"
00031
00032 namespace obby
00033 {
00034
00035 template<typename Document, typename Selector>
00036 class basic_client_buffer;
00037
00041 template<typename Document, typename Selector>
00042 class basic_client_document_info:
00043 virtual public basic_local_document_info<Document, Selector>
00044 {
00045 public:
00046 typedef basic_document_info<Document, Selector> base_type;
00047 typedef basic_local_document_info<Document, Selector> base_local_type;
00048
00049 typedef typename base_local_type::document_type document_type;
00050
00051 typedef basic_client_buffer<Document, Selector> buffer_type;
00052 typedef typename buffer_type::net_type net_type;
00053 typedef jupiter_client<Document> jupiter_type;
00054 typedef typename jupiter_type::record_type record_type;
00055
00056 typedef typename base_local_type::subscription_state subscription_state;
00057
00061 basic_client_document_info(const buffer_type& buffer,
00062 net_type& net,
00063 const user* owner,
00064 unsigned int id,
00065 const std::string& title,
00066 unsigned int suffix,
00067 const std::string& encoding);
00068
00077 basic_client_document_info(const buffer_type& buffer,
00078 net_type& net,
00079 const user* owner,
00080 unsigned int id,
00081 const std::string& title,
00082 const std::string& encoding,
00083 const std::string& content);
00084
00088 basic_client_document_info(const buffer_type& buffer,
00089 net_type& net,
00090 const net6::packet& init_pack);
00091
00094 virtual void insert(position pos, const std::string& text);
00095
00098 virtual void erase(position pos, position len);
00099
00102 virtual void rename(const std::string& new_title);
00103
00107 virtual void subscribe();
00108
00112 virtual void unsubscribe();
00113
00117 virtual subscription_state get_subscription_state() const;
00118
00122 virtual void on_net_packet(const document_packet& pack);
00123
00126 virtual void obby_session_close();
00127
00128 protected:
00131 virtual void user_subscribe(const user& user);
00132
00135 virtual void user_unsubscribe(const user& user);
00136
00139 bool execute_packet(const document_packet& pack);
00140
00143 virtual void on_net_rename(const document_packet& pack);
00144
00147 virtual void on_net_record(const document_packet& pack);
00148
00151 virtual void on_net_sync_init(const document_packet& pack);
00152
00155 virtual void on_net_sync_chunk(const document_packet& pack);
00156
00159 virtual void on_net_subscribe(const document_packet& pack);
00160
00163 virtual void on_net_unsubscribe(const document_packet& pack);
00164
00168 virtual void on_jupiter_record(const record_type& rec,
00169 const user* from);
00170
00174 virtual void session_close_impl();
00175
00176 std::auto_ptr<jupiter_type> m_jupiter;
00177 subscription_state m_subscription_state;
00178
00179 public:
00182 const buffer_type& get_buffer() const;
00183
00184 protected:
00187 net_type& get_net6();
00188
00191 const net_type& get_net6() const;
00192 };
00193
00194 template<typename Document, typename Selector>
00195 basic_client_document_info<Document, Selector>::
00196 basic_client_document_info(const buffer_type& buffer,
00197 net_type& net,
00198 const user* owner,
00199 unsigned int id,
00200 const std::string& title,
00201 unsigned int suffix,
00202 const std::string& encoding):
00203 base_type(buffer, net, owner, id, title, suffix, encoding),
00204 base_local_type(buffer, net, owner, id, title, suffix, encoding),
00205 m_subscription_state(base_local_type::UNSUBSCRIBED)
00206 {
00207
00208
00209 if(owner == &buffer.get_self() )
00210 {
00211 throw std::logic_error(
00212 "obby::basic_client_document_info::"
00213 "basic_client_document_info:\n"
00214 "Owner of document info without initial content is self"
00215 );
00216 }
00217
00218
00219 if(owner != NULL)
00220 user_subscribe(*owner);
00221 }
00222
00223 template<typename Document, typename Selector>
00224 basic_client_document_info<Document, Selector>::
00225 basic_client_document_info(const buffer_type& buffer,
00226 net_type& net,
00227 const user* owner,
00228 unsigned int id,
00229 const std::string& title,
00230 const std::string& encoding,
00231 const std::string& content):
00232 base_type(
00233 buffer,
00234 net,
00235 owner,
00236 id,
00237 title,
00238 encoding
00239 ),
00240 base_local_type(
00241 buffer,
00242 net,
00243 owner,
00244 id,
00245 title,
00246 encoding
00247 ),
00248 m_subscription_state(base_local_type::SUBSCRIBED)
00249 {
00250
00251 if(owner != &buffer.get_self() )
00252 {
00253 throw std::logic_error(
00254 "obby::basic_client_document_info::"
00255 "basic_client_document_info:\n"
00256 "Owner of document info with initial content is "
00257 "not self"
00258 );
00259 }
00260
00261
00262 base_type::assign_document();
00263 base_type::m_document->insert(0, content, NULL);
00264
00265
00266 user_subscribe(*owner);
00267 }
00268
00269 template<typename Document, typename Selector>
00270 basic_client_document_info<Document, Selector>::
00271 basic_client_document_info(const buffer_type& buffer,
00272 net_type& net,
00273 const net6::packet& init_pack):
00274
00275 base_type(buffer, net, init_pack),
00276 base_local_type(buffer, net, init_pack),
00277 m_subscription_state(base_local_type::UNSUBSCRIBED)
00278 {
00279
00280 for(unsigned int i = 5; i < init_pack.get_param_count(); ++ i)
00281 {
00282
00283 const user* cur_user =
00284 init_pack.get_param(i).net6::parameter::as<const user*>(
00285 ::serialise::hex_context_from<const user*>(
00286 buffer.get_user_table()
00287 )
00288 );
00289
00290
00291
00292 if(cur_user == &buffer.get_self() )
00293 {
00294 throw std::logic_error(
00295 "obby::basic_client_document_info::"
00296 "basic_client_document_info:\n"
00297 "Local user is in subscription list of "
00298 "initially synchronised document list"
00299 );
00300 }
00301
00302
00303 user_subscribe(*cur_user);
00304 }
00305 }
00306
00307 template<typename Document, typename Selector>
00308 void basic_client_document_info<Document, Selector>::
00309 insert(position pos,
00310 const std::string& text)
00311 {
00312 if(base_type::m_document.get() == NULL)
00313 {
00314 throw std::logic_error(
00315 "obby::basic_client_document_info::insert:\n"
00316 "Local user is not subscribed"
00317 );
00318 }
00319
00320
00321
00322 if(m_jupiter.get() != NULL)
00323 {
00324 insert_operation<document_type> op(pos, text);
00325 m_jupiter->local_op(op, &get_buffer().get_self() );
00326 }
00327 else
00328 {
00329
00330 base_type::m_document->insert(
00331 pos,
00332 text,
00333 &get_buffer().get_self()
00334 );
00335 }
00336 }
00337
00338 template<typename Document, typename Selector>
00339 void basic_client_document_info<Document, Selector>::
00340 erase(position pos,
00341 position len)
00342 {
00343 if(base_type::m_document.get() == NULL)
00344 {
00345 throw std::logic_error(
00346 "obby::basic_client_document_info::erase:\n"
00347 "Local user is not subscribed"
00348 );
00349 }
00350
00351
00352
00353 if(m_jupiter.get() != NULL)
00354 {
00355 delete_operation<document_type> op(pos, len);
00356 m_jupiter->local_op(op, &get_buffer().get_self() );
00357 }
00358 else
00359 {
00360 base_type::m_document->erase(pos, len);
00361 }
00362 }
00363
00364 template<typename Document, typename Selector>
00365 void basic_client_document_info<Document, Selector>::
00366 rename(const std::string& new_title)
00367 {
00368 if(base_type::m_net != NULL)
00369 {
00370
00371 document_packet pack(*this, "rename");
00372 pack << new_title;
00373 get_net6().send(pack);
00374 }
00375 else
00376 {
00377 base_type::document_rename(
00378 new_title,
00379 base_type::m_buffer.find_free_suffix(new_title, this)
00380 );
00381 }
00382 }
00383
00384 template<typename Document, typename Selector>
00385 void basic_client_document_info<Document, Selector>::subscribe()
00386 {
00387
00388 if(m_subscription_state == base_local_type::SUBSCRIBED ||
00389 m_subscription_state == base_local_type::SUBSCRIBING)
00390 {
00391 throw std::logic_error(
00392 "obby::basic_client_document_info::subscribe:\n"
00393 "Local user is already subscribed or has sent a "
00394 "subscription request"
00395 );
00396 }
00397
00398 if(base_type::m_net != NULL)
00399 {
00400
00401 document_packet pack(*this, "subscribe");
00402 get_net6().send(pack);
00403
00404 m_subscription_state = base_local_type::SUBSCRIBING;
00405 }
00406 else
00407 {
00408 throw std::logic_error(
00409 "obby::basic_client_document_info::subscribe:\n"
00410 "Cannot subscribe to document without being connected"
00411 );
00412 }
00413 }
00414
00415 template<typename Document, typename Selector>
00416 void basic_client_document_info<Document, Selector>::unsubscribe()
00417 {
00418
00419 if(m_subscription_state == base_local_type::UNSUBSCRIBED ||
00420 m_subscription_state == base_local_type::UNSUBSCRIBING)
00421 {
00422 throw std::logic_error(
00423 "obby::basic_client_document_info::unsubscribe:\n"
00424 "Local user is not subscribed or has sent a "
00425 "unsubscription request"
00426 );
00427 }
00428
00429 if(base_type::m_net != NULL)
00430 {
00431
00432 document_packet pack(*this, "unsubscribe");
00433 get_net6().send(pack);
00434
00435 m_subscription_state = base_local_type::UNSUBSCRIBING;
00436 }
00437 else
00438 {
00439 user_unsubscribe(get_buffer().get_self() );
00440 }
00441 }
00442
00443 template<typename Document, typename Selector>
00444 typename basic_client_document_info<Document, Selector>::subscription_state
00445 basic_client_document_info<Document, Selector>::get_subscription_state() const
00446 {
00447 return m_subscription_state;
00448 }
00449
00450 template<typename Document, typename Selector>
00451 void basic_client_document_info<Document, Selector>::
00452 on_net_packet(const document_packet& pack)
00453 {
00454 if(!execute_packet(pack) )
00455 {
00456 throw net6::bad_value(
00457 "Unexpected command: " + pack.get_command()
00458 );
00459 }
00460 }
00461
00462 template<typename Document, typename Selector>
00463 void basic_client_document_info<Document, Selector>::obby_session_close()
00464 {
00465 session_close_impl();
00466 basic_local_document_info<Document, Selector>::session_close_impl();
00467 basic_document_info<Document, Selector>::session_close_impl();
00468 }
00469
00470 template<typename Document, typename Selector>
00471 void basic_client_document_info<Document, Selector>::
00472 user_subscribe(const user& user)
00473 {
00474
00475 if(m_jupiter.get() != NULL)
00476 m_jupiter->client_add(user);
00477
00478
00479 if(&get_buffer().get_self() == &user)
00480 {
00481
00482
00483
00484 if(base_type::m_document.get() == NULL)
00485 {
00486 throw std::logic_error(
00487 "obby::basic_client_document_info::"
00488 "user_subscribe:\n"
00489 "Document content has not yet been synced"
00490 );
00491 }
00492
00493
00494 m_jupiter.reset(
00495 new jupiter_type(
00496 *basic_document_info<Document, Selector>::
00497 m_document
00498 )
00499 );
00500
00501
00502 for(typename base_type::user_iterator iter =
00503 base_type::user_begin();
00504 iter != base_type::user_end();
00505 ++ iter)
00506 {
00507 m_jupiter->client_add(*iter);
00508 }
00509
00510 m_jupiter->record_event().connect(
00511 sigc::mem_fun(
00512 *this,
00513 &basic_client_document_info::on_jupiter_record
00514 )
00515 );
00516
00517 m_subscription_state = base_local_type::SUBSCRIBED;
00518 }
00519
00520
00521 base_type::user_subscribe(user);
00522 }
00523
00524 template<typename Document, typename Selector>
00525 void basic_client_document_info<Document, Selector>::
00526 user_unsubscribe(const user& user)
00527 {
00528
00529
00530 if(&get_buffer().get_self() == &user)
00531 m_subscription_state = base_local_type::UNSUBSCRIBED;
00532
00533
00534 base_type::user_unsubscribe(user);
00535
00536
00537 if(m_jupiter.get() != NULL)
00538 m_jupiter->client_remove(user);
00539
00540
00541 if(&get_buffer().get_self() == &user)
00542 {
00543
00544 base_type::release_document();
00545
00546 m_jupiter.reset(NULL);
00547 }
00548 }
00549
00550 template<typename Document, typename Selector>
00551 bool basic_client_document_info<Document, Selector>::
00552 execute_packet(const document_packet& pack)
00553 {
00554
00555 if(pack.get_command() == "rename")
00556 { on_net_rename(pack); return true; }
00557
00558 if(pack.get_command() == "record")
00559 { on_net_record(pack); return true; }
00560
00561 if(pack.get_command() == "sync_init")
00562 { on_net_sync_init(pack); return true; }
00563
00564 if(pack.get_command() == "sync_chunk")
00565 { on_net_sync_chunk(pack); return true; }
00566
00567 if(pack.get_command() == "subscribe")
00568 { on_net_subscribe(pack); return true; }
00569
00570 if(pack.get_command() == "unsubscribe")
00571 { on_net_unsubscribe(pack); return true; }
00572
00573 return false;
00574 }
00575
00576 template<typename Document, typename Selector>
00577 void basic_client_document_info<Document, Selector>::
00578 on_net_rename(const document_packet& pack)
00579 {
00580
00581 const std::string& new_title =
00582 pack.get_param(1).net6::parameter::as<std::string>();
00583 unsigned int new_suffix =
00584 pack.get_param(2).net6::parameter::as<unsigned int>();
00585
00586
00587 base_type::document_rename(new_title, new_suffix);
00588 }
00589
00590 template<typename Document, typename Selector>
00591 void basic_client_document_info<Document, Selector>::
00592 on_net_record(const document_packet& pack)
00593 {
00594
00595 if(m_jupiter.get() == NULL)
00596 {
00597 format_string str(
00598 "Got record without being subscribed to document "
00599 "%0%/%1%"
00600 );
00601
00602 str << base_type::get_owner_id() << base_type::get_id();
00603 throw net6::bad_value(str.str() );
00604 }
00605
00606
00607 const user* author = pack.get_param(0).net6::parameter::as<const user*>(
00608 ::serialise::hex_context_from<const user*>(
00609 get_buffer().get_user_table()
00610 )
00611 );
00612
00613
00614
00615 unsigned int index = 1 + 2;
00616 record_type rec(pack, index, base_type::m_buffer.get_user_table() );
00617
00618
00619 m_jupiter->remote_op(rec, author);
00620 }
00621
00622 template<typename Document, typename Selector>
00623 void basic_client_document_info<Document, Selector>::
00624 on_net_sync_init(const document_packet& pack)
00625 {
00626
00627 if(m_subscription_state != base_local_type::SUBSCRIBING)
00628 {
00629 format_string str(
00630 "Got sync_init without having sent a subscription "
00631 "request for document %0%/%1%"
00632 );
00633
00634 str << base_type::get_owner_id() << base_type::get_id();
00635 throw net6::bad_value(str.str() );
00636 }
00637
00638
00639 base_type::assign_document();
00640 }
00641
00642 template<typename Document, typename Selector>
00643 void basic_client_document_info<Document, Selector>::
00644 on_net_sync_chunk(const document_packet& pack)
00645 {
00646
00647 if(base_type::m_document.get() == NULL ||
00648 m_subscription_state != base_local_type::SUBSCRIBING)
00649 {
00650 format_string str(
00651 "Got sync_chunk without sync_init for document %0%/%1%"
00652 );
00653
00654 str << base_type::get_owner_id() << base_type::get_id();
00655 throw net6::bad_value(str.str() );
00656 }
00657
00658
00659 unsigned int index = 2;
00660 base_type::m_document->append(
00661 pack.get_param(0).net6::parameter::as<std::string>(),
00662 pack.get_param(1).net6::parameter::as<const user*>(
00663 ::serialise::hex_context_from<
00664 const user*
00665 >(base_type::m_buffer.get_user_table() )
00666 )
00667 );
00668 }
00669
00670 template<typename Document, typename Selector>
00671 void basic_client_document_info<Document, Selector>::
00672 on_net_subscribe(const document_packet& pack)
00673 {
00674 const user* new_user =
00675 pack.get_param(0).net6::parameter::as<const user*>(
00676 ::serialise::hex_context_from<const user*>(
00677 get_buffer().get_user_table()
00678 )
00679 );
00680
00681
00682
00683
00684 user_subscribe(*new_user);
00685 }
00686
00687 template<typename Document, typename Selector>
00688 void basic_client_document_info<Document, Selector>::
00689 on_net_unsubscribe(const document_packet& pack)
00690 {
00691 const user* old_user =
00692 pack.get_param(0).net6::parameter::as<const user*>(
00693 ::serialise::hex_context_from<const user*>(
00694 get_buffer().get_user_table()
00695 )
00696 );
00697
00698
00699
00700
00701 user_unsubscribe(*old_user);
00702 }
00703
00704 template<typename Document, typename Selector>
00705 void basic_client_document_info<Document, Selector>::
00706 on_jupiter_record(const record_type& rec,
00707 const user* from)
00708 {
00709
00710 document_packet pack(*this, "record");
00711 rec.append_packet(pack);
00712
00713 get_net6().send(pack);
00714 }
00715
00716 template<typename Document, typename Selector>
00717 void basic_client_document_info<Document, Selector>::session_close_impl()
00718 {
00719
00720
00721 m_jupiter.reset(NULL);
00722 }
00723
00724
00725 template<typename Document, typename Selector>
00726 const typename basic_client_document_info<Document, Selector>::buffer_type&
00727 basic_client_document_info<Document, Selector>::get_buffer() const
00728 {
00729 return dynamic_cast<const buffer_type&>(base_type::get_buffer());
00730 }
00731
00732 template<typename Document, typename Selector>
00733 typename basic_client_document_info<Document, Selector>::net_type&
00734 basic_client_document_info<Document, Selector>::get_net6()
00735 {
00736 return dynamic_cast<net_type&>(base_type::get_net6() );
00737 }
00738
00739 template<typename Document, typename Selector>
00740 const typename basic_client_document_info<Document, Selector>::net_type&
00741 basic_client_document_info<Document, Selector>::get_net6() const
00742 {
00743 return dynamic_cast<const net_type&>(base_type::get_net6() );
00744 }
00745
00746 }
00747
00748 #endif // _OBBY_CLIENT_DOCUMENT_INFO_HPP_