text.cpp

Go to the documentation of this file.
00001 /* libobby - Network text editing library
00002  * Copyright (C) 2005, 2006 0x539 dev group
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public
00006  * License as published by the Free Software Foundation; either
00007  * version 2 of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public
00015  * License along with this program; if not, write to the Free
00016  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017  */
00018 
00019 #include "text.hpp"
00020 
00021 namespace
00022 {
00023         const obby::text::size_type CHUNK_INIT =
00024                 ~static_cast<obby::text::size_type>(0);
00025 
00026         inline obby::text::size_type CHUNK_SIZE(obby::text::size_type size)
00027         {
00028                 return size == obby::text::npos ? CHUNK_INIT : size;
00029         }
00030 
00031         // find_chunk implementation to avoid code duplication for
00032         // iterator and const_iterator versions
00033         template<typename List, typename Iter>
00034         Iter find_chunk(List list, obby::text::size_type& pos)
00035         {
00036                 for(Iter it = list.begin(); it != list.end(); ++ it)
00037                 {
00038                         if( (*it)->get_length() > pos)
00039                                 return it;
00040                         else
00041                                 pos -= (*it)->get_length();
00042                 }
00043 
00044                 if(pos == 0) return list.end();
00045 
00046                 throw std::logic_error(
00047                         "obby::text::find_chunk:\n"
00048                         "Requested position exceeds text's size"
00049                 );
00050         }
00051 }
00052 
00053 obby::text::chunk::chunk(const chunk& other):
00054         m_text(other.m_text), m_author(other.m_author)
00055 {
00056 }
00057 
00058 obby::text::chunk::chunk(const string_type& string,
00059                          const user* author):
00060         m_text(string),
00061         m_author(author)
00062 {
00063 }
00064 
00065 obby::text::chunk::chunk(const net6::packet& pack,
00066                          unsigned int& index,
00067                          const user_table& table):
00068         m_text(pack.get_param(index + 0).as<std::string>() ),
00069         m_author(
00070                 pack.get_param(index + 1).as<const user*>(
00071                         ::serialise::hex_context_from<const user*>(table)
00072                 )
00073         )
00074 {
00075         index += 2;
00076 }
00077 
00078 obby::text::chunk::chunk(const serialise::object& obj,
00079                          const user_table& table):
00080         m_text(obj.get_required_attribute("content").as<std::string>() ),
00081         m_author(
00082                 obj.get_required_attribute("author").as<const user*>(
00083                         ::serialise::default_context_from<const user*>(table)
00084                 )
00085         )
00086 {
00087 }
00088 
00089 void obby::text::chunk::serialise(serialise::object& obj) const
00090 {
00091         obj.add_attribute("content").set_value(m_text);
00092         obj.add_attribute("author").set_value(m_author);
00093 }
00094 
00095 void obby::text::chunk::append_packet(net6::packet& pack) const
00096 {
00097         pack << m_text << m_author;
00098 }
00099 
00100 void obby::text::chunk::prepend(const string_type& text)
00101 {
00102         m_text.insert(0, text);
00103 }
00104 
00105 void obby::text::chunk::append(const string_type& text)
00106 {
00107         m_text.append(text);
00108 }
00109 
00110 void obby::text::chunk::insert(size_type pos, const string_type& text)
00111 {
00112         m_text.insert(pos, text);
00113 }
00114 
00115 void obby::text::chunk::erase(size_type pos, size_type len)
00116 {
00117         m_text.erase(pos, len);
00118 }
00119 
00120 const obby::text::string_type& obby::text::chunk::get_text() const
00121 {
00122         return m_text;
00123 }
00124 
00125 const obby::user* obby::text::chunk::get_author() const
00126 {
00127         return m_author;
00128 }
00129 
00130 obby::text::size_type obby::text::chunk::get_length() const
00131 {
00132         return m_text.length();
00133 }
00134 
00135 obby::text::text(size_type initial_chunk_size):
00136         m_max_chunk(CHUNK_SIZE(initial_chunk_size) )
00137 {
00138 }
00139 
00140 obby::text::text(const text& other):
00141         m_max_chunk(other.m_max_chunk)
00142 {
00143         for(list_type::const_iterator iter = other.m_chunks.begin();
00144             iter != other.m_chunks.end();
00145             ++ iter)
00146         {
00147                 m_chunks.push_back(new chunk(**iter) );
00148         }
00149 }
00150 
00151 obby::text::text(const string_type& string,
00152                  const user* author,
00153                  size_type initial_chunk_size):
00154         m_max_chunk(CHUNK_SIZE(initial_chunk_size) )
00155 {
00156         for(size_type n = 0; n < string.length(); ++ n)
00157         {
00158                 size_type len = std::min(string.length() - n, m_max_chunk);
00159                 m_chunks.push_back(new chunk(string.substr(n, len), author) );
00160         }
00161 }
00162 
00163 obby::text::text(const net6::packet& pack,
00164                  unsigned int& index,
00165                  const user_table& table):
00166         m_max_chunk(CHUNK_INIT)
00167 {
00168         unsigned int count = pack.get_param(index ++).as<unsigned int>();
00169         for(unsigned int i = 0; i < count; ++ i)
00170                 m_chunks.push_back(new chunk(pack, index, table) );
00171 }
00172 
00173 obby::text::text(const serialise::object& obj,
00174                  const user_table& table):
00175         m_max_chunk(CHUNK_INIT)
00176 {
00177         for(serialise::object::child_iterator iter = obj.children_begin();
00178             iter != obj.children_end();
00179             ++ iter)
00180         {
00181                 if(iter->get_name() == "chunk")
00182                 {
00183                         m_chunks.push_back(new chunk(*iter, table) );
00184                 }
00185                 else
00186                 {
00187                         // TODO: unexpected_child_error
00188                         format_string str(_("Unexpected child node: '%0%'") );
00189                         str << iter->get_name();
00190                         throw serialise::error(str.str(), iter->get_line() );
00191                 }
00192         }
00193 }
00194 
00195 obby::text::~text()
00196 {
00197         clear();
00198 }
00199 
00200 obby::text& obby::text::operator=(const text& other)
00201 {
00202         if(&other == this) return *this;
00203 
00204         clear();
00205         m_max_chunk = other.m_max_chunk;
00206 
00207         for(list_type::const_iterator iter = other.m_chunks.begin();
00208             iter != other.m_chunks.end();
00209             ++ iter)
00210         {
00211                 m_chunks.push_back(new chunk(**iter) );
00212         }
00213 
00214         return *this;
00215 }
00216 
00217 void obby::text::serialise(serialise::object& obj) const
00218 {
00219         for(list_type::const_iterator it = m_chunks.begin();
00220             it != m_chunks.end();
00221             ++ it)
00222         {
00223                 serialise::object& part = obj.add_child();
00224                 part.set_name("chunk");
00225                 (*it)->serialise(part);
00226         }
00227 }
00228 
00229 void obby::text::append_packet(net6::packet& pack) const
00230 {
00231         pack << m_chunks.size();
00232         for(list_type::const_iterator it = m_chunks.begin();
00233             it != m_chunks.end();
00234             ++ it)
00235         {
00236                 (*it)->append_packet(pack);
00237         }
00238 }
00239 
00240 void obby::text::clear()
00241 {
00242         for(list_type::iterator it = m_chunks.begin();
00243             it != m_chunks.end();
00244             ++ it)
00245         {
00246                 delete *it;
00247         }
00248 
00249         m_chunks.clear();
00250 }
00251 
00252 obby::text obby::text::substr(size_type pos, size_type len) const
00253 {
00254         text new_text;
00255         list_type::const_iterator iter = find_chunk(pos);
00256 
00257         chunk* prev_chunk = NULL;
00258         while( (len == npos || len > 0) && (iter != m_chunks.end()) )
00259         {
00260                 chunk* cur_chunk = *iter;
00261                 size_type count = cur_chunk->get_length() - pos;
00262 
00263                 if(len != npos)
00264                 {
00265                         count = std::min(count, len);
00266                         len -= count;
00267                 }
00268 
00269                 if(prev_chunk != NULL &&
00270                    prev_chunk->get_author() == cur_chunk->get_author() &&
00271                    prev_chunk->get_length() + cur_chunk->get_length() <=
00272                    m_max_chunk)
00273                 {
00274                         prev_chunk->append(
00275                                 cur_chunk->get_text().substr(pos, count)
00276                         );
00277                 }
00278                 else
00279                 {
00280                         prev_chunk = new chunk(
00281                                 cur_chunk->get_text().substr(pos, count),
00282                                 cur_chunk->get_author()
00283                         );
00284 
00285                         new_text.m_chunks.push_back(prev_chunk);
00286                 }
00287 
00288                 ++ iter; pos = 0;
00289         }
00290 
00291         if(len > 0 && len != npos)
00292         {
00293                 throw std::logic_error(
00294                         "obby::text::substr:\n"
00295                         "len is out or range"
00296                 );
00297         }
00298 
00299         return new_text;
00300 }
00301 
00302 void obby::text::insert(size_type pos,
00303                         const string_type& str,
00304                         const user* author)
00305 {
00306         list_type::iterator ins_pos = find_chunk(pos);
00307         insert_chunk(ins_pos, pos, str, author);
00308 }
00309 
00310 void obby::text::insert(size_type pos,
00311                         const text& str)
00312 {
00313         list_type::iterator ins_pos = find_chunk(pos);
00314         for(list_type::const_iterator it = str.m_chunks.begin();
00315             it != str.m_chunks.end();
00316             ++ it)
00317         {
00318                 ins_pos = insert_chunk(
00319                         ins_pos,
00320                         pos,
00321                         (*it)->get_text(),
00322                         (*it)->get_author()
00323                 );
00324         }
00325 }
00326 
00327 void obby::text::erase(size_type pos, size_type len)
00328 {
00329         list_type::iterator ers_pos = find_chunk(pos);
00330         list_type::iterator first_chunk = ers_pos;
00331         size_type first_pos = pos;
00332 
00333         // Remember first chunk position that will not be removed.
00334         // After having erased a chunk in the middle, something may have
00335         // been mergen with this first chunk that should have been deleted.
00336         // We watch it and remove merged stuff if the chunk grew.
00337         if(first_pos == 0 && first_chunk != m_chunks.begin() )
00338         {
00339                 -- first_chunk;
00340                 first_pos = (*first_chunk)->get_length();
00341         }
00342 
00343         while( (len == npos || len > 0) && ers_pos != m_chunks.end() )
00344         {
00345                 size_type count = (*ers_pos)->get_length() - pos;
00346                 if(len != npos)
00347                 {
00348                         count = std::min(count, len);
00349                         len -= count;
00350                 }
00351 
00352                 ers_pos = erase_chunk(ers_pos, pos, count);
00353 
00354                 if(first_pos > 0 && (*first_chunk)->get_length() > first_pos)
00355                 {
00356                         ers_pos = first_chunk;
00357                         pos = first_pos;
00358                 }
00359                 else
00360                 {
00361                         pos = 0;
00362                 }
00363         }
00364 
00365         if(len != npos && len > 0)
00366         {
00367                 throw std::logic_error(
00368                         "obby::text::erase:\n"
00369                         "len is out of range"
00370                 );
00371         }
00372 }
00373 
00374 void obby::text::append(const string_type& str,
00375                         const user* author)
00376 {
00377         chunk* last_chunk = NULL;
00378         if(!m_chunks.empty() ) last_chunk = *m_chunks.rbegin();
00379         size_type pos = 0;
00380 
00381         // Merge beginning of str with last chunk if possible
00382         if(last_chunk != NULL &&
00383            last_chunk->get_author() == author &&
00384            last_chunk->get_length() < m_max_chunk)
00385         {
00386                 pos = std::min(
00387                         m_max_chunk - last_chunk->get_length(),
00388                         str.length()
00389                 );
00390 
00391                 last_chunk->append(str.substr(0, pos) );
00392         }
00393 
00394         // Append rest of string
00395         for(; pos < str.length(); pos += m_max_chunk)
00396         {
00397                 size_type count = std::min(str.length() - pos, m_max_chunk);
00398                 m_chunks.push_back(new chunk(str.substr(pos, count), author) );
00399         }
00400 }
00401 
00402 void obby::text::append(const text& str)
00403 {
00404         // Simply append all chunks of str
00405         for(list_type::const_iterator it = str.m_chunks.begin();
00406             it != str.m_chunks.end();
00407             ++ it)
00408         {
00409                 append( (*it)->get_text(), (*it)->get_author() );
00410         }
00411 }
00412 
00413 void obby::text::prepend(const string_type& str,
00414                          const user* author)
00415 {
00416         chunk* first_chunk = NULL;
00417         if(!m_chunks.empty() ) first_chunk = *m_chunks.begin();
00418 
00419         size_type len = str.length();
00420 
00421         // Prepend end of str to first chunk if possible
00422         if(first_chunk != NULL &&
00423            first_chunk->get_author() == author &&
00424            first_chunk->get_length() < m_max_chunk)
00425         {
00426                 size_type count = std::min(
00427                         m_max_chunk - first_chunk->get_length(),
00428                         len
00429                 );
00430 
00431                 len -= count;
00432                 first_chunk->prepend(str.substr(len, count) );
00433         }
00434 
00435         // Insert chunks before for the rest of str
00436         while(len > 0)
00437         {
00438                 size_type count = std::min(
00439                         len,
00440                         m_max_chunk
00441                 );
00442 
00443                 len -= count;
00444                 m_chunks.push_front(new chunk(str.substr(len, count), author));
00445         }
00446 }
00447 
00448 void obby::text::prepend(const text& str)
00449 {
00450         for(list_type::const_reverse_iterator it = str.m_chunks.rbegin();
00451             it != str.m_chunks.rend();
00452             ++ it)
00453         {
00454                 prepend( (*it)->get_text(), (*it)->get_author() );
00455         }
00456 }
00457 
00458 obby::text::size_type obby::text::length() const
00459 {
00460         // TODO: Cache this value?
00461         size_type len = 0;
00462         for(list_type::const_iterator it = m_chunks.begin();
00463             it != m_chunks.end();
00464             ++ it)
00465         {
00466                 len += (*it)->get_length();
00467         }
00468 
00469         return len;
00470 }
00471 
00472 bool obby::text::operator==(const text& other) const
00473 {
00474         return compare(other) == EQUAL_OWNERMATCH;
00475 }
00476 
00477 bool obby::text::operator!=(const text& other) const
00478 {
00479         return compare(other) != EQUAL_OWNERMATCH;
00480 }
00481 
00482 bool obby::text::operator<(const text& other) const
00483 {
00484         return compare(other) == LESS;
00485 }
00486 
00487 bool obby::text::operator>(const text& other) const
00488 {
00489         return compare(other) == GREATER;
00490 }
00491 
00492 bool obby::text::operator<=(const text& other) const
00493 {
00494         return compare(other) != GREATER;
00495 }
00496 
00497 bool obby::text::operator>=(const text& other) const
00498 {
00499         return compare(other) != LESS;
00500 }
00501 
00502 bool obby::text::operator==(const string_type& other) const
00503 {
00504         return compare(other) == EQUAL;
00505 }
00506 
00507 bool obby::text::operator!=(const string_type& other) const
00508 {
00509         return compare(other) != EQUAL;
00510 }
00511 
00512 bool obby::text::operator<(const string_type& other) const
00513 {
00514         return compare(other) == LESS;
00515 }
00516 
00517 bool obby::text::operator>(const string_type& other) const
00518 {
00519         return compare(other) == GREATER;
00520 }
00521 
00522 bool obby::text::operator<=(const string_type& other) const
00523 {
00524         return compare(other) != GREATER;
00525 }
00526 
00527 bool obby::text::operator>=(const string_type& other) const
00528 {
00529         return compare(other) != LESS;
00530 }
00531 
00532 bool obby::text::empty() const
00533 {
00534         return m_chunks.empty();
00535 }
00536 
00537 obby::text::chunk_iterator obby::text::chunk_begin() const
00538 {
00539         return chunk_iterator(m_chunks.begin() );
00540 }
00541 
00542 obby::text::chunk_iterator obby::text::chunk_end() const
00543 {
00544         return chunk_iterator(m_chunks.end() );
00545 }
00546 
00547 void obby::text::set_max_chunk_size(size_type max_chunk)
00548 {
00549         m_max_chunk = max_chunk;
00550         if(m_chunks.empty() ) return;
00551 
00552         // Merge and/or split current chunks
00553         list_type::iterator next = m_chunks.begin(); ++ next;
00554         for(list_type::iterator it = m_chunks.begin();
00555             it != m_chunks.end();
00556             ++ it, ++ next)
00557         {
00558                 chunk* cur_chunk = *it;
00559 
00560                 chunk* next_chunk = NULL;
00561                 if(next != m_chunks.end() ) next_chunk = *next;
00562 
00563                 // Split current chunk if necessary
00564                 if(cur_chunk->get_length() > m_max_chunk)
00565                 {
00566                         size_type pos = m_max_chunk;
00567                         while(cur_chunk->get_length() - pos > 0)
00568                         {
00569                                 // Merge with next if possible
00570                                 if(next_chunk != NULL &&
00571                                    next_chunk->get_author() ==
00572                                    cur_chunk->get_author() &&
00573                                    cur_chunk->get_length() - pos +
00574                                    next_chunk->get_length() <= m_max_chunk)
00575                                 {
00576                                         // Note that this could also be done
00577                                         // in the merging process below but
00578                                         // then we would split the chunk up
00579                                         // just to merge it then...
00580                                         next_chunk->prepend(
00581                                                 cur_chunk->get_text().substr(
00582                                                         pos
00583                                                 )
00584                                         );
00585 
00586                                         pos += (cur_chunk->get_length() - pos);
00587                                 }
00588                                 // Split otherwise
00589                                 else
00590                                 {
00591                                         size_type len = std::min(
00592                                                 cur_chunk->get_length() - pos,
00593                                                 m_max_chunk
00594                                         );
00595 
00596                                         const std::string& text =
00597                                                 cur_chunk->get_text();
00598 
00599                                         it = m_chunks.insert(
00600                                                 next,
00601                                                 new chunk(
00602                                                         text.substr(
00603                                                                 pos,
00604                                                                 len
00605                                                         ),
00606                                                         cur_chunk->get_author()
00607                                                 )
00608                                         );
00609 
00610                                         pos += len;
00611                                 }
00612 
00613                         }
00614 
00615                         // Remove splitted/merged stuff from current one
00616                         cur_chunk->erase(m_max_chunk);
00617                         cur_chunk = *it;
00618                 }
00619                 // Merge chunk with next
00620                 else if(next_chunk != NULL &&
00621                         cur_chunk->get_author() == next_chunk->get_author() &&
00622                         cur_chunk->get_length() + next_chunk->get_length() <=
00623                         m_max_chunk)
00624                 {
00625                         cur_chunk->append(next_chunk->get_text() );
00626 
00627                         delete next_chunk;
00628                         next = m_chunks.erase(next);
00629                 }
00630         }
00631 }
00632 
00633 obby::text::operator string_type() const
00634 {
00635         string_type str;
00636         str.reserve(length() );
00637 
00638         for(list_type::const_iterator it = m_chunks.begin();
00639             it != m_chunks.end();
00640             ++ it)
00641         {
00642                 str.append( (*it)->get_text() );
00643         }
00644 
00645         return str;
00646 }
00647 
00648 obby::text::list_type::iterator
00649 obby::text::find_chunk(size_type& pos)
00650 {
00651         return ::find_chunk<list_type&, list_type::iterator>(
00652                 m_chunks,
00653                 pos
00654         );
00655 }
00656 
00657 obby::text::list_type::const_iterator
00658 obby::text::find_chunk(size_type& pos) const
00659 {
00660         return ::find_chunk<const list_type&, list_type::const_iterator>(
00661                 m_chunks,
00662                 pos
00663         );
00664 }
00665 
00666 obby::text::list_type::iterator
00667 obby::text::insert_chunk(list_type::iterator chunk_it,
00668                          size_type& chunk_pos,
00669                          const string_type& str,
00670                          const user* author)
00671 {
00672         chunk* cur_chunk = NULL;
00673         if(chunk_it != m_chunks.end() ) cur_chunk = *chunk_it;
00674 
00675         list_type::iterator ins_pos = chunk_it;
00676 
00677         // Get previous chunk
00678         chunk* prev_chunk = NULL;
00679         list_type::iterator prev_pos = ins_pos;
00680         if(prev_pos != m_chunks.begin() )
00681         {
00682                 -- prev_pos;
00683                 prev_chunk = *prev_pos;
00684         }
00685 
00686         // Merge with previous
00687         if(prev_chunk != NULL &&
00688            chunk_pos == 0 &&
00689            author == prev_chunk->get_author() &&
00690            str.length() + prev_chunk->get_length() <= m_max_chunk)
00691         {
00692                 prev_chunk->append(str);
00693                 return chunk_it;
00694         }
00695         else if(cur_chunk == NULL)
00696         {
00697                 // Insertion at end (no current chunk) and cannot merge with
00698                 // previous: Need to create a new one, do nothing here - this
00699                 // is done below
00700         }
00701         // Merge with current
00702         else if(author == cur_chunk->get_author() &&
00703                 str.length() + cur_chunk->get_length() <= m_max_chunk)
00704         {
00705                 cur_chunk->insert(chunk_pos, str);
00706                 chunk_pos += str.length();
00707                 return chunk_it;
00708         }
00709         // Insert new chunk after current chunk if str is inserted at
00710         // end of current chunk
00711         else if(chunk_pos == cur_chunk->get_length() )
00712         {
00713                 ++ ins_pos;
00714         }
00715         // Insert new chunk before current chunk if str is inserted at
00716         // the beginning of current chunk
00717         else if(chunk_pos > 0)
00718         {
00719                 // Split up otherwise
00720                 chunk* new_chunk = new chunk(
00721                         cur_chunk->get_text().substr(chunk_pos),
00722                         cur_chunk->get_author()
00723                 );
00724 
00725                 cur_chunk->erase(chunk_pos);
00726                 chunk_pos = 0;
00727 
00728                 ++ ins_pos;
00729                 ins_pos = m_chunks.insert(
00730                         ins_pos,
00731                         new_chunk
00732                 );
00733 
00734                 // Try to merge with both chunks - they may be smaller
00735                 // and thus fit into max chunk size
00736                 if(cur_chunk->get_author() == author)
00737                 {
00738                         if(cur_chunk->get_length() + str.length() <=
00739                            m_max_chunk)
00740                         {
00741                                 cur_chunk->append(str);
00742                                 chunk_pos = cur_chunk->get_length();
00743                                 -- ins_pos;
00744                                 return ins_pos;
00745                         }
00746                         else if(new_chunk->get_length() +
00747                                 str.length() <= m_max_chunk)
00748                         {
00749                                 new_chunk->prepend(str);
00750                                 chunk_pos = str.length();
00751                                 return ins_pos;
00752                         }
00753                 }
00754 
00755                 // If not insert another new chunk between
00756                 // the chunks split up - ins_pos points already
00757                 // to the correct position
00758         }
00759 
00760         // Insert one new chunk if str fits into
00761         if(str.length() <= m_max_chunk)
00762         {
00763                 chunk_pos = 0;
00764                 m_chunks.insert(ins_pos, new chunk(str, author) );
00765                 return ins_pos;
00766         }
00767         else
00768         {
00769                 // Make multiple chunks otherwise
00770                 // TODO: Fill up previous chunk if author matches and ins_pos
00771                 // is != m_chunks.begin()
00772                 cur_chunk = ( (ins_pos == m_chunks.end()) ? NULL : *ins_pos);
00773                 for(size_type n = 0; n < str.length(); n += m_max_chunk)
00774                 {
00775                         size_type len = std::min(str.length() - n, m_max_chunk);
00776 
00777                         // Merge with next
00778                         if(cur_chunk &&
00779                            cur_chunk->get_author() == author &&
00780                            len + cur_chunk->get_length() <= m_max_chunk)
00781                         {
00782                                 // Must be last chunk to insert since all
00783                                 // others are m_max_chunk in size and thus
00784                                 // may not be merged
00785                                 cur_chunk->prepend(str.substr(n, len) );
00786                                 chunk_pos = len;
00787                                 return ins_pos;
00788                         }
00789                         else
00790                         {
00791                                 /*result =*/ m_chunks.insert(
00792                                         ins_pos,
00793                                         new chunk(str.substr(n, len), author)
00794                                 );
00795                         }
00796                 }
00797 
00798                 chunk_pos = 0;
00799                 return ins_pos;
00800         }
00801 }
00802 
00803 obby::text::list_type::iterator
00804 obby::text::erase_chunk(list_type::iterator chunk_it,
00805                         size_type pos,
00806                         size_type len)
00807 {
00808         chunk* prev_chunk = NULL;
00809         chunk* next_chunk = NULL;
00810 
00811         list_type::iterator prev_it = chunk_it;
00812         if(prev_it != m_chunks.begin() ) { -- prev_it; prev_chunk = *prev_it; }
00813 
00814         list_type::iterator next_it = chunk_it;
00815         ++ next_it;
00816         if(next_it != m_chunks.end() ) { next_chunk = *next_it; }
00817 
00818         chunk* cur_chunk = *chunk_it;
00819         //if(len == npos) len = cur_chunk->get_length() - pos;
00820 
00821         if(pos + len > cur_chunk->get_length() )
00822         {
00823                 throw std::logic_error(
00824                         "obby::text::erase_chunk:\n"
00825                         "Chunk len exceeded"
00826                 );
00827         }
00828 
00829         // Complete erasure
00830         if(len == cur_chunk->get_length() )
00831         {
00832                 delete cur_chunk;
00833                 m_chunks.erase(chunk_it);
00834 
00835                 // Merge surrounding chunks if possible
00836                 if(next_chunk != NULL && prev_chunk != NULL &&
00837                    next_chunk->get_author() == prev_chunk->get_author() &&
00838                    next_chunk->get_length() + prev_chunk->get_length() <
00839                    m_max_chunk)
00840                 {
00841                         prev_chunk->append(next_chunk->get_text() );
00842 
00843                         delete next_chunk;
00844                         next_it = m_chunks.erase(next_it);
00845                 }
00846 
00847                 return next_it;
00848         }
00849 
00850         // Merge with previous
00851         if(prev_chunk != NULL &&
00852            prev_chunk->get_author() == cur_chunk->get_author() &&
00853            cur_chunk->get_length() - len + prev_chunk->get_length() <
00854            m_max_chunk)
00855         {
00856                 if(pos > 0)
00857                 {
00858                         prev_chunk->append(
00859                                 cur_chunk->get_text().substr(0, pos)
00860                         );
00861                 }
00862 
00863                 if(pos + len < cur_chunk->get_length() )
00864                 {
00865                         prev_chunk->append(
00866                                 cur_chunk->get_text().substr(pos + len)
00867                         );
00868                 }
00869 
00870                 delete cur_chunk;
00871                 m_chunks.erase(chunk_it);
00872 
00873                 // Merge result with next if possible
00874                 if(next_chunk != NULL &&
00875                    prev_chunk->get_author() == next_chunk->get_author() &&
00876                    prev_chunk->get_length() + next_chunk->get_length() <=
00877                    m_max_chunk)
00878                 {
00879                         prev_chunk->append(next_chunk->get_text() );
00880 
00881                         delete next_chunk;
00882                         next_it = m_chunks.erase(next_it);
00883                 }
00884 
00885                 return next_it;
00886         }
00887 
00888         // Merge with next
00889         if(next_chunk != NULL &&
00890            next_chunk->get_author() == cur_chunk->get_author() &&
00891            cur_chunk->get_length() - len + next_chunk->get_length() <
00892            m_max_chunk)
00893         {
00894                 if(pos + len < cur_chunk->get_length() )
00895                 {
00896                         next_chunk->prepend(
00897                                 cur_chunk->get_text().substr(pos)
00898                         );
00899                 }
00900 
00901                 if(pos > 0)
00902                 {
00903                         next_chunk->prepend(
00904                                 cur_chunk->get_text().substr(0, pos)
00905                         );
00906                 }
00907 
00908                 delete cur_chunk;
00909                 m_chunks.erase(chunk_it);
00910 
00911                 // No need to try to merge with previous since the check
00912                 // above would already have done it.
00913 
00914                 ++ next_it;
00915                 return next_it;
00916         }
00917 
00918         // No merging possible...
00919         cur_chunk->erase(pos, len);
00920         return next_it;
00921 }
00922 
00923 obby::text::compare_result obby::text::compare(const text& other) const
00924 {
00925         list_type::const_iterator it1 = m_chunks.begin();
00926         list_type::const_iterator it2 = other.m_chunks.begin();
00927 
00928         size_type pos1 = 0, pos2 = 0;
00929         bool author_match = true;
00930 
00931         while(it1 != m_chunks.end() && it2 != other.m_chunks.end() )
00932         {
00933                 // Authors do not match: Remember this for later use. If
00934                 // the strings differ, LESS or GREATER is returned; author
00935                 // match does only play a role when the text content is equal
00936                 if( (*it1)->get_author() != (*it2)->get_author() )
00937                         author_match = false;
00938 
00939                 size_type len = std::min(
00940                         (*it1)->get_length() - pos1,
00941                         (*it2)->get_length() - pos2
00942                 );
00943 
00944                 int res = (*it1)->get_text().compare(
00945                         pos1,
00946                         len,
00947                         (*it2)->get_text(),
00948                         pos2,
00949                         len
00950                 );
00951 
00952                 if(res != 0) return res < 0 ? LESS : GREATER;
00953 
00954                 pos1 += len;
00955                 pos2 += len;
00956 
00957                 if(pos1 == (*it1)->get_length() )
00958                         { ++ it1; pos1 = 0; }
00959                 if(pos2 == (*it2)->get_length() )
00960                         { ++ it2; pos2 = 0; }
00961         }
00962 
00963         // *this has more length than other
00964         if(it1 != m_chunks.end() )
00965                 return GREATER;
00966         // other has more length
00967         else if(it2 != other.m_chunks.end() )
00968                 return LESS;
00969         // both text's content are equal
00970         else
00971                 return author_match ? EQUAL_OWNERMATCH : EQUAL;
00972 }
00973 
00974 obby::text::compare_result obby::text::compare(const string_type& text) const
00975 {
00976         size_type pos = 0;
00977         for(list_type::const_iterator it = m_chunks.begin();
00978             it != m_chunks.end();
00979             ++ it)
00980         {
00981                 size_type len = (*it)->get_length();
00982                 int res = text.compare(pos, len, (*it)->get_text());
00983                 if(res != 0) return res < 0 ? LESS : GREATER;
00984                 pos += len;
00985         }
00986 
00987         return EQUAL;
00988 }

Generated on Fri Jan 11 10:01:32 2008 for obby by  doxygen 1.5.1