diff options
-rw-r--r-- | XBMC.xcodeproj/project.pbxproj | 10 | ||||
-rw-r--r-- | project/VS2010Express/XBMC.vcxproj | 2 | ||||
-rw-r--r-- | project/VS2010Express/XBMC.vcxproj.filters | 8 | ||||
-rw-r--r-- | xbmc/dbwrappers/DatabaseQuery.cpp | 529 | ||||
-rw-r--r-- | xbmc/dbwrappers/DatabaseQuery.h | 144 | ||||
-rw-r--r-- | xbmc/dbwrappers/Makefile | 1 | ||||
-rw-r--r-- | xbmc/playlists/SmartPlayList.cpp | 491 | ||||
-rw-r--r-- | xbmc/playlists/SmartPlayList.h | 113 |
8 files changed, 696 insertions, 602 deletions
diff --git a/XBMC.xcodeproj/project.pbxproj b/XBMC.xcodeproj/project.pbxproj index c5c4be598d..bc3261d802 100644 --- a/XBMC.xcodeproj/project.pbxproj +++ b/XBMC.xcodeproj/project.pbxproj @@ -254,6 +254,9 @@ 7C1A85661520522500C63311 /* TextureCacheJob.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C1A85631520522500C63311 /* TextureCacheJob.cpp */; }; 7C1D682915A7D2FD00658B65 /* DatabaseManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C1D682715A7D2FD00658B65 /* DatabaseManager.cpp */; }; 7C1F6EBB13ECCFA7001726AB /* LibraryDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C1F6EB913ECCFA7001726AB /* LibraryDirectory.cpp */; }; + 7C2612711825B6340086E04D /* DatabaseQuery.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C26126F1825B6340086E04D /* DatabaseQuery.cpp */; }; + 7C2612721825B6340086E04D /* DatabaseQuery.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C26126F1825B6340086E04D /* DatabaseQuery.cpp */; }; + 7C2612731825B6340086E04D /* DatabaseQuery.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C26126F1825B6340086E04D /* DatabaseQuery.cpp */; }; 7C2D6AE40F35453E00DD2E85 /* SpecialProtocol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C2D6AE20F35453E00DD2E85 /* SpecialProtocol.cpp */; }; 7C4458BD161E203800A905F6 /* Screenshot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C4458BB161E203800A905F6 /* Screenshot.cpp */; }; 7C45DBE910F325C400D4BBF3 /* DAVDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C45DBE710F325C400D4BBF3 /* DAVDirectory.cpp */; }; @@ -3685,6 +3688,8 @@ 7C1D682815A7D2FD00658B65 /* DatabaseManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DatabaseManager.h; sourceTree = "<group>"; }; 7C1F6EB913ECCFA7001726AB /* LibraryDirectory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LibraryDirectory.cpp; sourceTree = "<group>"; }; 7C1F6EBA13ECCFA7001726AB /* LibraryDirectory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LibraryDirectory.h; sourceTree = "<group>"; }; + 7C26126F1825B6340086E04D /* DatabaseQuery.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DatabaseQuery.cpp; sourceTree = "<group>"; }; + 7C2612701825B6340086E04D /* DatabaseQuery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DatabaseQuery.h; sourceTree = "<group>"; }; 7C2D6AE20F35453E00DD2E85 /* SpecialProtocol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SpecialProtocol.cpp; sourceTree = "<group>"; }; 7C2D6AE30F35453E00DD2E85 /* SpecialProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpecialProtocol.h; sourceTree = "<group>"; }; 7C4458BB161E203800A905F6 /* Screenshot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Screenshot.cpp; sourceTree = "<group>"; }; @@ -6359,6 +6364,8 @@ children = ( E38E16800D25F9FA00618676 /* Database.cpp */, E38E16810D25F9FA00618676 /* Database.h */, + 7C26126F1825B6340086E04D /* DatabaseQuery.cpp */, + 7C2612701825B6340086E04D /* DatabaseQuery.h */, E38E1CD70D25F9FC00618676 /* dataset.cpp */, E38E1CD80D25F9FC00618676 /* dataset.h */, 7C7B2B2E1134F36400713D6D /* mysqldataset.cpp */, @@ -10622,6 +10629,7 @@ DFD882F817DD1A5B001516FE /* AddonPythonInvoker.cpp in Sources */, DFD882E917DD189E001516FE /* StringValidation.cpp in Sources */, F500E35617F3412C004FC217 /* WinEvents.cpp in Sources */, + 7C2612711825B6340086E04D /* DatabaseQuery.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -11652,6 +11660,7 @@ DFD882F617DD1A5B001516FE /* AddonPythonInvoker.cpp in Sources */, DFD882E717DD189E001516FE /* StringValidation.cpp in Sources */, F500E35817F3412C004FC217 /* WinEvents.cpp in Sources */, + 7C2612731825B6340086E04D /* DatabaseQuery.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -12684,6 +12693,7 @@ DFD882F717DD1A5B001516FE /* AddonPythonInvoker.cpp in Sources */, DFD882E817DD189E001516FE /* StringValidation.cpp in Sources */, F500E35717F3412C004FC217 /* WinEvents.cpp in Sources */, + 7C2612721825B6340086E04D /* DatabaseQuery.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/project/VS2010Express/XBMC.vcxproj b/project/VS2010Express/XBMC.vcxproj index 1a13b8bd03..9ff5c9851f 100644 --- a/project/VS2010Express/XBMC.vcxproj +++ b/project/VS2010Express/XBMC.vcxproj @@ -419,6 +419,7 @@ <ClCompile Include="..\..\xbmc\CueDocument.cpp" /> <ClCompile Include="..\..\xbmc\DbUrl.cpp" /> <ClCompile Include="..\..\xbmc\dbwrappers\Database.cpp" /> + <ClCompile Include="..\..\xbmc\dbwrappers\DatabaseQuery.cpp" /> <ClCompile Include="..\..\xbmc\dbwrappers\dataset.cpp" /> <ClCompile Include="..\..\xbmc\dbwrappers\mysqldataset.cpp" /> <ClCompile Include="..\..\xbmc\dbwrappers\qry_dat.cpp" /> @@ -2051,6 +2052,7 @@ <ClInclude Include="..\..\xbmc\cores\VideoRenderers\VideoShaders\WinVideoFilter.h" /> <ClInclude Include="..\..\xbmc\CueDocument.h" /> <ClInclude Include="..\..\xbmc\dbwrappers\Database.h" /> + <ClInclude Include="..\..\xbmc\dbwrappers\DatabaseQuery.h" /> <ClInclude Include="..\..\xbmc\dbwrappers\dataset.h" /> <ClInclude Include="..\..\xbmc\dbwrappers\mysqldataset.h" /> <ClInclude Include="..\..\xbmc\dbwrappers\qry_dat.h" /> diff --git a/project/VS2010Express/XBMC.vcxproj.filters b/project/VS2010Express/XBMC.vcxproj.filters index 74e82c44ac..adbb54e53e 100644 --- a/project/VS2010Express/XBMC.vcxproj.filters +++ b/project/VS2010Express/XBMC.vcxproj.filters @@ -2450,6 +2450,9 @@ <ClCompile Include="..\..\xbmc\dbwrappers\Database.cpp"> <Filter>dbwrappers</Filter> </ClCompile> + <ClCompile Include="..\..\xbmc\dbwrappers\DatabaseQuery.cpp"> + <Filter>dbwrappers</Filter> + </ClCompile> <ClCompile Include="..\..\xbmc\dbwrappers\dataset.cpp"> <Filter>dbwrappers</Filter> </ClCompile> @@ -5520,6 +5523,9 @@ <ClInclude Include="..\..\xbmc\dbwrappers\Database.h"> <Filter>dbwrappers</Filter> </ClInclude> + <ClInclude Include="..\..\xbmc\dbwrappers\DatabaseQuery.h"> + <Filter>dbwrappers</Filter> + </ClInclude> <ClInclude Include="..\..\xbmc\dbwrappers\dataset.h"> <Filter>dbwrappers</Filter> </ClInclude> @@ -6051,4 +6057,4 @@ <Filter>interfaces\swig</Filter> </None> </ItemGroup> -</Project>
\ No newline at end of file +</Project> diff --git a/xbmc/dbwrappers/DatabaseQuery.cpp b/xbmc/dbwrappers/DatabaseQuery.cpp new file mode 100644 index 0000000000..011f06b5b5 --- /dev/null +++ b/xbmc/dbwrappers/DatabaseQuery.cpp @@ -0,0 +1,529 @@ +/* + * Copyright (C) 2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include "DatabaseQuery.h" +#include "Database.h" +#include "XBDateTime.h" +#include "guilib/LocalizeStrings.h" +#include "utils/CharsetConverter.h" +#include "utils/StringUtils.h" +#include "utils/Variant.h" +#include "utils/XBMCTinyXML.h" + +using namespace std; + +typedef struct +{ + char string[15]; + CDatabaseQueryRule::SEARCH_OPERATOR op; + int localizedString; +} operatorField; + +static const operatorField operators[] = { + { "contains", CDatabaseQueryRule::OPERATOR_CONTAINS, 21400 }, + { "doesnotcontain", CDatabaseQueryRule::OPERATOR_DOES_NOT_CONTAIN, 21401 }, + { "is", CDatabaseQueryRule::OPERATOR_EQUALS, 21402 }, + { "isnot", CDatabaseQueryRule::OPERATOR_DOES_NOT_EQUAL, 21403 }, + { "startswith", CDatabaseQueryRule::OPERATOR_STARTS_WITH, 21404 }, + { "endswith", CDatabaseQueryRule::OPERATOR_ENDS_WITH, 21405 }, + { "greaterthan", CDatabaseQueryRule::OPERATOR_GREATER_THAN, 21406 }, + { "lessthan", CDatabaseQueryRule::OPERATOR_LESS_THAN, 21407 }, + { "after", CDatabaseQueryRule::OPERATOR_AFTER, 21408 }, + { "before", CDatabaseQueryRule::OPERATOR_BEFORE, 21409 }, + { "inthelast", CDatabaseQueryRule::OPERATOR_IN_THE_LAST, 21410 }, + { "notinthelast", CDatabaseQueryRule::OPERATOR_NOT_IN_THE_LAST, 21411 }, + { "true", CDatabaseQueryRule::OPERATOR_TRUE, 20122 }, + { "false", CDatabaseQueryRule::OPERATOR_FALSE, 20424 }, + { "between", CDatabaseQueryRule::OPERATOR_BETWEEN, 21456 } +}; + +static const size_t NUM_OPERATORS = sizeof(operators) / sizeof(operatorField); + +#define RULE_VALUE_SEPARATOR " / " + +CDatabaseQueryRule::CDatabaseQueryRule() +{ + m_field = 0; + m_operator = OPERATOR_CONTAINS; +} + +bool CDatabaseQueryRule::Load(const TiXmlNode *node, const std::string &encoding /* = "UTF-8" */) +{ + if (node == NULL) + return false; + + const TiXmlElement *element = node->ToElement(); + if (element == NULL) + return false; + + // format is: + // <rule field="Genre" operator="contains">parameter</rule> + // where parameter can either be a string or a list of + // <value> tags containing a string + const char *field = element->Attribute("field"); + const char *oper = element->Attribute("operator"); + if (field == NULL || oper == NULL) + return false; + + m_field = TranslateField(field); + m_operator = TranslateOperator(oper); + + if (m_operator == OPERATOR_TRUE || m_operator == OPERATOR_FALSE) + return true; + + const TiXmlNode *parameter = element->FirstChild(); + if (parameter == NULL) + return false; + + if (parameter->Type() == TiXmlNode::TINYXML_TEXT) + { + CStdString utf8Parameter; + if (encoding.empty()) // utf8 + utf8Parameter = parameter->ValueStr(); + else + g_charsetConverter.ToUtf8(encoding, parameter->ValueStr(), utf8Parameter); + + if (!utf8Parameter.empty()) + m_parameter.push_back(utf8Parameter); + } + else if (parameter->Type() == TiXmlNode::TINYXML_ELEMENT) + { + const TiXmlNode *valueNode = element->FirstChild("value"); + while (valueNode != NULL) + { + const TiXmlNode *value = valueNode->FirstChild(); + if (value != NULL && value->Type() == TiXmlNode::TINYXML_TEXT) + { + CStdString utf8Parameter; + if (encoding.empty()) // utf8 + utf8Parameter = value->ValueStr(); + else + g_charsetConverter.ToUtf8(encoding, value->ValueStr(), utf8Parameter); + + if (!utf8Parameter.empty()) + m_parameter.push_back(utf8Parameter); + } + + valueNode = valueNode->NextSibling("value"); + } + } + else + return false; + + return true; +} + +bool CDatabaseQueryRule::Load(const CVariant &obj) +{ + if (!obj.isObject() || + !obj.isMember("field") || !obj["field"].isString() || + !obj.isMember("operator") || !obj["operator"].isString()) + return false; + + m_field = TranslateField(obj["field"].asString().c_str()); + m_operator = TranslateOperator(obj["operator"].asString().c_str()); + + if (m_operator == OPERATOR_TRUE || m_operator == OPERATOR_FALSE) + return true; + + if (!obj.isMember("value") || (!obj["value"].isString() && !obj["value"].isArray())) + return false; + + const CVariant &value = obj["value"]; + if (value.isString() && !value.asString().empty()) + m_parameter.push_back(value.asString()); + else if (value.isArray()) + { + for (CVariant::const_iterator_array val = value.begin_array(); val != value.end_array(); val++) + { + if (val->isString() && !val->asString().empty()) + m_parameter.push_back(val->asString()); + } + } + else + return false; + + return true; +} + +bool CDatabaseQueryRule::Save(TiXmlNode *parent) const +{ + if (parent == NULL || (m_parameter.empty() && m_operator != OPERATOR_TRUE && m_operator != OPERATOR_FALSE)) + return false; + + TiXmlElement rule("rule"); + rule.SetAttribute("field", TranslateField(m_field).c_str()); + rule.SetAttribute("operator", TranslateOperator(m_operator).c_str()); + + for (vector<CStdString>::const_iterator it = m_parameter.begin(); it != m_parameter.end(); it++) + { + TiXmlElement value("value"); + TiXmlText text(it->c_str()); + value.InsertEndChild(text); + rule.InsertEndChild(value); + } + + parent->InsertEndChild(rule); + + return true; +} + +bool CDatabaseQueryRule::Save(CVariant &obj) const +{ + if (obj.isNull() || (m_parameter.empty() && m_operator != OPERATOR_TRUE && m_operator != OPERATOR_FALSE)) + return false; + + obj["field"] = TranslateField(m_field); + obj["operator"] = TranslateOperator(m_operator); + + obj["value"] = CVariant(CVariant::VariantTypeArray); + for (vector<CStdString>::const_iterator it = m_parameter.begin(); it != m_parameter.end(); it++) + obj["value"].push_back(*it); + + return true; +} + +CDatabaseQueryRule::SEARCH_OPERATOR CDatabaseQueryRule::TranslateOperator(const char *oper) +{ + for (unsigned int i = 0; i < NUM_OPERATORS; i++) + if (StringUtils::EqualsNoCase(oper, operators[i].string)) return operators[i].op; + return OPERATOR_CONTAINS; +} + +CStdString CDatabaseQueryRule::TranslateOperator(SEARCH_OPERATOR oper) +{ + for (unsigned int i = 0; i < NUM_OPERATORS; i++) + if (oper == operators[i].op) return operators[i].string; + return "contains"; +} + +CStdString CDatabaseQueryRule::GetLocalizedOperator(SEARCH_OPERATOR oper) +{ + for (unsigned int i = 0; i < NUM_OPERATORS; i++) + if (oper == operators[i].op) return g_localizeStrings.Get(operators[i].localizedString); + return g_localizeStrings.Get(16018); +} + +void CDatabaseQueryRule::GetAvailableOperators(std::vector<std::string> &operatorList) +{ + for (unsigned int index = 0; index < NUM_OPERATORS; index++) + operatorList.push_back(operators[index].string); +} + +CStdString CDatabaseQueryRule::GetParameter() const +{ + return StringUtils::JoinString(m_parameter, RULE_VALUE_SEPARATOR); +} + +void CDatabaseQueryRule::SetParameter(const CStdString &value) +{ + m_parameter.clear(); + StringUtils::SplitString(value, RULE_VALUE_SEPARATOR, m_parameter); +} + +void CDatabaseQueryRule::SetParameter(const std::vector<CStdString> &values) +{ + m_parameter.assign(values.begin(), values.end()); +} + +CStdString CDatabaseQueryRule::FormatParameter(const CStdString &operatorString, const CStdString ¶m, const CDatabase &db, const CStdString &strType) const +{ + CStdString parameter; + if (GetFieldType(m_field) == TEXTIN_FIELD) + { + CStdStringArray split; + StringUtils::SplitString(param, ",", split); + for (CStdStringArray::iterator itIn = split.begin(); itIn != split.end(); ++itIn) + { + if (!parameter.IsEmpty()) + parameter += ","; + parameter += db.PrepareSQL("'%s'", (*itIn).Trim().c_str()); + } + parameter = " IN (" + parameter + ")"; + } + else + parameter = db.PrepareSQL(operatorString.c_str(), param.c_str()); + + if (GetFieldType(m_field) == DATE_FIELD) + { + if (m_operator == OPERATOR_IN_THE_LAST || m_operator == OPERATOR_NOT_IN_THE_LAST) + { // translate time period + CDateTime date=CDateTime::GetCurrentDateTime(); + CDateTimeSpan span; + span.SetFromPeriod(param); + date-=span; + parameter = db.PrepareSQL(operatorString.c_str(), date.GetAsDBDate().c_str()); + } + } + return parameter; +} + +CStdString CDatabaseQueryRule::GetOperatorString(SEARCH_OPERATOR op) const +{ + CStdString operatorString; + if (GetFieldType(m_field) != TEXTIN_FIELD) + { + // the comparison piece + switch (op) + { + case OPERATOR_CONTAINS: + operatorString = " LIKE '%%%s%%'"; break; + case OPERATOR_DOES_NOT_CONTAIN: + operatorString = " LIKE '%%%s%%'"; break; + case OPERATOR_EQUALS: + if (GetFieldType(m_field) == NUMERIC_FIELD || GetFieldType(m_field) == SECONDS_FIELD) + operatorString = " = %s"; + else + operatorString = " LIKE '%s'"; + break; + case OPERATOR_DOES_NOT_EQUAL: + if (GetFieldType(m_field) == NUMERIC_FIELD || GetFieldType(m_field) == SECONDS_FIELD) + operatorString = " != %s"; + else + operatorString = " LIKE '%s'"; + break; + case OPERATOR_STARTS_WITH: + operatorString = " LIKE '%s%%'"; break; + case OPERATOR_ENDS_WITH: + operatorString = " LIKE '%%%s'"; break; + case OPERATOR_AFTER: + case OPERATOR_GREATER_THAN: + case OPERATOR_IN_THE_LAST: + operatorString = " > "; + if (GetFieldType(m_field) == NUMERIC_FIELD || GetFieldType(m_field) == SECONDS_FIELD) + operatorString += "%s"; + else + operatorString += "'%s'"; + break; + case OPERATOR_BEFORE: + case OPERATOR_LESS_THAN: + case OPERATOR_NOT_IN_THE_LAST: + operatorString = " < "; + if (GetFieldType(m_field) == NUMERIC_FIELD || GetFieldType(m_field) == SECONDS_FIELD) + operatorString += "%s"; + else + operatorString += "'%s'"; + break; + case OPERATOR_TRUE: + operatorString = " = 1"; break; + case OPERATOR_FALSE: + operatorString = " = 0"; break; + default: + break; + } + } + return operatorString; +} + +CStdString CDatabaseQueryRule::GetWhereClause(const CDatabase &db, const CStdString& strType) const +{ + SEARCH_OPERATOR op = GetOperator(strType); + + CStdString operatorString = GetOperatorString(op); + CStdString negate; + if (op == OPERATOR_DOES_NOT_CONTAIN || op == OPERATOR_FALSE || + (op == OPERATOR_DOES_NOT_EQUAL && GetFieldType(m_field) != NUMERIC_FIELD && GetFieldType(m_field) != SECONDS_FIELD)) + negate = " NOT"; + + // boolean operators don't have any values in m_parameter, they work on the operator + if (m_operator == OPERATOR_FALSE || m_operator == OPERATOR_TRUE) + return GetBooleanQuery(negate, strType); + + // The BETWEEN operator is handled special + if (op == OPERATOR_BETWEEN) + { + if (m_parameter.size() != 2) + return ""; + + FIELD_TYPE fieldType = GetFieldType(m_field); + if (fieldType == NUMERIC_FIELD) + return db.PrepareSQL("CAST(%s as DECIMAL(5,1)) BETWEEN %s AND %s", GetField(m_field, strType).c_str(), m_parameter[0].c_str(), m_parameter[1].c_str()); + else if (fieldType == SECONDS_FIELD) + return db.PrepareSQL("CAST(%s as INTEGER) BETWEEN %s AND %s", GetField(m_field, strType).c_str(), m_parameter[0].c_str(), m_parameter[1].c_str()); + else + return db.PrepareSQL("%s BETWEEN '%s' AND '%s'", GetField(m_field, strType).c_str(), m_parameter[0].c_str(), m_parameter[1].c_str()); + } + + // now the query parameter + CStdString wholeQuery; + for (vector<CStdString>::const_iterator it = m_parameter.begin(); it != m_parameter.end(); ++it) + { + CStdString query = "(" + FormatWhereClause(negate, operatorString, *it, db, strType) + ")"; + + if (it+1 != m_parameter.end()) + query += " OR "; + + wholeQuery += query; + } + + return wholeQuery; +} + +CStdString CDatabaseQueryRule::FormatWhereClause(const CStdString &negate, const CStdString &oper, const CStdString ¶m, + const CDatabase &db, const CStdString &strType) const +{ + CStdString parameter = FormatParameter(oper, param, db, strType); + + CStdString query; + if (m_field != 0) + { + string fmt = "%s"; + if (GetFieldType(m_field) == NUMERIC_FIELD) + fmt = "CAST(%s as DECIMAL(5,1))"; + else if (GetFieldType(m_field) == SECONDS_FIELD) + fmt = "CAST(%s as INTEGER)"; + + query.Format(fmt.c_str(), GetField(m_field,strType).c_str()); + query += negate + parameter; + } + + if (query.Equals(negate + parameter)) + query = "1"; + return query; +} + +CDatabaseQueryRuleCombination::CDatabaseQueryRuleCombination() + : m_type(CombinationAnd) +{ } + +void CDatabaseQueryRuleCombination::clear() +{ + m_combinations.clear(); + m_rules.clear(); + m_type = CombinationAnd; +} + +CStdString CDatabaseQueryRuleCombination::GetWhereClause(const CDatabase &db, const CStdString& strType) const +{ + CStdString rule, currentRule; + + // translate the combinations into SQL + for (CDatabaseQueryRuleCombinations::const_iterator it = m_combinations.begin(); it != m_combinations.end(); ++it) + { + if (it != m_combinations.begin()) + rule += m_type == CombinationAnd ? " AND " : " OR "; + rule += "(" + (*it)->GetWhereClause(db, strType) + ")"; + } + + // translate the rules into SQL + for (CDatabaseQueryRules::const_iterator it = m_rules.begin(); it != m_rules.end(); ++it) + { + if (!rule.empty()) + rule += m_type == CombinationAnd ? " AND " : " OR "; + rule += "("; + CStdString currentRule = (*it)->GetWhereClause(db, strType); + // if we don't get a rule, we add '1' or '0' so the query is still valid and doesn't fail + if (currentRule.IsEmpty()) + currentRule = m_type == CombinationAnd ? "'1'" : "'0'"; + rule += currentRule; + rule += ")"; + } + + return rule; +} + +bool CDatabaseQueryRuleCombination::Load(const CVariant &obj, const IDatabaseQueryRuleFactory *factory) +{ + if (!obj.isObject() && !obj.isArray()) + return false; + + CVariant child; + if (obj.isObject()) + { + if (obj.isMember("and") && obj["and"].isArray()) + { + m_type = CombinationAnd; + child = obj["and"]; + } + else if (obj.isMember("or") && obj["or"].isArray()) + { + m_type = CombinationOr; + child = obj["or"]; + } + else + return false; + } + else + child = obj; + + for (CVariant::const_iterator_array it = child.begin_array(); it != child.end_array(); it++) + { + if (!it->isObject()) + continue; + + if (it->isMember("and") || it->isMember("or")) + { + boost::shared_ptr<CDatabaseQueryRuleCombination> combo(factory->CreateCombination()); + if (combo && combo->Load(*it, factory)) + m_combinations.push_back(combo); + } + else + { + boost::shared_ptr<CDatabaseQueryRule> rule(factory->CreateRule()); + if (rule && rule->Load(*it)) + m_rules.push_back(rule); + } + } + + return true; +} + +bool CDatabaseQueryRuleCombination::Save(TiXmlNode *parent) const +{ + for (CDatabaseQueryRules::const_iterator it = m_rules.begin(); it != m_rules.end(); ++it) + (*it)->Save(parent); + return true; +} + +bool CDatabaseQueryRuleCombination::Save(CVariant &obj) const +{ + if (!obj.isObject() || (m_combinations.empty() && m_rules.empty())) + return false; + + CVariant comboArray(CVariant::VariantTypeArray); + if (!m_combinations.empty()) + { + for (CDatabaseQueryRuleCombinations::const_iterator combo = m_combinations.begin(); combo != m_combinations.end(); combo++) + { + CVariant comboObj(CVariant::VariantTypeObject); + if ((*combo)->Save(comboObj)) + comboArray.push_back(comboObj); + } + + } + if (!m_rules.empty()) + { + for (CDatabaseQueryRules::const_iterator rule = m_rules.begin(); rule != m_rules.end(); rule++) + { + CVariant ruleObj(CVariant::VariantTypeObject); + if ((*rule)->Save(ruleObj)) + comboArray.push_back(ruleObj); + } + } + + obj[TranslateCombinationType()] = comboArray; + + return true; +} + +std::string CDatabaseQueryRuleCombination::TranslateCombinationType() const +{ + return m_type == CombinationAnd ? "and" : "or"; +} diff --git a/xbmc/dbwrappers/DatabaseQuery.h b/xbmc/dbwrappers/DatabaseQuery.h new file mode 100644 index 0000000000..7e684a563f --- /dev/null +++ b/xbmc/dbwrappers/DatabaseQuery.h @@ -0,0 +1,144 @@ +#pragma once +/* + * Copyright (C) 2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include <set> +#include <vector> +#include <boost/shared_ptr.hpp> + +#include "utils/StdString.h" + +class CDatabase; +class CVariant; +class TiXmlNode; + +class CDatabaseQueryRule +{ +public: + CDatabaseQueryRule(); + virtual ~CDatabaseQueryRule() { }; + + enum SEARCH_OPERATOR { OPERATOR_START = 0, + OPERATOR_CONTAINS, + OPERATOR_DOES_NOT_CONTAIN, + OPERATOR_EQUALS, + OPERATOR_DOES_NOT_EQUAL, + OPERATOR_STARTS_WITH, + OPERATOR_ENDS_WITH, + OPERATOR_GREATER_THAN, + OPERATOR_LESS_THAN, + OPERATOR_AFTER, + OPERATOR_BEFORE, + OPERATOR_IN_THE_LAST, + OPERATOR_NOT_IN_THE_LAST, + OPERATOR_TRUE, + OPERATOR_FALSE, + OPERATOR_BETWEEN, + OPERATOR_END + }; + + enum FIELD_TYPE { TEXT_FIELD = 0, + NUMERIC_FIELD, + DATE_FIELD, + PLAYLIST_FIELD, + SECONDS_FIELD, + BOOLEAN_FIELD, + TEXTIN_FIELD + }; + + virtual bool Load(const TiXmlNode *node, const std::string &encoding = "UTF-8"); + virtual bool Load(const CVariant &obj); + virtual bool Save(TiXmlNode *parent) const; + virtual bool Save(CVariant &obj) const; + + static CStdString GetLocalizedOperator(SEARCH_OPERATOR oper); + static void GetAvailableOperators(std::vector<std::string> &operatorList); + + CStdString GetParameter() const; + void SetParameter(const CStdString &value); + void SetParameter(const std::vector<CStdString> &values); + + virtual CStdString GetWhereClause(const CDatabase &db, const CStdString& strType) const; + + int m_field; + SEARCH_OPERATOR m_operator; + std::vector<CStdString> m_parameter; + +protected: + virtual CStdString GetField(int field, const CStdString& type) const=0; + virtual FIELD_TYPE GetFieldType(int field) const=0; + virtual int TranslateField(const char *field) const=0; + virtual CStdString TranslateField(int field) const=0; + virtual CStdString FormatParameter(const CStdString &negate, const CStdString &oper, const CDatabase &db, const CStdString &type) const; + virtual CStdString FormatWhereClause(const CStdString &negate, const CStdString &oper, const CStdString ¶m, + const CDatabase &db, const CStdString &type) const; + virtual SEARCH_OPERATOR GetOperator(const CStdString &type) const { return m_operator; }; + virtual CStdString GetOperatorString(SEARCH_OPERATOR op) const; + virtual CStdString GetBooleanQuery(const CStdString &negate, const CStdString &strType) const { return ""; } + + static SEARCH_OPERATOR TranslateOperator(const char *oper); + static CStdString TranslateOperator(SEARCH_OPERATOR oper); +}; + +class CDatabaseQueryRuleCombination; + +typedef std::vector< boost::shared_ptr<CDatabaseQueryRule> > CDatabaseQueryRules; +typedef std::vector< boost::shared_ptr<CDatabaseQueryRuleCombination> > CDatabaseQueryRuleCombinations; + +class IDatabaseQueryRuleFactory +{ +public: + virtual CDatabaseQueryRule *CreateRule() const=0; + virtual CDatabaseQueryRuleCombination *CreateCombination() const=0; +}; + +class CDatabaseQueryRuleCombination +{ +public: + CDatabaseQueryRuleCombination(); + virtual ~CDatabaseQueryRuleCombination() { }; + + typedef enum { + CombinationOr = 0, + CombinationAnd + } Combination; + + void clear(); + virtual bool Load(const TiXmlNode *node, const std::string &encoding = "UTF-8") { return false; } + virtual bool Load(const CVariant &obj, const IDatabaseQueryRuleFactory *factory); + virtual bool Save(TiXmlNode *parent) const; + virtual bool Save(CVariant &obj) const; + + CStdString GetWhereClause(const CDatabase &db, const CStdString& strType) const; + std::string TranslateCombinationType() const; + + Combination GetType() const { return m_type; } + void SetType(Combination combination) { m_type = combination; } + + bool empty() const { return m_combinations.empty() && m_rules.empty(); } + +protected: + friend class CGUIDialogSmartPlaylistEditor; + friend class CGUIDialogMediaFilter; + + Combination m_type; + CDatabaseQueryRuleCombinations m_combinations; + CDatabaseQueryRules m_rules; +}; diff --git a/xbmc/dbwrappers/Makefile b/xbmc/dbwrappers/Makefile index ce50c1c8c4..4558aecbf8 100644 --- a/xbmc/dbwrappers/Makefile +++ b/xbmc/dbwrappers/Makefile @@ -1,4 +1,5 @@ SRCS=Database.cpp \ + DatabaseQuery.cpp \ dataset.cpp \ mysqldataset.cpp \ qry_dat.cpp \ diff --git a/xbmc/playlists/SmartPlayList.cpp b/xbmc/playlists/SmartPlayList.cpp index 057cd1aed2..a6d44c72ff 100644 --- a/xbmc/playlists/SmartPlayList.cpp +++ b/xbmc/playlists/SmartPlayList.cpp @@ -118,33 +118,6 @@ static const size_t NUM_FIELDS = sizeof(fields) / sizeof(translateField); typedef struct { - char string[15]; - CDatabaseQueryRule::SEARCH_OPERATOR op; - int localizedString; -} operatorField; - -static const operatorField operators[] = { - { "contains", CDatabaseQueryRule::OPERATOR_CONTAINS, 21400 }, - { "doesnotcontain", CDatabaseQueryRule::OPERATOR_DOES_NOT_CONTAIN, 21401 }, - { "is", CDatabaseQueryRule::OPERATOR_EQUALS, 21402 }, - { "isnot", CDatabaseQueryRule::OPERATOR_DOES_NOT_EQUAL, 21403 }, - { "startswith", CDatabaseQueryRule::OPERATOR_STARTS_WITH, 21404 }, - { "endswith", CDatabaseQueryRule::OPERATOR_ENDS_WITH, 21405 }, - { "greaterthan", CDatabaseQueryRule::OPERATOR_GREATER_THAN, 21406 }, - { "lessthan", CDatabaseQueryRule::OPERATOR_LESS_THAN, 21407 }, - { "after", CDatabaseQueryRule::OPERATOR_AFTER, 21408 }, - { "before", CDatabaseQueryRule::OPERATOR_BEFORE, 21409 }, - { "inthelast", CDatabaseQueryRule::OPERATOR_IN_THE_LAST, 21410 }, - { "notinthelast", CDatabaseQueryRule::OPERATOR_NOT_IN_THE_LAST, 21411 }, - { "true", CDatabaseQueryRule::OPERATOR_TRUE, 20122 }, - { "false", CDatabaseQueryRule::OPERATOR_FALSE, 20424 }, - { "between", CDatabaseQueryRule::OPERATOR_BETWEEN, 21456 } -}; - -static const size_t NUM_OPERATORS = sizeof(operators) / sizeof(operatorField); - -typedef struct -{ std::string name; Field field; bool canMix; @@ -170,148 +143,6 @@ static const size_t NUM_GROUPS = sizeof(groups) / sizeof(group); #define RULE_VALUE_SEPARATOR " / " -CDatabaseQueryRule::CDatabaseQueryRule() -{ - m_field = FieldNone; - m_operator = OPERATOR_CONTAINS; -} - -bool CDatabaseQueryRule::Load(const TiXmlNode *node, const std::string &encoding /* = "UTF-8" */) -{ - if (node == NULL) - return false; - - const TiXmlElement *element = node->ToElement(); - if (element == NULL) - return false; - - // format is: - // <rule field="Genre" operator="contains">parameter</rule> - // where parameter can either be a string or a list of - // <value> tags containing a string - const char *field = element->Attribute("field"); - const char *oper = element->Attribute("operator"); - if (field == NULL || oper == NULL) - return false; - - m_field = TranslateField(field); - m_operator = TranslateOperator(oper); - - if (m_operator == OPERATOR_TRUE || m_operator == OPERATOR_FALSE) - return true; - - const TiXmlNode *parameter = element->FirstChild(); - if (parameter == NULL) - return false; - - if (parameter->Type() == TiXmlNode::TINYXML_TEXT) - { - CStdString utf8Parameter; - if (encoding.empty()) // utf8 - utf8Parameter = parameter->ValueStr(); - else - g_charsetConverter.ToUtf8(encoding, parameter->ValueStr(), utf8Parameter); - - if (!utf8Parameter.empty()) - m_parameter.push_back(utf8Parameter); - } - else if (parameter->Type() == TiXmlNode::TINYXML_ELEMENT) - { - const TiXmlNode *valueNode = element->FirstChild("value"); - while (valueNode != NULL) - { - const TiXmlNode *value = valueNode->FirstChild(); - if (value != NULL && value->Type() == TiXmlNode::TINYXML_TEXT) - { - CStdString utf8Parameter; - if (encoding.empty()) // utf8 - utf8Parameter = value->ValueStr(); - else - g_charsetConverter.ToUtf8(encoding, value->ValueStr(), utf8Parameter); - - if (!utf8Parameter.empty()) - m_parameter.push_back(utf8Parameter); - } - - valueNode = valueNode->NextSibling("value"); - } - } - else - return false; - - return true; -} - -bool CDatabaseQueryRule::Load(const CVariant &obj) -{ - if (!obj.isObject() || - !obj.isMember("field") || !obj["field"].isString() || - !obj.isMember("operator") || !obj["operator"].isString()) - return false; - - m_field = TranslateField(obj["field"].asString().c_str()); - m_operator = TranslateOperator(obj["operator"].asString().c_str()); - - if (m_operator == OPERATOR_TRUE || m_operator == OPERATOR_FALSE) - return true; - - if (!obj.isMember("value") || (!obj["value"].isString() && !obj["value"].isArray())) - return false; - - const CVariant &value = obj["value"]; - if (value.isString() && !value.asString().empty()) - m_parameter.push_back(value.asString()); - else if (value.isArray()) - { - for (CVariant::const_iterator_array val = value.begin_array(); val != value.end_array(); val++) - { - if (val->isString() && !val->asString().empty()) - m_parameter.push_back(val->asString()); - } - } - else - return false; - - return true; -} - -bool CDatabaseQueryRule::Save(TiXmlNode *parent) const -{ - if (parent == NULL || (m_parameter.empty() && m_operator != OPERATOR_TRUE && m_operator != OPERATOR_FALSE)) - return false; - - TiXmlElement rule("rule"); - rule.SetAttribute("field", TranslateField(m_field).c_str()); - rule.SetAttribute("operator", TranslateOperator(m_operator).c_str()); - - for (vector<CStdString>::const_iterator it = m_parameter.begin(); it != m_parameter.end(); it++) - { - TiXmlElement value("value"); - TiXmlText text(it->c_str()); - value.InsertEndChild(text); - rule.InsertEndChild(value); - } - - parent->InsertEndChild(rule); - - return true; -} - -bool CDatabaseQueryRule::Save(CVariant &obj) const -{ - if (obj.isNull() || (m_parameter.empty() && m_operator != OPERATOR_TRUE && m_operator != OPERATOR_FALSE)) - return false; - - obj["field"] = TranslateField(m_field); - obj["operator"] = TranslateOperator(m_operator); - - obj["value"] = CVariant(CVariant::VariantTypeArray); - for (vector<CStdString>::const_iterator it = m_parameter.begin(); it != m_parameter.end(); it++) - obj["value"].push_back(*it); - - return true; -} - CSmartPlaylistRule::CSmartPlaylistRule() { } @@ -344,20 +175,6 @@ CStdString CSmartPlaylistRule::TranslateOrder(SortBy order) return "none"; } -CDatabaseQueryRule::SEARCH_OPERATOR CDatabaseQueryRule::TranslateOperator(const char *oper) -{ - for (unsigned int i = 0; i < NUM_OPERATORS; i++) - if (StringUtils::EqualsNoCase(oper, operators[i].string)) return operators[i].op; - return OPERATOR_CONTAINS; -} - -CStdString CDatabaseQueryRule::TranslateOperator(SEARCH_OPERATOR oper) -{ - for (unsigned int i = 0; i < NUM_OPERATORS; i++) - if (oper == operators[i].op) return operators[i].string; - return "contains"; -} - Field CSmartPlaylistRule::TranslateGroup(const char *group) { for (unsigned int i = 0; i < NUM_GROUPS; i++) @@ -763,9 +580,7 @@ std::vector<Field> CSmartPlaylistRule::GetGroups(const CStdString &type) CStdString CSmartPlaylistRule::GetLocalizedOperator(SEARCH_OPERATOR oper) { - for (unsigned int i = 0; i < NUM_OPERATORS; i++) - if (oper == operators[i].op) return g_localizeStrings.Get(operators[i].localizedString); - return g_localizeStrings.Get(16018); + return CDatabaseQueryRule::GetLocalizedOperator(oper); } CStdString CSmartPlaylistRule::GetLocalizedGroup(Field group) @@ -797,22 +612,6 @@ CStdString CSmartPlaylistRule::GetLocalizedRule() const return rule; } -CStdString CDatabaseQueryRule::GetParameter() const -{ - return StringUtils::JoinString(m_parameter, RULE_VALUE_SEPARATOR); -} - -void CDatabaseQueryRule::SetParameter(const CStdString &value) -{ - m_parameter.clear(); - StringUtils::SplitString(value, RULE_VALUE_SEPARATOR, m_parameter); -} - -void CDatabaseQueryRule::SetParameter(const std::vector<CStdString> &values) -{ - m_parameter.assign(values.begin(), values.end()); -} - CStdString CSmartPlaylistRule::GetVideoResolutionQuery(const CStdString ¶meter) const { CStdString retVal(" IN (SELECT DISTINCT idFile FROM streamdetails WHERE iVideoWidth "); @@ -887,38 +686,6 @@ CDatabaseQueryRule::SEARCH_OPERATOR CSmartPlaylistRule::GetOperator(const CStdSt return op; } -CStdString CDatabaseQueryRule::FormatParameter(const CStdString &operatorString, const CStdString ¶m, const CDatabase &db, const CStdString &strType) const -{ - CStdString parameter; - if (GetFieldType(m_field) == TEXTIN_FIELD) - { - CStdStringArray split; - StringUtils::SplitString(param, ",", split); - for (CStdStringArray::iterator itIn = split.begin(); itIn != split.end(); ++itIn) - { - if (!parameter.IsEmpty()) - parameter += ","; - parameter += db.PrepareSQL("'%s'", (*itIn).Trim().c_str()); - } - parameter = " IN (" + parameter + ")"; - } - else - parameter = db.PrepareSQL(operatorString.c_str(), param.c_str()); - - if (GetFieldType(m_field) == DATE_FIELD) - { - if (m_operator == OPERATOR_IN_THE_LAST || m_operator == OPERATOR_NOT_IN_THE_LAST) - { // translate time period - CDateTime date=CDateTime::GetCurrentDateTime(); - CDateTimeSpan span; - span.SetFromPeriod(param); - date-=span; - parameter = db.PrepareSQL(operatorString.c_str(), date.GetAsDBDate().c_str()); - } - } - return parameter; -} - CStdString CSmartPlaylistRule::FormatParameter(const CStdString &operatorString, const CStdString ¶m, const CDatabase &db, const CStdString &strType) const { // special-casing @@ -930,107 +697,6 @@ CStdString CSmartPlaylistRule::FormatParameter(const CStdString &operatorString, return CDatabaseQueryRule::FormatParameter(operatorString, param, db, strType); } -CStdString CDatabaseQueryRule::GetOperatorString(SEARCH_OPERATOR op) const -{ - CStdString operatorString; - if (GetFieldType(m_field) != TEXTIN_FIELD) - { - // the comparison piece - switch (op) - { - case OPERATOR_CONTAINS: - operatorString = " LIKE '%%%s%%'"; break; - case OPERATOR_DOES_NOT_CONTAIN: - operatorString = " LIKE '%%%s%%'"; break; - case OPERATOR_EQUALS: - if (GetFieldType(m_field) == NUMERIC_FIELD || GetFieldType(m_field) == SECONDS_FIELD) - operatorString = " = %s"; - else - operatorString = " LIKE '%s'"; - break; - case OPERATOR_DOES_NOT_EQUAL: - if (GetFieldType(m_field) == NUMERIC_FIELD || GetFieldType(m_field) == SECONDS_FIELD) - operatorString = " != %s"; - else - operatorString = " LIKE '%s'"; - break; - case OPERATOR_STARTS_WITH: - operatorString = " LIKE '%s%%'"; break; - case OPERATOR_ENDS_WITH: - operatorString = " LIKE '%%%s'"; break; - case OPERATOR_AFTER: - case OPERATOR_GREATER_THAN: - case OPERATOR_IN_THE_LAST: - operatorString = " > "; - if (GetFieldType(m_field) == NUMERIC_FIELD || GetFieldType(m_field) == SECONDS_FIELD) - operatorString += "%s"; - else - operatorString += "'%s'"; - break; - case OPERATOR_BEFORE: - case OPERATOR_LESS_THAN: - case OPERATOR_NOT_IN_THE_LAST: - operatorString = " < "; - if (GetFieldType(m_field) == NUMERIC_FIELD || GetFieldType(m_field) == SECONDS_FIELD) - operatorString += "%s"; - else - operatorString += "'%s'"; - break; - case OPERATOR_TRUE: - operatorString = " = 1"; break; - case OPERATOR_FALSE: - operatorString = " = 0"; break; - default: - break; - } - } - return operatorString; -} - -CStdString CDatabaseQueryRule::GetWhereClause(const CDatabase &db, const CStdString& strType) const -{ - SEARCH_OPERATOR op = GetOperator(strType); - - CStdString operatorString = GetOperatorString(op); - CStdString negate; - if (op == OPERATOR_DOES_NOT_CONTAIN || op == OPERATOR_FALSE || - (op == OPERATOR_DOES_NOT_EQUAL && GetFieldType(m_field) != NUMERIC_FIELD && GetFieldType(m_field) != SECONDS_FIELD)) - negate = " NOT"; - - // boolean operators don't have any values in m_parameter, they work on the operator - if (m_operator == OPERATOR_FALSE || m_operator == OPERATOR_TRUE) - return GetBooleanQuery(negate, strType); - - // The BETWEEN operator is handled special - if (op == OPERATOR_BETWEEN) - { - if (m_parameter.size() != 2) - return ""; - - FIELD_TYPE fieldType = GetFieldType(m_field); - if (fieldType == NUMERIC_FIELD) - return db.PrepareSQL("CAST(%s as DECIMAL(5,1)) BETWEEN %s AND %s", GetField(m_field, strType).c_str(), m_parameter[0].c_str(), m_parameter[1].c_str()); - else if (fieldType == SECONDS_FIELD) - return db.PrepareSQL("CAST(%s as INTEGER) BETWEEN %s AND %s", GetField(m_field, strType).c_str(), m_parameter[0].c_str(), m_parameter[1].c_str()); - else - return db.PrepareSQL("%s BETWEEN '%s' AND '%s'", GetField(m_field, strType).c_str(), m_parameter[0].c_str(), m_parameter[1].c_str()); - } - - // now the query parameter - CStdString wholeQuery; - for (vector<CStdString>::const_iterator it = m_parameter.begin(); it != m_parameter.end(); ++it) - { - CStdString query = "(" + FormatWhereClause(negate, operatorString, *it, db, strType) + ")"; - - if (it+1 != m_parameter.end()) - query += " OR "; - - wholeQuery += query; - } - - return wholeQuery; -} - CStdString CSmartPlaylistRule::FormatWhereClause(const CStdString &negate, const CStdString &oper, const CStdString ¶m, const CDatabase &db, const CStdString &strType) const { @@ -1176,29 +842,6 @@ CStdString CSmartPlaylistRule::FormatWhereClause(const CStdString &negate, const return query; } -CStdString CDatabaseQueryRule::FormatWhereClause(const CStdString &negate, const CStdString &oper, const CStdString ¶m, - const CDatabase &db, const CStdString &strType) const -{ - CStdString parameter = FormatParameter(oper, param, db, strType); - - CStdString query; - if (m_field != FieldNone) - { - string fmt = "%s"; - if (GetFieldType(m_field) == NUMERIC_FIELD) - fmt = "CAST(%s as DECIMAL(5,1))"; - else if (GetFieldType(m_field) == SECONDS_FIELD) - fmt = "CAST(%s as INTEGER)"; - - query.Format(fmt.c_str(), GetField(m_field,strType).c_str()); - query += negate + parameter; - } - - if (query.Equals(negate + parameter)) - query = "1"; - return query; -} - CStdString CSmartPlaylistRule::GetField(int field, const CStdString &type) const { if (field >= FieldUnknown && field < FieldMax) @@ -1206,46 +849,6 @@ CStdString CSmartPlaylistRule::GetField(int field, const CStdString &type) const return ""; } -CDatabaseQueryRuleCombination::CDatabaseQueryRuleCombination() - : m_type(CombinationAnd) -{ } - -void CDatabaseQueryRuleCombination::clear() -{ - m_combinations.clear(); - m_rules.clear(); - m_type = CombinationAnd; -} - -CStdString CDatabaseQueryRuleCombination::GetWhereClause(const CDatabase &db, const CStdString& strType) const -{ - CStdString rule, currentRule; - - // translate the combinations into SQL - for (CDatabaseQueryRuleCombinations::const_iterator it = m_combinations.begin(); it != m_combinations.end(); ++it) - { - if (it != m_combinations.begin()) - rule += m_type == CombinationAnd ? " AND " : " OR "; - rule += "(" + (*it)->GetWhereClause(db, strType) + ")"; - } - - // translate the rules into SQL - for (CDatabaseQueryRules::const_iterator it = m_rules.begin(); it != m_rules.end(); ++it) - { - if (!rule.empty()) - rule += m_type == CombinationAnd ? " AND " : " OR "; - rule += "("; - CStdString currentRule = (*it)->GetWhereClause(db, strType); - // if we don't get a rule, we add '1' or '0' so the query is still valid and doesn't fail - if (currentRule.IsEmpty()) - currentRule = m_type == CombinationAnd ? "'1'" : "'0'"; - rule += currentRule; - rule += ")"; - } - - return rule; -} - CStdString CSmartPlaylistRuleCombination::GetWhereClause(const CDatabase &db, const CStdString& strType, std::set<CStdString> &referencedPlaylists) const { CStdString rule, currentRule; @@ -1343,95 +946,6 @@ void CSmartPlaylistRuleCombination::GetVirtualFolders(const CStdString& strType, } } -bool CDatabaseQueryRuleCombination::Load(const CVariant &obj, const IDatabaseQueryRuleFactory *factory) -{ - if (!obj.isObject() && !obj.isArray()) - return false; - - CVariant child; - if (obj.isObject()) - { - if (obj.isMember("and") && obj["and"].isArray()) - { - m_type = CombinationAnd; - child = obj["and"]; - } - else if (obj.isMember("or") && obj["or"].isArray()) - { - m_type = CombinationOr; - child = obj["or"]; - } - else - return false; - } - else - child = obj; - - for (CVariant::const_iterator_array it = child.begin_array(); it != child.end_array(); it++) - { - if (!it->isObject()) - continue; - - if (it->isMember("and") || it->isMember("or")) - { - boost::shared_ptr<CDatabaseQueryRuleCombination> combo(factory->CreateCombination()); - if (combo && combo->Load(*it, factory)) - m_combinations.push_back(combo); - } - else - { - boost::shared_ptr<CDatabaseQueryRule> rule(factory->CreateRule()); - if (rule && rule->Load(*it)) - m_rules.push_back(rule); - } - } - - return true; -} - -bool CDatabaseQueryRuleCombination::Save(TiXmlNode *parent) const -{ - for (CDatabaseQueryRules::const_iterator it = m_rules.begin(); it != m_rules.end(); ++it) - (*it)->Save(parent); - return true; -} - -bool CDatabaseQueryRuleCombination::Save(CVariant &obj) const -{ - if (!obj.isObject() || (m_combinations.empty() && m_rules.empty())) - return false; - - CVariant comboArray(CVariant::VariantTypeArray); - if (!m_combinations.empty()) - { - for (CDatabaseQueryRuleCombinations::const_iterator combo = m_combinations.begin(); combo != m_combinations.end(); combo++) - { - CVariant comboObj(CVariant::VariantTypeObject); - if ((*combo)->Save(comboObj)) - comboArray.push_back(comboObj); - } - - } - if (!m_rules.empty()) - { - for (CDatabaseQueryRules::const_iterator rule = m_rules.begin(); rule != m_rules.end(); rule++) - { - CVariant ruleObj(CVariant::VariantTypeObject); - if ((*rule)->Save(ruleObj)) - comboArray.push_back(ruleObj); - } - } - - obj[TranslateCombinationType()] = comboArray; - - return true; -} - -std::string CDatabaseQueryRuleCombination::TranslateCombinationType() const -{ - return m_type == CombinationAnd ? "and" : "or"; -} - void CSmartPlaylistRuleCombination::AddRule(const CSmartPlaylistRule &rule) { boost::shared_ptr<CSmartPlaylistRule> ptr(new CSmartPlaylistRule(rule)); @@ -1830,8 +1344,7 @@ void CSmartPlaylist::GetAvailableFields(const std::string &type, std::vector<std void CSmartPlaylist::GetAvailableOperators(std::vector<std::string> &operatorList) { - for (unsigned int index = 0; index < NUM_OPERATORS; index++) - operatorList.push_back(operators[index].string); + CDatabaseQueryRule::GetAvailableOperators(operatorList); } bool CSmartPlaylist::IsEmpty(bool ignoreSortAndLimit /* = true */) const diff --git a/xbmc/playlists/SmartPlayList.h b/xbmc/playlists/SmartPlayList.h index 15c9c9d183..468977a34c 100644 --- a/xbmc/playlists/SmartPlayList.h +++ b/xbmc/playlists/SmartPlayList.h @@ -23,78 +23,13 @@ #include <vector> #include <boost/shared_ptr.hpp> +#include "dbwrappers/DatabaseQuery.h" #include "utils/SortUtils.h" #include "utils/StdString.h" #include "utils/XBMCTinyXML.h" -class CDatabase; class CVariant; -class CDatabaseQueryRule -{ -public: - CDatabaseQueryRule(); - virtual ~CDatabaseQueryRule() { }; - - enum SEARCH_OPERATOR { OPERATOR_START = 0, - OPERATOR_CONTAINS, - OPERATOR_DOES_NOT_CONTAIN, - OPERATOR_EQUALS, - OPERATOR_DOES_NOT_EQUAL, - OPERATOR_STARTS_WITH, - OPERATOR_ENDS_WITH, - OPERATOR_GREATER_THAN, - OPERATOR_LESS_THAN, - OPERATOR_AFTER, - OPERATOR_BEFORE, - OPERATOR_IN_THE_LAST, - OPERATOR_NOT_IN_THE_LAST, - OPERATOR_TRUE, - OPERATOR_FALSE, - OPERATOR_BETWEEN, - OPERATOR_END - }; - - enum FIELD_TYPE { TEXT_FIELD = 0, - NUMERIC_FIELD, - DATE_FIELD, - PLAYLIST_FIELD, - SECONDS_FIELD, - BOOLEAN_FIELD, - TEXTIN_FIELD - }; - - virtual bool Load(const TiXmlNode *node, const std::string &encoding = "UTF-8"); - virtual bool Load(const CVariant &obj); - virtual bool Save(TiXmlNode *parent) const; - virtual bool Save(CVariant &obj) const; - - CStdString GetParameter() const; - void SetParameter(const CStdString &value); - void SetParameter(const std::vector<CStdString> &values); - - virtual CStdString GetWhereClause(const CDatabase &db, const CStdString& strType) const; - - int m_field; - SEARCH_OPERATOR m_operator; - std::vector<CStdString> m_parameter; - -protected: - virtual CStdString GetField(int field, const CStdString& type) const=0; - virtual FIELD_TYPE GetFieldType(int field) const=0; - virtual int TranslateField(const char *field) const=0; - virtual CStdString TranslateField(int field) const=0; - virtual CStdString FormatParameter(const CStdString &negate, const CStdString &oper, const CDatabase &db, const CStdString &type) const; - virtual CStdString FormatWhereClause(const CStdString &negate, const CStdString &oper, const CStdString ¶m, - const CDatabase &db, const CStdString &type) const; - virtual SEARCH_OPERATOR GetOperator(const CStdString &type) const { return m_operator; }; - virtual CStdString GetOperatorString(SEARCH_OPERATOR op) const; - virtual CStdString GetBooleanQuery(const CStdString &negate, const CStdString &strType) const { return ""; } - - static SEARCH_OPERATOR TranslateOperator(const char *oper); - static CStdString TranslateOperator(SEARCH_OPERATOR oper); -}; - class CSmartPlaylistRule : public CDatabaseQueryRule { public: @@ -136,52 +71,6 @@ private: CStdString GetVideoResolutionQuery(const CStdString ¶meter) const; }; -class CDatabaseQueryRuleCombination; - -typedef std::vector< boost::shared_ptr<CDatabaseQueryRule> > CDatabaseQueryRules; -typedef std::vector< boost::shared_ptr<CDatabaseQueryRuleCombination> > CDatabaseQueryRuleCombinations; - -class IDatabaseQueryRuleFactory -{ -public: - virtual CDatabaseQueryRule *CreateRule() const=0; - virtual CDatabaseQueryRuleCombination *CreateCombination() const=0; -}; - -class CDatabaseQueryRuleCombination -{ -public: - CDatabaseQueryRuleCombination(); - virtual ~CDatabaseQueryRuleCombination() { }; - - typedef enum { - CombinationOr = 0, - CombinationAnd - } Combination; - - void clear(); - virtual bool Load(const TiXmlNode *node, const std::string &encoding = "UTF-8") { return false; } - virtual bool Load(const CVariant &obj, const IDatabaseQueryRuleFactory *factory); - virtual bool Save(TiXmlNode *parent) const; - virtual bool Save(CVariant &obj) const; - - CStdString GetWhereClause(const CDatabase &db, const CStdString& strType) const; - std::string TranslateCombinationType() const; - - Combination GetType() const { return m_type; } - void SetType(Combination combination) { m_type = combination; } - - bool empty() const { return m_combinations.empty() && m_rules.empty(); } - -protected: - friend class CGUIDialogSmartPlaylistEditor; - friend class CGUIDialogMediaFilter; - - Combination m_type; - CDatabaseQueryRuleCombinations m_combinations; - CDatabaseQueryRules m_rules; -}; - class CSmartPlaylistRuleCombination : public CDatabaseQueryRuleCombination { public: |