00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <config.h>
00025
00026 #include "autoptr.h"
00027
00028 #include <xapian/error.h>
00029 #include <xapian/positioniterator.h>
00030 #include <xapian/postingiterator.h>
00031 #include <xapian/termiterator.h>
00032 #include <xapian/unicode.h>
00033
00034 #include "omassert.h"
00035 #include "omdebug.h"
00036 #include "../backends/multi/multi_postlist.h"
00037 #include "../backends/multi/multi_termlist.h"
00038 #include "alltermslist.h"
00039 #include "multialltermslist.h"
00040 #include "database.h"
00041 #include "editdistance.h"
00042 #include "ortermlist.h"
00043 #include "noreturn.h"
00044
00045 #include <stdlib.h>
00046
00047 #include <cstring>
00048 #include <vector>
00049
00050 using namespace std;
00051
00052 XAPIAN_NORETURN(static void no_subdatabases());
00053 static void no_subdatabases()
00054 {
00055 throw Xapian::DocNotFoundError("No subdatabases");
00056 }
00057
00058 namespace Xapian {
00059
00060 Database::Database()
00061 {
00062 DEBUGAPICALL(void, "Database::Database", "");
00063 }
00064
00065 Database::Database(Database::Internal *internal_)
00066 {
00067 DEBUGAPICALL(void, "Database::Database", "Database::Internal");
00068 Xapian::Internal::RefCntPtr<Database::Internal> newi(internal_);
00069 internal.push_back(newi);
00070 }
00071
00072 Database::Database(const Database &other)
00073 {
00074 DEBUGAPICALL(void, "Database::Database", "Database");
00075 internal = other.internal;
00076 }
00077
00078 void
00079 Database::operator=(const Database &other)
00080 {
00081 DEBUGAPICALL(void, "Database::operator=", "Database");
00082 if (this == &other) {
00083 DEBUGLINE(API, "Database assigned to itself");
00084 return;
00085 }
00086
00087 internal = other.internal;
00088 }
00089
00090 Database::~Database()
00091 {
00092 DEBUGAPICALL(void, "Database::~Database", "");
00093 }
00094
00095 void
00096 Database::reopen()
00097 {
00098 DEBUGAPICALL(void, "Database::reopen", "");
00099 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::iterator i;
00100 for (i = internal.begin(); i != internal.end(); ++i) {
00101 (*i)->reopen();
00102 }
00103 }
00104
00105 void
00106 Database::add_database(const Database & database)
00107 {
00108 DEBUGAPICALL(void, "Database::add_database", "Database");
00109 if (this == &database) {
00110 DEBUGLINE(API, "Database added to itself");
00111 throw Xapian::InvalidArgumentError("Can't add an Database to itself");
00112 return;
00113 }
00114 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00115 for (i = database.internal.begin(); i != database.internal.end(); ++i) {
00116 internal.push_back(*i);
00117 }
00118 }
00119
00120 PostingIterator
00121 Database::postlist_begin(const string &tname) const
00122 {
00123 DEBUGAPICALL(PostingIterator, "Database::postlist_begin", tname);
00124
00125
00126
00127
00128
00129
00130 if (internal.size() == 1)
00131 RETURN(PostingIterator(internal[0]->open_post_list(tname)));
00132
00133 if (rare(internal.size() == 0))
00134 RETURN(PostingIterator(NULL));
00135
00136 vector<LeafPostList *> pls;
00137 try {
00138 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00139 for (i = internal.begin(); i != internal.end(); ++i) {
00140 pls.push_back((*i)->open_post_list(tname));
00141 pls.back()->next();
00142 }
00143 Assert(pls.begin() != pls.end());
00144 } catch (...) {
00145 vector<LeafPostList *>::iterator i;
00146 for (i = pls.begin(); i != pls.end(); ++i) {
00147 delete *i;
00148 *i = 0;
00149 }
00150 throw;
00151 }
00152
00153 RETURN(PostingIterator(new MultiPostList(pls, *this)));
00154 }
00155
00156 TermIterator
00157 Database::termlist_begin(Xapian::docid did) const
00158 {
00159 DEBUGAPICALL(TermIterator, "Database::termlist_begin", did);
00160 if (did == 0) throw InvalidArgumentError("Document ID 0 is invalid");
00161
00162 unsigned int multiplier = internal.size();
00163 if (rare(multiplier == 0))
00164 no_subdatabases();
00165 TermList *tl;
00166 if (multiplier == 1) {
00167
00168
00169 tl = internal[0]->open_term_list(did);
00170 } else {
00171 Assert(multiplier != 0);
00172 Xapian::doccount n = (did - 1) % multiplier;
00173 Xapian::docid m = (did - 1) / multiplier + 1;
00174
00175 tl = new MultiTermList(internal[n]->open_term_list(m), *this, n);
00176 }
00177 RETURN(TermIterator(tl));
00178 }
00179
00180 TermIterator
00181 Database::allterms_begin() const
00182 {
00183 return allterms_begin("");
00184 }
00185
00186 TermIterator
00187 Database::allterms_begin(const std::string & prefix) const
00188 {
00189 DEBUGAPICALL(TermIterator, "Database::allterms_begin", "");
00190 TermList * tl;
00191 if (rare(internal.size() == 0)) {
00192 tl = NULL;
00193 } else if (internal.size() == 1) {
00194 tl = internal[0]->open_allterms(prefix);
00195 } else {
00196 tl = new MultiAllTermsList(internal, prefix);
00197 }
00198 RETURN(TermIterator(tl));
00199 }
00200
00201 bool
00202 Database::has_positions() const
00203 {
00204 DEBUGAPICALL(bool, "Database::has_positions", "");
00205
00206 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00207 for (i = internal.begin(); i != internal.end(); ++i) {
00208 if ((*i)->has_positions()) RETURN(true);
00209 }
00210 RETURN(false);
00211 }
00212
00213 PositionIterator
00214 Database::positionlist_begin(Xapian::docid did, const string &tname) const
00215 {
00216 DEBUGAPICALL(PositionIterator, "Database::positionlist_begin",
00217 did << ", " << tname);
00218 if (tname.empty())
00219 throw InvalidArgumentError("Zero length terms are invalid");
00220 if (did == 0) throw InvalidArgumentError("Document ID 0 is invalid");
00221
00222 unsigned int multiplier = internal.size();
00223 if (rare(multiplier == 0))
00224 no_subdatabases();
00225 Xapian::doccount n = (did - 1) % multiplier;
00226 Xapian::docid m = (did - 1) / multiplier + 1;
00227 RETURN(PositionIterator(internal[n]->open_position_list(m, tname)));
00228 }
00229
00230 Xapian::doccount
00231 Database::get_doccount() const
00232 {
00233 DEBUGAPICALL(Xapian::doccount, "Database::get_doccount", "");
00234 Xapian::doccount docs = 0;
00235 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00236 for (i = internal.begin(); i != internal.end(); ++i) {
00237 docs += (*i)->get_doccount();
00238 }
00239 RETURN(docs);
00240 }
00241
00242 Xapian::docid
00243 Database::get_lastdocid() const
00244 {
00245 DEBUGAPICALL(Xapian::docid, "Database::get_lastdocid", "");
00246 Xapian::docid did = 0;
00247
00248 unsigned int multiplier = internal.size();
00249 for (Xapian::doccount i = 0; i < multiplier; ++i) {
00250 Xapian::docid did_i = internal[i]->get_lastdocid();
00251 if (did_i) did = std::max(did, (did_i - 1) * multiplier + i + 1);
00252 }
00253 RETURN(did);
00254 }
00255
00256 Xapian::doclength
00257 Database::get_avlength() const
00258 {
00259 DEBUGAPICALL(Xapian::doclength, "Database::get_avlength", "");
00260 Xapian::doccount docs = 0;
00261 Xapian::doclength totlen = 0;
00262
00263 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00264 for (i = internal.begin(); i != internal.end(); ++i) {
00265 Xapian::doccount db_doccount = (*i)->get_doccount();
00266 docs += db_doccount;
00267 totlen += (*i)->get_avlength() * db_doccount;
00268 }
00269 DEBUGLINE(UNKNOWN, "get_avlength() = " << totlen << " / " << docs <<
00270 " (from " << internal.size() << " dbs)");
00271
00272 if (docs == 0) RETURN(0.0);
00273 RETURN(totlen / docs);
00274 }
00275
00276 Xapian::doccount
00277 Database::get_termfreq(const string & tname) const
00278 {
00279 DEBUGAPICALL(Xapian::doccount, "Database::get_termfreq", tname);
00280 if (tname.empty()) RETURN(get_doccount());
00281
00282 Xapian::doccount tf = 0;
00283 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00284 for (i = internal.begin(); i != internal.end(); i++) {
00285 tf += (*i)->get_termfreq(tname);
00286 }
00287 RETURN(tf);
00288 }
00289
00290 Xapian::termcount
00291 Database::get_collection_freq(const string & tname) const
00292 {
00293 DEBUGAPICALL(Xapian::termcount, "Database::get_collection_freq", tname);
00294 if (tname.empty()) RETURN(get_doccount());
00295
00296 Xapian::termcount cf = 0;
00297 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00298 for (i = internal.begin(); i != internal.end(); i++) {
00299 cf += (*i)->get_collection_freq(tname);
00300 }
00301 RETURN(cf);
00302 }
00303
00304 Xapian::doclength
00305 Database::get_doclength(Xapian::docid did) const
00306 {
00307 DEBUGAPICALL(Xapian::doclength, "Database::get_doclength", did);
00308 if (did == 0) throw InvalidArgumentError("Document ID 0 is invalid");
00309
00310 unsigned int multiplier = internal.size();
00311 if (rare(multiplier == 0))
00312 no_subdatabases();
00313 Xapian::doccount n = (did - 1) % multiplier;
00314 Xapian::docid m = (did - 1) / multiplier + 1;
00315 RETURN(internal[n]->get_doclength(m));
00316 }
00317
00318 Document
00319 Database::get_document(Xapian::docid did) const
00320 {
00321 DEBUGAPICALL(Document, "Database::get_document", did);
00322 if (did == 0) throw InvalidArgumentError("Document ID 0 is invalid");
00323
00324 unsigned int multiplier = internal.size();
00325 if (rare(multiplier == 0))
00326 no_subdatabases();
00327 Xapian::doccount n = (did - 1) % multiplier;
00328 Xapian::docid m = (did - 1) / multiplier + 1;
00329
00330
00331 RETURN(Document(internal[n]->open_document(m, false)));
00332 }
00333
00334 bool
00335 Database::term_exists(const string & tname) const
00336 {
00337 DEBUGAPICALL(bool, "Database::term_exists", tname);
00338 if (tname.empty()) {
00339 RETURN(get_doccount() != 0);
00340 }
00341 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00342 for (i = internal.begin(); i != internal.end(); ++i) {
00343 if ((*i)->term_exists(tname)) RETURN(true);
00344 }
00345 RETURN(false);
00346 }
00347
00348 void
00349 Database::keep_alive()
00350 {
00351 DEBUGAPICALL(void, "Database::keep_alive", "");
00352 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00353 for (i = internal.begin(); i != internal.end(); ++i) {
00354 (*i)->keep_alive();
00355 }
00356 }
00357
00358 string
00359 Database::get_description() const
00360 {
00362 return "Database()";
00363 }
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375 #define VEC_SIZE 64
00376
00377 static int
00378 freq_edit_lower_bound(const vector<unsigned> & a, const vector<unsigned> & b)
00379 {
00380 int vec[VEC_SIZE];
00381 memset(vec, 0, sizeof(vec));
00382 vector<unsigned>::const_iterator i;
00383 for (i = a.begin(); i != a.end(); ++i) {
00384 ++vec[(*i) % VEC_SIZE];
00385 }
00386 for (i = b.begin(); i != b.end(); ++i) {
00387 --vec[(*i) % VEC_SIZE];
00388 }
00389 unsigned int total = 0;
00390 for (size_t j = 0; j < VEC_SIZE; ++j) {
00391 total += abs(vec[j]);
00392 }
00393
00394
00395
00396
00397 return (total + 1) / 2;
00398 }
00399
00400
00401
00402 #define TRIGRAM_SCORE_THRESHOLD 2
00403
00404 string
00405 Database::get_spelling_suggestion(const string &word,
00406 unsigned max_edit_distance) const
00407 {
00408 DEBUGAPICALL(string, "Database::get_spelling_suggestion",
00409 word << ", " << max_edit_distance);
00410 if (word.size() <= 1) return string();
00411 AutoPtr<TermList> merger;
00412 for (size_t i = 0; i < internal.size(); ++i) {
00413 TermList * tl = internal[i]->open_spelling_termlist(word);
00414 DEBUGLINE(SPELLING, "Sub db " << i << " tl = " << (void*)tl);
00415 if (tl) {
00416 if (merger.get()) {
00417 merger = new OrTermList(merger.release(), tl);
00418 } else {
00419 merger = tl;
00420 }
00421 }
00422 }
00423 if (!merger.get()) RETURN(string());
00424
00425
00426 #ifdef __SUNPRO_CC
00427 vector<unsigned> utf32_word;
00428 for (Utf8Iterator sunpro_it(word); sunpro_it != Utf8Iterator(); ++sunpro_it) {
00429 utf32_word.push_back(*sunpro_it);
00430 }
00431 #else
00432
00433
00434 vector<unsigned> utf32_word((Utf8Iterator(word)), Utf8Iterator());
00435 #endif
00436
00437 vector<unsigned> utf32_term;
00438
00439 Xapian::termcount best = 1;
00440 string result;
00441 int edist_best = max_edit_distance;
00442 Xapian::doccount freq_best = 0;
00443 while (true) {
00444 TermList *ret = merger->next();
00445 if (ret) merger = ret;
00446
00447 if (merger->at_end()) break;
00448
00449 string term = merger->get_termname();
00450 Xapian::termcount score = merger->get_wdf();
00451
00452 DEBUGLINE(SPELLING, "Term \"" << term << "\" ngram score " << score);
00453 if (score + TRIGRAM_SCORE_THRESHOLD >= best) {
00454 if (score > best) best = score;
00455
00456
00457
00458
00459
00460
00461
00462 if (abs((long)term.size() - (long)word.size()) > edist_best * 4) {
00463 DEBUGLINE(SPELLING, "Lengths much too different");
00464 continue;
00465 }
00466
00467
00468
00469 utf32_term.assign(Utf8Iterator(term), Utf8Iterator());
00470
00471 if (abs(long(utf32_term.size()) - long(utf32_word.size()))
00472 > edist_best) {
00473 DEBUGLINE(SPELLING, "Lengths too different");
00474 continue;
00475 }
00476
00477 if (freq_edit_lower_bound(utf32_term, utf32_word) > edist_best) {
00478 DEBUGLINE(SPELLING, "Rejected by character frequency test");
00479 continue;
00480 }
00481
00482 int edist = edit_distance_unsigned(&utf32_term[0],
00483 int(utf32_term.size()),
00484 &utf32_word[0],
00485 int(utf32_word.size()),
00486 edist_best);
00487 DEBUGLINE(SPELLING, "Edit distance " << edist);
00488
00489
00490 if (edist == 0) RETURN(string());
00491
00492 if (edist <= edist_best) {
00493 Xapian::doccount freq = 0;
00494 for (size_t j = 0; j < internal.size(); ++j)
00495 freq += internal[j]->get_spelling_frequency(term);
00496
00497 DEBUGLINE(SPELLING, "Freq " << freq << " best " << freq_best);
00498 if (edist < edist_best || freq > freq_best) {
00499 DEBUGLINE(SPELLING, "Best so far: \"" << term <<
00500 "\" edist " << edist << " freq " <<
00501 freq);
00502 result = term;
00503 edist_best = edist;
00504 freq_best = freq;
00505 }
00506 }
00507 }
00508 }
00509 RETURN(result);
00510 }
00511
00512 TermIterator
00513 Database::spellings_begin() const
00514 {
00515 DEBUGAPICALL(TermIterator, "Database::spellings_begin", "");
00516 AutoPtr<TermList> merger;
00517 for (size_t i = 0; i < internal.size(); ++i) {
00518 TermList * tl = internal[i]->open_spelling_wordlist();
00519 if (tl) {
00520 if (merger.get()) {
00521 merger = new FreqAdderOrTermList(merger.release(), tl);
00522 } else {
00523 merger = tl;
00524 }
00525 }
00526 }
00527 RETURN(TermIterator(merger.release()));
00528 }
00529
00530 TermIterator
00531 Database::synonyms_begin(const std::string &term) const
00532 {
00533 DEBUGAPICALL(TermIterator, "Database::synonyms_begin", term);
00534 AutoPtr<TermList> merger;
00535 for (size_t i = 0; i < internal.size(); ++i) {
00536 TermList * tl = internal[i]->open_synonym_termlist(term);
00537 if (tl) {
00538 if (merger.get()) {
00539 merger = new OrTermList(merger.release(), tl);
00540 } else {
00541 merger = tl;
00542 }
00543 }
00544 }
00545 RETURN(TermIterator(merger.release()));
00546 }
00547
00548 TermIterator
00549 Database::synonym_keys_begin(const std::string &prefix) const
00550 {
00551 DEBUGAPICALL(TermIterator, "Database::synonyms_keys_begin", prefix);
00552 AutoPtr<TermList> merger;
00553 for (size_t i = 0; i < internal.size(); ++i) {
00554 TermList * tl = internal[i]->open_synonym_keylist(prefix);
00555 if (tl) {
00556 if (merger.get()) {
00557 merger = new OrTermList(merger.release(), tl);
00558 } else {
00559 merger = tl;
00560 }
00561 }
00562 }
00563 RETURN(TermIterator(merger.release()));
00564 }
00565
00566 string
00567 Database::get_metadata(const string & key) const
00568 {
00569 DEBUGAPICALL(string, "Database::get_metadata", key);
00570 if (key.empty())
00571 throw InvalidArgumentError("Empty metadata keys are invalid");
00572 if (internal.empty()) RETURN(std::string());
00573 RETURN(internal[0]->get_metadata(key));
00574 }
00575
00576 Xapian::TermIterator
00577 Database::metadata_keys_begin(const std::string &prefix) const
00578 {
00579 DEBUGAPICALL(Xapian::TermIterator, "Database::metadata_keys_begin", "");
00580 if (internal.empty()) RETURN(TermIterator(NULL));
00581 RETURN(TermIterator(internal[0]->open_metadata_keylist(prefix)));
00582 }
00583
00585
00586 WritableDatabase::WritableDatabase() : Database()
00587 {
00588 DEBUGAPICALL(void, "WritableDatabase::WritableDatabase", "");
00589 }
00590
00591 WritableDatabase::WritableDatabase(Database::Internal *internal_)
00592 : Database(internal_)
00593 {
00594 DEBUGAPICALL(void, "WritableDatabase::WritableDatabase",
00595 "Database::Internal");
00596 }
00597
00598 WritableDatabase::WritableDatabase(const WritableDatabase &other)
00599 : Database(other)
00600 {
00601 DEBUGAPICALL(void, "WritableDatabase::WritableDatabase", "WritableDatabase");
00602 }
00603
00604 void
00605 WritableDatabase::operator=(const WritableDatabase &other)
00606 {
00607 DEBUGAPICALL(void, "WritableDatabase::operator=", "WritableDatabase");
00608 Database::operator=(other);
00609 }
00610
00611 WritableDatabase::~WritableDatabase()
00612 {
00613 DEBUGAPICALL(void, "WritableDatabase::~WritableDatabase", "");
00614 }
00615
00616 XAPIAN_NORETURN(static void only_one_subdatabase_allowed());
00617 static void only_one_subdatabase_allowed()
00618 {
00619 throw Xapian::InvalidOperationError("WritableDatabase needs exactly one subdatabase");
00620 }
00621
00622 void
00623 WritableDatabase::flush()
00624 {
00625 DEBUGAPICALL(void, "WritableDatabase::flush", "");
00626 if (internal.size() != 1) only_one_subdatabase_allowed();
00627 internal[0]->flush();
00628 }
00629
00630 void
00631 WritableDatabase::begin_transaction(bool flushed)
00632 {
00633 DEBUGAPICALL(void, "WritableDatabase::begin_transaction", "");
00634 if (internal.size() != 1) only_one_subdatabase_allowed();
00635 internal[0]->begin_transaction(flushed);
00636 }
00637
00638 void
00639 WritableDatabase::commit_transaction()
00640 {
00641 DEBUGAPICALL(void, "WritableDatabase::commit_transaction", "");
00642 if (internal.size() != 1) only_one_subdatabase_allowed();
00643 internal[0]->commit_transaction();
00644 }
00645
00646 void
00647 WritableDatabase::cancel_transaction()
00648 {
00649 DEBUGAPICALL(void, "WritableDatabase::cancel_transaction", "");
00650 if (internal.size() != 1) only_one_subdatabase_allowed();
00651 internal[0]->cancel_transaction();
00652 }
00653
00654
00655 Xapian::docid
00656 WritableDatabase::add_document(const Document & document)
00657 {
00658 DEBUGAPICALL(Xapian::docid, "WritableDatabase::add_document", document);
00659 if (internal.size() != 1) only_one_subdatabase_allowed();
00660 RETURN(internal[0]->add_document(document));
00661 }
00662
00663 void
00664 WritableDatabase::delete_document(Xapian::docid did)
00665 {
00666 DEBUGAPICALL(void, "WritableDatabase::delete_document", did);
00667 if (internal.size() != 1) only_one_subdatabase_allowed();
00668 if (did == 0) throw InvalidArgumentError("Document ID 0 is invalid");
00669 internal[0]->delete_document(did);
00670 }
00671
00672 void
00673 WritableDatabase::delete_document(const std::string & unique_term)
00674 {
00675 DEBUGAPICALL(void, "WritableDatabase::delete_document", unique_term);
00676 if (internal.size() != 1) only_one_subdatabase_allowed();
00677 if (unique_term.empty())
00678 throw InvalidArgumentError("Empty termnames are invalid");
00679 internal[0]->delete_document(unique_term);
00680 }
00681
00682 void
00683 WritableDatabase::replace_document(Xapian::docid did, const Document & document)
00684 {
00685 DEBUGAPICALL(void, "WritableDatabase::replace_document",
00686 did << ", " << document);
00687 if (internal.size() != 1) only_one_subdatabase_allowed();
00688 if (did == 0) throw Xapian::InvalidArgumentError("Document ID 0 is invalid");
00689 internal[0]->replace_document(did, document);
00690 }
00691
00692 Xapian::docid
00693 WritableDatabase::replace_document(const std::string & unique_term,
00694 const Document & document)
00695 {
00696 DEBUGAPICALL(Xapian::docid, "WritableDatabase::replace_document",
00697 unique_term << ", " << document);
00698 if (internal.size() != 1) only_one_subdatabase_allowed();
00699 if (unique_term.empty())
00700 throw InvalidArgumentError("Empty termnames are invalid");
00701 RETURN(internal[0]->replace_document(unique_term, document));
00702 }
00703
00704 void
00705 WritableDatabase::add_spelling(const std::string & word,
00706 Xapian::termcount freqinc) const
00707 {
00708 DEBUGAPICALL(void, "WritableDatabase::add_spelling",
00709 word << ", " << freqinc);
00710 if (internal.size() != 1) only_one_subdatabase_allowed();
00711 internal[0]->add_spelling(word, freqinc);
00712 }
00713
00714 void
00715 WritableDatabase::remove_spelling(const std::string & word,
00716 Xapian::termcount freqdec) const
00717 {
00718 DEBUGAPICALL(void, "WritableDatabase::remove_spelling",
00719 word << ", " << freqdec);
00720 if (internal.size() != 1) only_one_subdatabase_allowed();
00721 internal[0]->remove_spelling(word, freqdec);
00722 }
00723
00724 void
00725 WritableDatabase::add_synonym(const std::string & term,
00726 const std::string & synonym) const
00727 {
00728 DEBUGAPICALL(void, "WritableDatabase::add_synonym",
00729 term << ", " << synonym);
00730 if (internal.size() != 1) only_one_subdatabase_allowed();
00731 internal[0]->add_synonym(term, synonym);
00732 }
00733
00734 void
00735 WritableDatabase::remove_synonym(const std::string & term,
00736 const std::string & synonym) const
00737 {
00738 DEBUGAPICALL(void, "WritableDatabase::remove_synonym",
00739 term << ", " << synonym);
00740 if (internal.size() != 1) only_one_subdatabase_allowed();
00741 internal[0]->remove_synonym(term, synonym);
00742 }
00743
00744 void
00745 WritableDatabase::clear_synonyms(const std::string & term) const
00746 {
00747 DEBUGAPICALL(void, "WritableDatabase::clear_synonyms", term);
00748 if (internal.size() != 1) only_one_subdatabase_allowed();
00749 internal[0]->clear_synonyms(term);
00750 }
00751
00752 void
00753 WritableDatabase::set_metadata(const string & key, const string & value)
00754 {
00755 DEBUGAPICALL(void, "WritableDatabase::set_metadata", key << ", " << value);
00756 if (internal.size() != 1) only_one_subdatabase_allowed();
00757 if (key.empty())
00758 throw InvalidArgumentError("Empty metadata keys are invalid");
00759 internal[0]->set_metadata(key, value);
00760 }
00761
00762 string
00763 WritableDatabase::get_description() const
00764 {
00766 return "WritableDatabase()";
00767 }
00768
00769 }