00001 #include <algorithm>
00002 #include <vector>
00003 #include <map>
00004
00005 #include "mythdb.h"
00006 #include "cleanup.h"
00007 #include "dbaccess.h"
00008
00009 namespace
00010 {
00011 template <typename T, typename arg_type>
00012 struct call_sort
00013 {
00014 call_sort(T &c) : m_c(c) {}
00015
00016 bool operator()(const arg_type &lhs, const arg_type &rhs)
00017 {
00018 return m_c.sort(lhs, rhs);
00019 }
00020
00021 T &m_c;
00022 };
00023 }
00024
00025 class SingleValueImp
00026 {
00027 public:
00028 typedef SingleValue::entry entry;
00029 typedef std::vector<entry> entry_list;
00030
00031 private:
00032 typedef std::map<int, QString> entry_map;
00033
00034 public:
00035 SingleValueImp(const QString &table_name, const QString &id_name,
00036 const QString &value_name) : m_table_name(table_name),
00037 m_id_name(id_name), m_value_name(value_name), m_ready(false),
00038 m_dirty(true), m_clean_stub(this)
00039 {
00040 m_insert_sql = QString("INSERT INTO %1 (%2) VALUES (:NAME)")
00041 .arg(m_table_name).arg(m_value_name);
00042 m_fill_sql = QString("SELECT %1, %2 FROM %3").arg(m_id_name)
00043 .arg(m_value_name).arg(m_table_name);
00044 m_delete_sql = QString("DELETE FROM %1 WHERE %2 = :ID")
00045 .arg(m_table_name).arg(m_id_name);
00046 }
00047
00048 virtual ~SingleValueImp() {}
00049
00050 void load_data()
00051 {
00052 if (!m_ready)
00053 {
00054 fill_from_db();
00055 m_ready = true;
00056 }
00057 }
00058
00059 int add(const QString &name)
00060 {
00061 int id = 0;
00062
00063 if (!exists(name, &id))
00064 {
00065 MSqlQuery query(MSqlQuery::InitCon());
00066 query.prepare(m_insert_sql);
00067 query.bindValue(":NAME", name);
00068 if (query.exec())
00069 {
00070 if (query.exec("SELECT LAST_INSERT_ID()") && query.next())
00071 {
00072 id = query.value(0).toInt();
00073 m_entries.insert(entry_map::value_type(id, name));
00074 m_dirty = true;
00075 }
00076 else
00077 MythDB::DBError("get last id", query);
00078 }
00079 }
00080
00081 return id;
00082 }
00083
00084 bool get(int id, QString &value)
00085 {
00086 entry_map::const_iterator p = m_entries.find(id);
00087 if (p != m_entries.end())
00088 {
00089 value = p->second;
00090 return true;
00091 }
00092 return false;
00093 }
00094
00095 void remove(int id)
00096 {
00097 entry_map::iterator p = m_entries.find(id);
00098 if (p != m_entries.end())
00099 {
00100 MSqlQuery query(MSqlQuery::InitCon());
00101 query.prepare(m_delete_sql);
00102 query.bindValue(":ID", p->first);
00103 if (query.exec())
00104 {
00105 m_dirty = true;
00106 m_entries.erase(p);
00107 }
00108 }
00109 }
00110
00111 bool exists(int id)
00112 {
00113 return m_entries.find(id) != m_entries.end();
00114 }
00115
00116 bool exists(const QString &name, int *id = 0)
00117 {
00118 entry_map::const_iterator p = find(name);
00119 if (p != m_entries.end())
00120 {
00121 if (id)
00122 *id = p->first;
00123 return true;
00124 }
00125 return false;
00126 }
00127
00128 const entry_list &getList()
00129 {
00130 if (m_dirty)
00131 {
00132 m_dirty = false;
00133 m_ret_entries.clear();
00134
00135 for (entry_map::const_iterator p = m_entries.begin();
00136 p != m_entries.end(); ++p)
00137 {
00138 m_ret_entries.push_back(entry_list::value_type(p->first,
00139 p->second));
00140 }
00141 std::sort(m_ret_entries.begin(), m_ret_entries.end(),
00142 call_sort<SingleValueImp, entry>(*this));
00143 }
00144
00145 return m_ret_entries;
00146 }
00147
00148 virtual bool sort(const entry &lhs, const entry &rhs)
00149 {
00150 return QString::localeAwareCompare(lhs.second, rhs.second) < 0;
00151 }
00152
00153 void cleanup()
00154 {
00155 m_ready = false;
00156 m_dirty = true;
00157 m_ret_entries.clear();
00158 m_entries.clear();
00159 }
00160
00161 private:
00162 entry_map::iterator find(const QString &name)
00163 {
00164 for (entry_map::iterator p = m_entries.begin();
00165 p != m_entries.end(); ++p)
00166 {
00167 if (p->second == name)
00168 return p;
00169 }
00170 return m_entries.end();
00171 }
00172
00173 void fill_from_db()
00174 {
00175 m_entries.clear();
00176
00177 MSqlQuery query(MSqlQuery::InitCon());
00178
00179 if (query.exec(m_fill_sql))
00180 {
00181 while (query.next())
00182 {
00183 int id = query.value(0).toInt();
00184 QString val = query.value(1).toString();
00185 m_entries.insert(entry_map::value_type(id, val));
00186 }
00187 }
00188 }
00189
00190 private:
00191 QString m_table_name;
00192 QString m_id_name;
00193 QString m_value_name;
00194
00195 QString m_insert_sql;
00196 QString m_fill_sql;
00197 QString m_delete_sql;
00198
00199 bool m_ready;
00200 bool m_dirty;
00201 entry_list m_ret_entries;
00202 entry_map m_entries;
00203 SimpleCleanup<SingleValueImp> m_clean_stub;
00204 };
00205
00207
00208 SingleValue::SingleValue(SingleValueImp *imp) : m_imp(imp)
00209 {
00210 }
00211
00212 SingleValue::~SingleValue()
00213 {
00214 delete m_imp;
00215 }
00216
00217 int SingleValue::add(const QString &name)
00218 {
00219 return m_imp->add(name);
00220 }
00221
00222 bool SingleValue::get(int id, QString &category)
00223 {
00224 return m_imp->get(id, category);
00225 }
00226
00227 void SingleValue::remove(int id)
00228 {
00229 m_imp->remove(id);
00230 }
00231
00232 bool SingleValue::exists(int id)
00233 {
00234 return m_imp->exists(id);
00235 }
00236
00237 bool SingleValue::exists(const QString &name)
00238 {
00239 return m_imp->exists(name);
00240 }
00241
00242 const SingleValue::entry_list &SingleValue::getList()
00243 {
00244 return m_imp->getList();
00245 }
00246
00247 void SingleValue::load_data()
00248 {
00249 m_imp->load_data();
00250 }
00251
00253
00254 class MultiValueImp
00255 {
00256 public:
00257 typedef MultiValue::entry entry;
00258
00259 private:
00260 typedef std::map<int, entry> id_map;
00261
00262 public:
00263 MultiValueImp(const QString &table_name, const QString &id_name,
00264 const QString &value_name) : m_table_name(table_name),
00265 m_id_name(id_name), m_value_name(value_name), m_ready(false),
00266 m_clean_stub(this)
00267 {
00268 m_insert_sql = QString("INSERT INTO %1 (%2, %3) VALUES (:ID, :VALUE)")
00269 .arg(m_table_name).arg(m_id_name).arg(m_value_name);
00270 m_fill_sql = QString("SELECT %1, %2 FROM %3 ORDER BY %4").arg(m_id_name)
00271 .arg(m_value_name).arg(m_table_name).arg(m_id_name);
00272 }
00273
00274 void load_data()
00275 {
00276 if (!m_ready)
00277 {
00278 fill_from_db();
00279 m_ready = true;
00280 }
00281 }
00282
00283 void cleanup()
00284 {
00285 m_ready = false;
00286 m_val_map.clear();
00287 }
00288
00289 int add(int id, int value)
00290 {
00291 bool db_insert = false;
00292 id_map::iterator p = m_val_map.find(id);
00293 if (p != m_val_map.end())
00294 {
00295 entry::values_type &va = p->second.values;
00296 entry::values_type::iterator v =
00297 std::find(va.begin(), va.end(), value);
00298 if (v == va.end())
00299 {
00300 va.push_back(value);
00301 db_insert = true;
00302 }
00303 }
00304 else
00305 {
00306 entry e;
00307 e.id = id;
00308 e.values.push_back(value);
00309 m_val_map.insert(id_map::value_type(id, e));
00310 db_insert = true;
00311 }
00312
00313 if (db_insert)
00314 {
00315 MSqlQuery query(MSqlQuery::InitCon());
00316 query.prepare(m_insert_sql);
00317 query.bindValue(":ID", id);
00318 query.bindValue(":VALUE", value);
00319 if (!query.exec())
00320 MythDB::DBError("multi value insert", query);
00321 }
00322
00323 return id;
00324 }
00325
00326 bool get(int id, entry &values)
00327 {
00328 id_map::iterator p = m_val_map.find(id);
00329 if (p != m_val_map.end())
00330 {
00331 values = p->second;
00332 return true;
00333 }
00334 return false;
00335 }
00336
00337 void remove(int id, int value)
00338 {
00339 id_map::iterator p = m_val_map.find(id);
00340 if (p != m_val_map.end())
00341 {
00342 entry::values_type::iterator vp =
00343 std::find(p->second.values.begin(), p->second.values.end(),
00344 value);
00345 if (vp != p->second.values.end())
00346 {
00347 MSqlQuery query(MSqlQuery::InitCon());
00348 QString del_query = QString("DELETE FROM %1 WHERE %2 = :ID AND "
00349 "%3 = :VALUE")
00350 .arg(m_table_name).arg(m_id_name).arg(m_value_name);
00351 query.prepare(del_query);
00352 query.bindValue(":ID", p->first);
00353 query.bindValue(":VALUE", int(*vp));
00354 if (!query.exec() || !query.isActive())
00355 {
00356 MythDB::DBError("multivalue remove", query);
00357 }
00358 p->second.values.erase(vp);
00359 }
00360 }
00361 }
00362
00363 void remove(int id)
00364 {
00365 id_map::iterator p = m_val_map.find(id);
00366 if (p != m_val_map.end())
00367 {
00368 MSqlQuery query(MSqlQuery::InitCon());
00369 QString del_query = QString("DELETE FROM %1 WHERE %2 = :ID")
00370 .arg(m_table_name).arg(m_id_name);
00371 query.prepare(del_query);
00372 query.bindValue(":ID", p->first);
00373 if (!query.exec() || !query.isActive())
00374 {
00375 MythDB::DBError("multivalue remove", query);
00376 }
00377 m_val_map.erase(p);
00378 }
00379 }
00380
00381 bool exists(int id, int value)
00382 {
00383 id_map::iterator p = m_val_map.find(id);
00384 if (p != m_val_map.end())
00385 {
00386 entry::values_type::iterator vp =
00387 std::find(p->second.values.begin(), p->second.values.end(),
00388 value);
00389 return vp != p->second.values.end();
00390 }
00391 return false;
00392 }
00393
00394 bool exists(int id)
00395 {
00396 return m_val_map.find(id) != m_val_map.end();
00397 }
00398
00399 private:
00400 void fill_from_db()
00401 {
00402 m_val_map.clear();
00403
00404 MSqlQuery query(MSqlQuery::InitCon());
00405
00406 if (query.exec(m_fill_sql) && query.size() > 0)
00407 {
00408 id_map::iterator p = m_val_map.end();
00409 while (query.next())
00410 {
00411 int id = query.value(0).toInt();
00412 int val = query.value(1).toInt();
00413
00414 if (p == m_val_map.end() ||
00415 (p != m_val_map.end() && p->first != id))
00416 {
00417 p = m_val_map.find(id);
00418 if (p == m_val_map.end())
00419 {
00420 entry e;
00421 e.id = id;
00422 p = m_val_map.insert(id_map::value_type(id, e)).first;
00423 }
00424 }
00425 p->second.values.push_back(val);
00426 }
00427 }
00428 }
00429
00430 private:
00431 id_map m_val_map;
00432
00433 QString m_table_name;
00434 QString m_id_name;
00435 QString m_value_name;
00436
00437 QString m_insert_sql;
00438 QString m_fill_sql;
00439 QString m_id_sql;
00440
00441 bool m_ready;
00442 SimpleCleanup<MultiValueImp> m_clean_stub;
00443 };
00444
00446
00447 MultiValue::MultiValue(MultiValueImp *imp) : m_imp(imp)
00448 {
00449 }
00450
00451 MultiValue::~MultiValue()
00452 {
00453 }
00454
00455 int MultiValue::add(int id, int value)
00456 {
00457 return m_imp->add(id, value);
00458 }
00459
00460 bool MultiValue::get(int id, entry &values)
00461 {
00462 return m_imp->get(id, values);
00463 }
00464
00465 void MultiValue::remove(int id, int value)
00466 {
00467 m_imp->remove(id, value);
00468 }
00469
00470 void MultiValue::remove(int id)
00471 {
00472 m_imp->remove(id);
00473 }
00474
00475 bool MultiValue::exists(int id, int value)
00476 {
00477 return m_imp->exists(id, value);
00478 }
00479
00480 bool MultiValue::exists(int id)
00481 {
00482 return m_imp->exists(id);
00483 }
00484
00485 void MultiValue::load_data()
00486 {
00487 m_imp->load_data();
00488 }
00489
00491
00492 VideoCategory::VideoCategory() :
00493 SingleValue(new SingleValueImp("videocategory", "intid", "category"))
00494 {
00495 }
00496
00497 VideoCategory::~VideoCategory()
00498 {
00499 }
00500
00501 VideoCategory &VideoCategory::GetCategory()
00502 {
00503 static VideoCategory vc;
00504 vc.load_data();
00505 return vc;
00506 }
00507
00509
00510 VideoCountry::VideoCountry() :
00511 SingleValue(new SingleValueImp("videocountry", "intid", "country"))
00512 {
00513 }
00514
00515 VideoCountry::~VideoCountry()
00516 {
00517 }
00518
00519 VideoCountry &VideoCountry::getCountry()
00520 {
00521 static VideoCountry vc;
00522 vc.load_data();
00523 return vc;
00524 }
00525
00527
00528 VideoGenre::VideoGenre() :
00529 SingleValue(new SingleValueImp("videogenre", "intid", "genre"))
00530 {
00531 }
00532
00533 VideoGenre::~VideoGenre()
00534 {
00535 }
00536
00537 VideoGenre &VideoGenre::getGenre()
00538 {
00539 static VideoGenre vg;
00540 vg.load_data();
00541 return vg;
00542 }
00543
00545
00546 VideoCast::VideoCast() :
00547 SingleValue(new SingleValueImp("videocast", "intid", "cast"))
00548 {
00549 }
00550
00551 VideoCast::~VideoCast()
00552 {
00553 }
00554
00555 VideoCast &VideoCast::GetCast()
00556 {
00557 static VideoCast vc;
00558 vc.load_data();
00559 return vc;
00560 }
00561
00563
00564 VideoGenreMap::VideoGenreMap() :
00565 MultiValue(new MultiValueImp("videometadatagenre", "idvideo", "idgenre"))
00566 {
00567 }
00568
00569 VideoGenreMap::~VideoGenreMap()
00570 {
00571 }
00572
00573 VideoGenreMap &VideoGenreMap::getGenreMap()
00574 {
00575 static VideoGenreMap vgm;
00576 vgm.load_data();
00577 return vgm;
00578 }
00579
00581
00582 VideoCountryMap::VideoCountryMap() :
00583 MultiValue(new MultiValueImp("videometadatacountry", "idvideo",
00584 "idcountry"))
00585 {
00586 }
00587
00588 VideoCountryMap::~VideoCountryMap()
00589 {
00590 }
00591
00592 VideoCountryMap &VideoCountryMap::getCountryMap()
00593 {
00594 static VideoCountryMap vcm;
00595 vcm.load_data();
00596 return vcm;
00597 }
00598
00600
00601 VideoCastMap::VideoCastMap() :
00602 MultiValue(new MultiValueImp("videometadatacast", "idvideo",
00603 "idcast"))
00604 {
00605 }
00606
00607 VideoCastMap::~VideoCastMap()
00608 {
00609 }
00610
00611 VideoCastMap &VideoCastMap::getCastMap()
00612 {
00613 static VideoCastMap vcm;
00614 vcm.load_data();
00615 return vcm;
00616 }
00617
00619
00620 class FileAssociationsImp
00621 {
00622 public:
00623 typedef FileAssociations::file_association file_association;
00624 typedef FileAssociations::association_list association_list;
00625 typedef FileAssociations::ext_ignore_list ext_ignore_list;
00626
00627 public:
00628 FileAssociationsImp() : m_ready(false) {}
00629
00630 bool add(file_association &fa)
00631 {
00632 file_association ret_fa(fa);
00633
00634 file_association *existing_fa = 0;
00635 MSqlQuery query(MSqlQuery::InitCon());
00636
00637 association_list::iterator p = find(ret_fa.extension);
00638 if (p != m_file_associations.end())
00639 {
00640 ret_fa.id = p->id;
00641 existing_fa = &(*p);
00642
00643 query.prepare("UPDATE videotypes SET extension = :EXT, "
00644 "playcommand = :PLAYCMD, f_ignore = :IGNORED, "
00645 "use_default = :USEDEFAULT WHERE intid = :ID");
00646 query.bindValue(":ID", ret_fa.id);
00647 }
00648 else
00649 query.prepare("INSERT INTO videotypes (extension, playcommand, "
00650 "f_ignore, use_default) VALUES "
00651 "(:EXT, :PLAYCMD, :IGNORED, :USEDEFAULT)");
00652
00653 query.bindValue(":EXT", ret_fa.extension);
00654 query.bindValue(":PLAYCMD", ret_fa.playcommand);
00655 query.bindValue(":IGNORED", ret_fa.ignore);
00656 query.bindValue(":USEDEFAULT", ret_fa.use_default);
00657
00658 if (query.exec() && query.isActive())
00659 {
00660 if (!existing_fa)
00661 {
00662 if (query.exec("SELECT LAST_INSERT_ID()") && query.next())
00663 {
00664 ret_fa.id = query.value(0).toUInt();
00665 m_file_associations.push_back(ret_fa);
00666 }
00667 else
00668 return false;
00669 }
00670 else
00671 *existing_fa = ret_fa;
00672
00673 fa = ret_fa;
00674 return true;
00675 }
00676
00677 return false;
00678 }
00679
00680 bool get(unsigned int id, file_association &val) const
00681 {
00682 association_list::const_iterator p = find(id);
00683 if (p != m_file_associations.end())
00684 {
00685 val = *p;
00686 return true;
00687 }
00688 return false;
00689 }
00690
00691 bool get(const QString &ext, file_association &val) const
00692 {
00693 association_list::const_iterator p = find(ext);
00694 if (p != m_file_associations.end())
00695 {
00696 val = *p;
00697 return true;
00698 }
00699 return false;
00700 }
00701
00702 bool remove(unsigned int id)
00703 {
00704 association_list::iterator p = find(id);
00705 if (p != m_file_associations.end())
00706 {
00707 MSqlQuery query(MSqlQuery::InitCon());
00708 query.prepare("DELETE FROM videotypes WHERE intid = :ID");
00709 query.bindValue(":ID", p->id);
00710 if (query.exec())
00711 {
00712 m_file_associations.erase(p);
00713 return true;
00714 }
00715 }
00716 return false;
00717 }
00718
00719 const association_list &getList() const
00720 {
00721 return m_file_associations;
00722 }
00723
00724 void getExtensionIgnoreList(ext_ignore_list &ext_ignore) const
00725 {
00726 for (association_list::const_iterator p = m_file_associations.begin();
00727 p != m_file_associations.end(); ++p)
00728 {
00729 ext_ignore.push_back(std::make_pair(p->extension, p->ignore));
00730 }
00731 }
00732
00733 void load_data()
00734 {
00735 if (!m_ready)
00736 {
00737 fill_from_db();
00738 m_ready = true;
00739 }
00740 }
00741
00742 void cleanup()
00743 {
00744 m_ready = false;
00745 m_file_associations.clear();
00746 }
00747
00748 private:
00749 void fill_from_db()
00750 {
00751 MSqlQuery query(MSqlQuery::InitCon());
00752 if (query.exec("SELECT intid, extension, playcommand, f_ignore, "
00753 "use_default FROM videotypes"))
00754 {
00755 while (query.next())
00756 {
00757 file_association fa(query.value(0).toUInt(),
00758 query.value(1).toString(),
00759 query.value(2).toString(),
00760 query.value(3).toBool(),
00761 query.value(4).toBool());
00762 m_file_associations.push_back(fa);
00763 }
00764 }
00765 }
00766
00767 association_list::iterator find(const QString &ext)
00768 {
00769 for (association_list::iterator p = m_file_associations.begin();
00770 p != m_file_associations.end(); ++p)
00771 {
00772 if (p->extension.length() == ext.length() &&
00773 ext.indexOf(p->extension) == 0)
00774 {
00775 return p;
00776 }
00777 }
00778 return m_file_associations.end();
00779 }
00780
00781 association_list::iterator find(unsigned int id)
00782 {
00783 for (association_list::iterator p = m_file_associations.begin();
00784 p != m_file_associations.end(); ++p)
00785 {
00786 if (p->id == id) return p;
00787 }
00788 return m_file_associations.end();
00789 }
00790
00791 association_list::const_iterator find(const QString &ext) const
00792 {
00793 for (association_list::const_iterator p = m_file_associations.begin();
00794 p != m_file_associations.end(); ++p)
00795 {
00796 if (p->extension.length() == ext.length() &&
00797 ext.indexOf(p->extension) == 0)
00798 {
00799 return p;
00800 }
00801 }
00802 return m_file_associations.end();
00803 }
00804
00805 association_list::const_iterator find(unsigned int id) const
00806 {
00807 for (association_list::const_iterator p = m_file_associations.begin();
00808 p != m_file_associations.end(); ++p)
00809 {
00810 if (p->id == id) return p;
00811 }
00812 return m_file_associations.end();
00813 }
00814
00815 private:
00816 association_list m_file_associations;
00817 bool m_ready;
00818 };
00819
00820
00821 FileAssociations::file_association::file_association() : id(0), ignore(false),
00822 use_default(false)
00823 {
00824 }
00825
00826 FileAssociations::file_association::file_association(unsigned int l_id,
00827 const QString &ext,
00828 const QString &playcmd,
00829 bool l_ignore,
00830 bool l_use_default) :
00831 id(l_id), extension(ext), playcommand(playcmd), ignore(l_ignore),
00832 use_default(l_use_default)
00833 {
00834 }
00835
00836 bool FileAssociations::add(file_association &fa)
00837 {
00838 return m_imp->add(fa);
00839 }
00840
00841 bool FileAssociations::get(unsigned int id, file_association &val) const
00842 {
00843 return m_imp->get(id, val);
00844 }
00845
00846 bool FileAssociations::get(const QString &ext, file_association &val) const
00847 {
00848 return m_imp->get(ext, val);
00849 }
00850
00851 bool FileAssociations::remove(unsigned int id)
00852 {
00853 return m_imp->remove(id);
00854 }
00855
00856 const FileAssociations::association_list &FileAssociations::getList() const
00857 {
00858 return m_imp->getList();
00859 }
00860
00861 void FileAssociations::getExtensionIgnoreList(ext_ignore_list &ext_ignore) const
00862 {
00863 return m_imp->getExtensionIgnoreList(ext_ignore);
00864 }
00865
00866 void FileAssociations::load_data()
00867 {
00868 m_imp->load_data();
00869 }
00870
00871 FileAssociations::FileAssociations()
00872 {
00873 m_imp = new FileAssociationsImp;
00874 }
00875
00876 FileAssociations::~FileAssociations()
00877 {
00878 delete m_imp;
00879 }
00880
00881 FileAssociations &FileAssociations::getFileAssociation()
00882 {
00883 static FileAssociations fa;
00884 fa.load_data();
00885 return fa;
00886 }