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