|
MythTV
0.26-pre
|
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 }
1.7.6.1